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:
Marc 2023-08-18 21:07:00 -04:00 committed by GitHub
parent 35b582394c
commit 91e6004956
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 48 additions and 62 deletions

View file

@ -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
View file

@ -0,0 +1,7 @@
import axios from "axios"
const axiosWithDefaults = axios.create({
baseURL: "http://localhost:8000",
})
export default axiosWithDefaults

View file

@ -12,7 +12,7 @@ import {
useFileDetails,
useFileMutations,
useFileFetches,
} from "../queries/files"
} from "../hooks/files"
interface FileDetailsProps {
itemId: string

View file

@ -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>
}

View file

@ -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()

View file

@ -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 }

View 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 }

View file

@ -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", () => {

View file

@ -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", () => {

View file

@ -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 }