From 861da0c145f337c189c18f2fd0571f012803af7a Mon Sep 17 00:00:00 2001 From: soup-bowl Date: Sun, 26 Nov 2023 20:10:36 +0000 Subject: [PATCH] Error handling and notification in upload. --- package-lock.json | 38 +++++++++++++++++++++++++++ package.json | 1 + src/App.tsx | 57 +++++++++++++++++++++------------------- src/interfaces.ts | 9 +++++++ src/pages/scratchpad.tsx | 26 +++++++++++++----- src/utils/scratch.ts | 10 ++++++- 6 files changed, 107 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index d413afa..808ea96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", "libwhatsthis": "^0.1.2", + "notistack": "^3.0.1", "react": "^18.0.0", "react-dom": "^18.0.0", "react-router-dom": "^6.11.0", @@ -4719,6 +4720,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/goober": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz", + "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -5485,6 +5494,35 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/notistack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.1.tgz", + "integrity": "sha512-ntVZXXgSQH5WYfyU+3HfcXuKaapzAJ8fBLQ/G618rn3yvSzEbnOB8ZSOwhX+dAORy/lw+GC2N061JA0+gYWTVA==", + "dependencies": { + "clsx": "^1.1.0", + "goober": "^2.0.33" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/notistack" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/notistack/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/package.json b/package.json index 106a11d..f2c6a1c 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", "libwhatsthis": "^0.1.2", + "notistack": "^3.0.1", "react": "^18.0.0", "react-dom": "^18.0.0", "react-router-dom": "^6.11.0", diff --git a/src/App.tsx b/src/App.tsx index fbc231f..1f99b03 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,10 +9,11 @@ import { } from './pages'; import { APIContext, ConnectionContext } from './context'; import { APIAgentType, Agent } from 'libwhatsthis'; +import { SnackbarProvider } from 'notistack'; const App = () => { const [connectionState, setConnectionState] = useState(navigator.onLine); - + const apiAgent: APIAgentType = new Agent(import.meta.env.VITE_API_URL); useEffect(() => { @@ -32,32 +33,34 @@ const App = () => { - - - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - + + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + diff --git a/src/interfaces.ts b/src/interfaces.ts index c63f068..fb682dc 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -57,3 +57,12 @@ export interface IScratchpadItem { message: string; image?: string; } + +export interface IPossibleScratchpadItem { + id?: string; + created?: number; + title?: string; + type?: ScratchpadItemType; + message?: string; + image?: string; +} diff --git a/src/pages/scratchpad.tsx b/src/pages/scratchpad.tsx index dc592d8..3f496ed 100644 --- a/src/pages/scratchpad.tsx +++ b/src/pages/scratchpad.tsx @@ -1,9 +1,10 @@ import { useEffect, useState } from "react"; import { Alert, AlertTitle, Box, Button, ListItemIcon, ListItemText, Menu, MenuItem, Stack, Typography } from "@mui/material"; +import { useSnackbar } from 'notistack'; import { Scratches } from "../components"; import { ScratchEditorModal } from "../modals"; -import { addScratch, createItem, getScratches, removeScratch, saveScratches, updateScratch } from "../utils/scratch"; -import { IScratchpadItem } from "../interfaces"; +import { addScratch, createItem, createItemViaPossible, getScratches, removeScratch, saveScratches, updateScratch } from "../utils/scratch"; +import { IPossibleScratchpadItem, IScratchpadItem } from "../interfaces"; import AddIcon from '@mui/icons-material/Add'; import MoreVertIcon from '@mui/icons-material/MoreVert'; @@ -16,6 +17,7 @@ const ScratchpadPage = () => { const [openNewNote, setOpenNewNote] = useState(false); const [menuAnchorEl, setMenuAnchorEl] = useState(null); const openMenuDialog = Boolean(menuAnchorEl); + const { enqueueSnackbar } = useSnackbar(); useEffect(() => { document.title = `${siteTitle} - What's This?` }); @@ -67,11 +69,23 @@ const ScratchpadPage = () => { reader.onload = (e) => { const text = (e.target?.result || "") as string; try { - const data = JSON.parse(text) as IScratchpadItem[]; - saveScratches(data); - setScratches(getScratches()); + const data = JSON.parse(text) as IPossibleScratchpadItem[]; + + let collection = getScratches() ?? []; + let count = 0; + data.forEach((item) => { + if (collection.find(exist => exist.id === item.id) === undefined) { + collection?.push(createItemViaPossible(item)); + count++; + } + }); + + saveScratches(collection); + setScratches(collection); + enqueueSnackbar(`Imported ${count.toString()} scratches`); } catch (error) { - console.error("Error parsing JSON:", error); + console.error("Error parsing file:", error); + enqueueSnackbar("An error occurred processing the import"); } finally { handleCloseMenu(); } diff --git a/src/utils/scratch.ts b/src/utils/scratch.ts index 2f53f00..5b0855d 100644 --- a/src/utils/scratch.ts +++ b/src/utils/scratch.ts @@ -1,5 +1,5 @@ import { ScratchpadItemType } from "../enums"; -import { IScratchpadItem } from "../interfaces"; +import { IPossibleScratchpadItem, IScratchpadItem } from "../interfaces"; import { readFromLocalStorage, writeToLocalStorage } from "./localStorage"; export const getScratches = () => readFromLocalStorage('WTScratchpadItems'); @@ -32,3 +32,11 @@ export const createItem = (title: string, message: string): IScratchpadItem => ( title: title, message: message, }); + +export const createItemViaPossible = (item: IPossibleScratchpadItem):IScratchpadItem => ({ + id: item.id ?? self.crypto.randomUUID(), + created: item.created ?? Date.now(), + type: item.type ?? ScratchpadItemType.Text, + title: item.title ?? 'Undefined Title', + message: item.message ?? '', +});