From b6d6913f37c5f9def8d2f17343d4307b63a07cf5 Mon Sep 17 00:00:00 2001 From: RaviAnand Mohabir Date: Sat, 20 May 2023 00:15:48 +0200 Subject: [PATCH 1/4] feat: :sparkles: use UUID to track uploads and display multiple indicators --- package.json | 6 ++- src/hooks/useUploadFiles.tsx | 99 +++++++++++++++++++----------------- src/pages/posts/[id].tsx | 8 +-- yarn.lock | 10 ++++ 4 files changed, 71 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 2f7b739..695600e 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,10 @@ "react-dom": "18.2.0", "react-icons": "^4.8.0", "react-query": "^3.39.3", - "typescript": "5.0.4" + "typescript": "5.0.4", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@types/uuid": "^9.0.1" } } diff --git a/src/hooks/useUploadFiles.tsx b/src/hooks/useUploadFiles.tsx index 8f77682..daf9672 100644 --- a/src/hooks/useUploadFiles.tsx +++ b/src/hooks/useUploadFiles.tsx @@ -1,9 +1,10 @@ +import { usePocketBase } from "@/pocketbase"; import { File as ShareMeFile } from "@/pocketbase/models"; import { uploadFile } from "@/pocketbase/uploadFile"; import { notifications } from "@mantine/notifications"; import { IconAlertCircle } from "@tabler/icons-react"; -import { useState } from "react"; -import { usePocketBase } from "@/pocketbase"; +import { useCallback, useState } from "react"; +import { v4 as uuidv4 } from "uuid"; export interface NewFile { file: File; @@ -12,66 +13,70 @@ export interface NewFile { description: string; } +interface Upload { + file: NewFile; + uuid: string; +} + interface UseUploadFilesOptions { acceptTypes: string[]; } export const useUploadFiles = ({ acceptTypes }: UseUploadFilesOptions) => { const pb = usePocketBase(); - const [uploading, setUploading] = useState(false); + const [uploads, setUploads] = useState([] as Upload[]); - const uploadFiles = async (files: NewFile[]) => { - setUploading(true); + const uploadFiles = useCallback( + async (files: NewFile[]) => { + const promises = files.map(async (file) => { + if (!acceptTypes.includes(file.file.type)) return; - const promises = files.map(async (file) => { - if (!acceptTypes.includes(file.file.type)) return; + const uuid = uuidv4(); - try { - const createdRecord = await uploadFile(pb, { - file: file.file, - name: file.name, - author: file.author, - description: file.description, - }); - return createdRecord; - } catch (ex: any) { - console.error(ex); + try { + setUploads((uploads) => [...uploads, { file, uuid }]); + const createdRecord = await uploadFile(pb, file); + return createdRecord; + } catch (ex: any) { + console.error(ex); - if (ex.response) { - const { data, message } = ex.response; - if (message === "Failed to create record.") { - if (data.file) { - const { code, message } = data.file; - if (code === "validation_file_size_limit") { - notifications.show({ - color: "orange", - title: "File too large", - message: message, - icon: , - }); - return; + if (ex.response) { + const { data, message } = ex.response; + if (message === "Failed to create record.") { + if (data.file) { + const { code, message } = data.file; + if (code === "validation_file_size_limit") { + notifications.show({ + color: "orange", + title: "File too large", + message: message, + icon: , + }); + return; + } } } } - } - - notifications.show({ - color: "red", - title: "An error occured", - message: "Please contact the developers", - icon: , - }); - } - }); - const records = (await Promise.all(promises)).filter( - (r) => r !== undefined - ) as ShareMeFile[]; + notifications.show({ + color: "red", + title: "An error occured", + message: "Please contact the developers", + icon: , + }); + } finally { + setUploads((uploads) => uploads.filter(({ uuid: u }) => u !== uuid)); + } + }); - setUploading(false); + const records = (await Promise.all(promises)).filter( + (r) => r !== undefined + ) as ShareMeFile[]; - return records; - }; + return records; + }, + [setUploads, acceptTypes, pb] + ); - return { uploading, uploadFiles }; + return { uploads, uploadFiles, uploading: uploads.length !== 0 }; }; diff --git a/src/pages/posts/[id].tsx b/src/pages/posts/[id].tsx index 5f73bf6..4f3c7fb 100644 --- a/src/pages/posts/[id].tsx +++ b/src/pages/posts/[id].tsx @@ -78,7 +78,7 @@ export default function Post(props: PostProps) { const [debouncedTitle] = useDebouncedValue(title, 200, { leading: true }); - const { uploading, uploadFiles: _uploadFiles } = useUploadFiles({ + const { uploads, uploadFiles: _uploadFiles } = useUploadFiles({ acceptTypes: MEDIA_MIME_TYPE, }); @@ -481,9 +481,9 @@ export default function Post(props: PostProps) { ))} - {uploading && ( - - )} + {uploads.map(({ uuid }) => ( + + ))} {userIsAuthor && } Date: Sat, 20 May 2023 00:18:25 +0200 Subject: [PATCH 2/4] chore(server): :pushpin: run `go mod tidy` --- server/go.mod | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/go.mod b/server/go.mod index bc5bddf..30af4a2 100644 --- a/server/go.mod +++ b/server/go.mod @@ -38,7 +38,7 @@ require ( github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/ganigeorgiev/fexpr v0.3.0 // indirect github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/uuid v1.3.0 // indirect @@ -50,7 +50,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.16.5 // indirect - github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198 // indirect + github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198 github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.18 // indirect @@ -58,7 +58,7 @@ require ( github.com/meilisearch/meilisearch-go v0.24.0 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/cast v1.5.0 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect From 7c6202029460b69e553874864500bf98a18f44f3 Mon Sep 17 00:00:00 2001 From: RaviAnand Mohabir Date: Sat, 20 May 2023 00:18:54 +0200 Subject: [PATCH 3/4] style(frontend): :art: use path alias in imports --- src/pages/posts/[id].tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/posts/[id].tsx b/src/pages/posts/[id].tsx index 4f3c7fb..bf327b1 100644 --- a/src/pages/posts/[id].tsx +++ b/src/pages/posts/[id].tsx @@ -4,6 +4,7 @@ import Head from "@/components/head"; import Layout from "@/components/layout"; import UserAvatar from "@/components/userAvatar"; import { useCreatePost } from "@/hooks/useCreatePost"; +import { useRefCallback } from "@/hooks/useRefCallback"; import { useUploadFiles } from "@/hooks/useUploadFiles"; import { initPocketBaseServer, usePocketBase } from "@/pocketbase"; import { useAuth } from "@/pocketbase/auth"; @@ -43,7 +44,6 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { Record, RecordSubscription } from "pocketbase"; import { useCallback, useEffect, useState } from "react"; -import { useRefCallback } from "../../hooks/useRefCallback"; interface PostProps extends ShareMeEnv { title: string; From a71ec2e999fe6efe60b37844ca195d2b27dd3277 Mon Sep 17 00:00:00 2001 From: RaviAnand Mohabir Date: Sat, 20 May 2023 00:20:58 +0200 Subject: [PATCH 4/4] chore: :wrench: add conventional commit scopes --- .vscode/settings.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a0d0cfd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "conventionalCommits.scopes": [ + "server", + "tagger", + "search", + "frontend" + ] +}