refactor(frontend): queries > hooks, consolidate useFileMutations (#24)
* refactor(frontend): queries > hooks, consolidate useFileMutations * refactor(frontend): extract hook types * refactor(frontend): extract axios, fix type imports
This commit is contained in:
parent
35b582394c
commit
91e6004956
10 changed files with 48 additions and 62 deletions
|
@ -10,7 +10,7 @@ import FileList from "./components/FileList"
|
|||
import FileDetails from "./components/FileDetails"
|
||||
import AsyncTaskContext from "./contexts/AsyncTaskContext"
|
||||
import LocationContext, { useLocationContext } from "./contexts/LocationContext"
|
||||
import { useOwnFileList } from "./queries/files"
|
||||
import { useOwnFileList } from "./hooks/files"
|
||||
|
||||
const routeLabels = {
|
||||
ITEM_DETAILS: "item-details",
|
||||
|
|
7
frontend/src/axios.ts
Normal file
7
frontend/src/axios.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import axios from "axios"
|
||||
|
||||
const axiosWithDefaults = axios.create({
|
||||
baseURL: "http://localhost:8000",
|
||||
})
|
||||
|
||||
export default axiosWithDefaults
|
|
@ -12,7 +12,7 @@ import {
|
|||
useFileDetails,
|
||||
useFileMutations,
|
||||
useFileFetches,
|
||||
} from "../queries/files"
|
||||
} from "../hooks/files"
|
||||
|
||||
interface FileDetailsProps {
|
||||
itemId: string
|
||||
|
|
|
@ -14,12 +14,8 @@ import MuiTypography from "@mui/material/Typography"
|
|||
import { byteSizeToUnits } from "../utils"
|
||||
import { useLocationContext } from "../contexts/LocationContext"
|
||||
import { useAsyncTaskContext } from "../contexts/AsyncTaskContext"
|
||||
import {
|
||||
type FileData,
|
||||
useFileMutations,
|
||||
useFileFetches,
|
||||
} from "../queries/files"
|
||||
|
||||
import { useFileMutations, useFileFetches } from "../hooks/files"
|
||||
import { type FileData } from "../types/files"
|
||||
interface FileListProps {
|
||||
data: Array<FileData>
|
||||
}
|
||||
|
|
|
@ -7,11 +7,12 @@ import Button from "@mui/material/Button"
|
|||
import Typography from "@mui/material/Typography"
|
||||
import UploadIcon from "@mui/icons-material/Upload"
|
||||
|
||||
import { uploadFile } from "../queries/files"
|
||||
import { useFileMutations } from "../hooks/files"
|
||||
|
||||
function UploadFileButton() {
|
||||
const fileRef = useRef(null)
|
||||
const queryClient = useQueryClient()
|
||||
const { uploadFile } = useFileMutations()
|
||||
|
||||
const onClick = () => {
|
||||
if (fileRef.current) (fileRef.current as HTMLInputElement).click()
|
||||
|
|
|
@ -1,25 +1,11 @@
|
|||
import { useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
|
||||
import axios from "axios"
|
||||
|
||||
export const axiosWithDefaults = axios.create({
|
||||
baseURL: "http://localhost:8000",
|
||||
})
|
||||
|
||||
interface FileData {
|
||||
/* Displayed title of the item. */
|
||||
title: string
|
||||
/* Filename of the item as it appears on disk. */
|
||||
filename: string
|
||||
/* Size of the file in bytes. */
|
||||
size: number
|
||||
/* Unique identifier */
|
||||
id: string
|
||||
}
|
||||
import axios from "../axios"
|
||||
import { type FileData } from "../types/files"
|
||||
|
||||
function useOwnFileList() {
|
||||
return useQuery(["file-list"], async () => {
|
||||
const response = await axiosWithDefaults.get<Array<FileData>>("/files/")
|
||||
const response = await axios.get<Array<FileData>>("/files/")
|
||||
|
||||
return response.data
|
||||
})
|
||||
|
@ -27,7 +13,7 @@ function useOwnFileList() {
|
|||
|
||||
function useFileDetails(fileId: string) {
|
||||
return useQuery(["file-details", fileId], async () => {
|
||||
const response = await axiosWithDefaults.get<FileData>(`/files/${fileId}/`)
|
||||
const response = await axios.get<FileData>(`/files/${fileId}/`)
|
||||
|
||||
return response.data
|
||||
})
|
||||
|
@ -39,19 +25,27 @@ function useFileDetails(fileId: string) {
|
|||
*/
|
||||
function useFileMutations(): {
|
||||
deleteFile: (fileId: string) => Promise<FileData>
|
||||
uploadFile: (file: File) => Promise<FileData>
|
||||
} {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const deleteFile = async (fileId: string): Promise<FileData> => {
|
||||
const response = await axiosWithDefaults.delete<FileData>(
|
||||
`/files/${fileId}/`,
|
||||
)
|
||||
const response = await axios.delete<FileData>(`/files/${fileId}/`)
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ["file-list"] })
|
||||
return response.data
|
||||
}
|
||||
|
||||
return { deleteFile }
|
||||
const uploadFile = async (file: File) => {
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
|
||||
const response = await axios.postForm<FileData>("/files/", formData)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
return { deleteFile, uploadFile }
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -71,7 +65,7 @@ function useFileFetches(): {
|
|||
* click. This is a hack to trigger a file download from a non-file URL.
|
||||
*/
|
||||
const downloadFile = async (fileId: string, fileName: string) => {
|
||||
const response = await axiosWithDefaults.get(`/files/${fileId}/content/`)
|
||||
const response = await axios.get(`/files/${fileId}/content/`)
|
||||
const virtualAnchor = document.createElement("a")
|
||||
virtualAnchor.href = URL.createObjectURL(
|
||||
new Blob([response.data], { type: "application/octet-stream" }),
|
||||
|
@ -83,28 +77,4 @@ function useFileFetches(): {
|
|||
return { downloadFile }
|
||||
}
|
||||
|
||||
/*
|
||||
* Uploads a file.
|
||||
*/
|
||||
async function uploadFile(file: File) {
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
|
||||
const response = await axiosWithDefaults.postForm<FileData>(
|
||||
"/files/",
|
||||
formData,
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
export {
|
||||
useOwnFileList,
|
||||
useFileDetails,
|
||||
useFileMutations,
|
||||
useFileFetches,
|
||||
uploadFile,
|
||||
}
|
||||
|
||||
// Types
|
||||
export { FileData }
|
||||
export { useOwnFileList, useFileDetails, useFileMutations, useFileFetches }
|
12
frontend/src/types/files.ts
Normal file
12
frontend/src/types/files.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
interface FileData {
|
||||
/* Displayed title of the item. */
|
||||
title: string
|
||||
/* Filename of the item as it appears on disk. */
|
||||
filename: string
|
||||
/* Size of the file in bytes. */
|
||||
size: number
|
||||
/* Unique identifier */
|
||||
id: string
|
||||
}
|
||||
|
||||
export { FileData }
|
|
@ -5,8 +5,8 @@ import { type UseQueryResult } from "@tanstack/react-query"
|
|||
|
||||
import { renderWithContexts as render, getAxiosMockAdapter } from "./helpers"
|
||||
import FileDetails from "../src/components/FileDetails"
|
||||
import { type FileData } from "../src/queries/files"
|
||||
import * as fileQueries from "../src/queries/files"
|
||||
import { type FileData } from "../src/types/files"
|
||||
import * as fileQueries from "../src/hooks/files"
|
||||
import * as locationContextUtils from "../src/contexts/LocationContext"
|
||||
|
||||
describe("FileDetails", () => {
|
||||
|
|
|
@ -4,7 +4,7 @@ import userEvent from "@testing-library/user-event"
|
|||
import { renderWithContexts as render, getAxiosMockAdapter } from "./helpers"
|
||||
import NavigationBar from "../src/components/NavigationBar"
|
||||
|
||||
import { type FileData } from "../src/queries/files"
|
||||
import { type FileData } from "../src/types/files"
|
||||
|
||||
describe("NavigationBar", () => {
|
||||
describe("Upload functionality", () => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { type ReactNode } from "react"
|
||||
import { render } from "@testing-library/react"
|
||||
import { QueryClientProvider, QueryClient } from "@tanstack/react-query"
|
||||
import { axiosWithDefaults } from "../src/queries/files"
|
||||
import axios from "../src/axios"
|
||||
import AxiosMockAdapter from "axios-mock-adapter"
|
||||
|
||||
import AsyncTaskContext, {
|
||||
|
@ -36,7 +36,7 @@ function renderWithContexts(
|
|||
}
|
||||
|
||||
function getAxiosMockAdapter() {
|
||||
return new AxiosMockAdapter(axiosWithDefaults)
|
||||
return new AxiosMockAdapter(axios)
|
||||
}
|
||||
|
||||
export { getAxiosMockAdapter, renderWithContexts }
|
||||
|
|
Reference in a new issue