From 27998405a700d521579a68ca638de4afee27cb87 Mon Sep 17 00:00:00 2001 From: vikyw Date: Mon, 1 May 2023 02:44:17 -0400 Subject: [PATCH 1/5] changes: - when visiting / - signed in user will be redirected to contacts/ - unsigned in user will be redirected to login/ - when visiting login - signed in user will be redirected to contacts/ --- client/src/pages/index.js | 13 ++++++++++++- client/src/pages/login/index.js | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/client/src/pages/index.js b/client/src/pages/index.js index b6113c0..03ddb44 100644 --- a/client/src/pages/index.js +++ b/client/src/pages/index.js @@ -1,7 +1,9 @@ import HomeComponent from '@/components/home' import { useSyncLocalStorage } from '@/lib/hooks/useSync' import { getRandomJoke } from '@/lib/services/random' +import { useRouter } from 'next/router' import { useEffect, useState } from 'react' +import { debugSyncV, useSyncV } from 'use-sync-v' const defaultState = { joke:undefined, @@ -11,7 +13,16 @@ const defaultState = { function Index() { const [state, setState] = useState(defaultState) const activeTheme = useSyncLocalStorage('activeTheme') - + const auth = useSyncV('auth') + const router = useRouter() + + useEffect(()=>{ + if (auth.authUser) { + router.push('/contacts') + } else { + router.push('/login') + } + }) useEffect(()=>{ (async()=>{ const randomJoke = await getRandomJoke() diff --git a/client/src/pages/login/index.js b/client/src/pages/login/index.js index 4dbb4ef..825526a 100644 --- a/client/src/pages/login/index.js +++ b/client/src/pages/login/index.js @@ -84,10 +84,10 @@ function Login () { },[]) useEffect(() => { - if (!authLoading && authUser) { + if (authUser) { router.push('/contacts') } - }, [router, authUser, authLoading]) + }, [authUser]) const resetError = () => { updateSyncV('auth', (p) => ({ ...p, authError: '' })) From a73813adc012de7e3c15ee12c81705a93f6a85bd Mon Sep 17 00:00:00 2001 From: vikyw Date: Mon, 1 May 2023 02:51:31 -0400 Subject: [PATCH 2/5] fix: lint --- client/src/pages/index.js | 4 ++-- client/src/pages/login/index.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/pages/index.js b/client/src/pages/index.js index 03ddb44..2e55f7e 100644 --- a/client/src/pages/index.js +++ b/client/src/pages/index.js @@ -3,7 +3,7 @@ import { useSyncLocalStorage } from '@/lib/hooks/useSync' import { getRandomJoke } from '@/lib/services/random' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' -import { debugSyncV, useSyncV } from 'use-sync-v' +import { useSyncV } from 'use-sync-v' const defaultState = { joke:undefined, @@ -15,7 +15,7 @@ function Index() { const activeTheme = useSyncLocalStorage('activeTheme') const auth = useSyncV('auth') const router = useRouter() - + useEffect(()=>{ if (auth.authUser) { router.push('/contacts') diff --git a/client/src/pages/login/index.js b/client/src/pages/login/index.js index 825526a..3e2a878 100644 --- a/client/src/pages/login/index.js +++ b/client/src/pages/login/index.js @@ -27,7 +27,7 @@ function Login () { const [state, setState] = useState(defaultState) const { username, password } = state const router = useRouter() - const { authUser, authLoading } = useSyncV('auth') + const { authUser } = useSyncV('auth') class eventsHandler { static usernameHandler = (e) => { @@ -87,7 +87,7 @@ function Login () { if (authUser) { router.push('/contacts') } - }, [authUser]) + }, [authUser, router]) const resetError = () => { updateSyncV('auth', (p) => ({ ...p, authError: '' })) From f952426e1ba8616a5cf16eae7bde8568d68b0fad Mon Sep 17 00:00:00 2001 From: vikyw Date: Mon, 1 May 2023 04:12:06 -0400 Subject: [PATCH 3/5] changelogs: - set default of animate to true - set default of theme to 'light' - updated / fix useSyncLocalStorage JSON.parsing --- client/src/common/layout/page/index.js | 12 ++- client/src/lib/hooks/useSync.js | 100 +++---------------------- client/src/pages/_app.js | 9 ++- 3 files changed, 25 insertions(+), 96 deletions(-) diff --git a/client/src/common/layout/page/index.js b/client/src/common/layout/page/index.js index 851fc1d..d253813 100644 --- a/client/src/common/layout/page/index.js +++ b/client/src/common/layout/page/index.js @@ -1,7 +1,7 @@ import Box from '@mui/material/Box' import Header from '@/common/layout/header' import Section from '@/common/layout/section' -import { useSyncLocalStorage } from '@/lib/hooks/useSync' +import { setSyncLocalStorage, useSyncLocalStorage } from '@/lib/hooks/useSync' import { Sidebar } from '../sidebar' import { updateSyncV, useSyncV } from 'use-sync-v' import { Divider, useMediaQuery } from '@mui/material' @@ -20,8 +20,7 @@ const Background = () => { top: 0, width: '100%', height: '100%', - animation: `animate 90s linear infinite, ${ - activeTheme === 'dark' ? 'darkColorSwitcher' : 'lightColorSwitcher' + animation: `animate 90s linear infinite, ${activeTheme === 'dark' ? 'darkColorSwitcher' : 'lightColorSwitcher' } 42s linear infinite`, }, '&:after': { @@ -79,6 +78,11 @@ function Page({ children }) { updateSyncV('show.sidebar', true) } }, [isMobile]) + useEffect(() => { + if (typeof animate === 'undefined') { + setSyncLocalStorage('animate', true) + } + },[animate]) return ( <> {animate && } @@ -98,7 +102,7 @@ function Page({ children }) { sx={{ flex: 1, display: 'flex', - overflowY:'scroll' + overflowY: 'scroll' }} > {showSidebar && auth.authStatus === 'signedIn' && } diff --git a/client/src/lib/hooks/useSync.js b/client/src/lib/hooks/useSync.js index 64bcbf6..9082c38 100644 --- a/client/src/lib/hooks/useSync.js +++ b/client/src/lib/hooks/useSync.js @@ -1,8 +1,6 @@ import { useCallback, useSyncExternalStore } from 'react' const useSyncLocalStorageSubscribers = {} -const useSyncStoreSubscribers = {} -const store = {} export const useSyncLocalStorage = (saveDirectory = 'global') => { const subscribe = useCallback( @@ -34,7 +32,11 @@ export const useSyncLocalStorage = (saveDirectory = 'global') => { const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) - return state ? JSON.parse(state) : undefined + try { + return JSON.parse(state) + } catch (e) { + return state + } } export const setSyncLocalStorage = (saveDirectory = 'global', updatedValue) => { @@ -43,94 +45,12 @@ export const setSyncLocalStorage = (saveDirectory = 'global', updatedValue) => { subscriber() } } - localStorage[saveDirectory] = JSON.stringify(updatedValue) - emitChange() -} - -// this is an attempt to solve the bug, and detach getter from setter, so calling setter won't neccesarily subscribe the component to the store -/** - * Returns the state of the given save directory in the store, by subscribing to the save directory - * and using an external state manager to manage state updates. - * - * @param {string} saveDirectory - The save directory to be subscribed to. - * @returns {*} The state of the given save directory in the store. - */ -export const useSyncStore = (saveDirectory = 'global') => { - /** - * Subscribes to the given save directory and returns a function to unsubscribe from the - * save directory. - * - * @param {Function} callback - The callback function to be called when the save directory changes. - * @returns {Function} A function to unsubscribe from the save directory. - */ - const subscribe = useCallback( - (callback) => { - if (!useSyncStoreSubscribers[saveDirectory]) { - useSyncStoreSubscribers[saveDirectory] = [] - } - - // Adds the callback function to the list of subscribers for the save directory - useSyncStoreSubscribers[saveDirectory] = [ - ...useSyncStoreSubscribers[saveDirectory], - callback, - ] - - // Returns a function to remove the callback function from the list of subscribers - return () => { - useSyncStoreSubscribers[saveDirectory] = useSyncStoreSubscribers[ - saveDirectory - ].filter((el) => el !== callback) - } - }, - [saveDirectory] - ) - - /** - * Returns a snapshot of the state of the given save directory in the store. - * - * @returns {*} A snapshot of the state of the given save directory in the store. - */ - const getSnapshot = () => { - return JSON.stringify(store[saveDirectory]) - } - - /** - * Returns a snapshot of the state of the given save directory in the server. - * - * @returns {*} A snapshot of the state of the given save directory in the server. - */ - const getServerSnapshot = () => { - return JSON.stringify(store[saveDirectory]) - } - - - // Uses an external state manager to manage state updates and returns the state of the given save directory - const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) - - // Returns the parsed state of the given save directory - return state ? JSON.parse(state) : undefined -} - -/** - * Sets the updated value to the given save directory in the store, and notifies all subscribers - * subscribed to the save directory. - * - * @param {string} saveDirectory - The save directory in which to set the updated value. - * @param {*} updatedValue - The updated value to be set in the save directory. - */ -export const setSyncStore = (saveDirectory = 'global', updatedValue) => { - /** - * Emits change event to all subscribers subscribed to the save directory. - */ - const emitChange = () => { - for (let subscriber of useSyncStoreSubscribers[saveDirectory]) { - subscriber() - } + try { + const data = JSON.stringify(updatedValue) + localStorage[saveDirectory] = data + } catch(e) { + localStorage[saveDirectory] = updatedValue } - // Sets the updated value to the given save directory in the store - store[saveDirectory] = updatedValue - - // Notifies all subscribers subscribed to the save directory emitChange() } \ No newline at end of file diff --git a/client/src/pages/_app.js b/client/src/pages/_app.js index 336058e..bfaad6d 100644 --- a/client/src/pages/_app.js +++ b/client/src/pages/_app.js @@ -8,9 +8,10 @@ import '@/styles/globals.css' // MUI import createEmotionCache from '@/lib/mui/createEmotionCache' import { lightTheme, darkTheme } from '@/lib/mui/theme' -import { useSyncLocalStorage } from '@/lib/hooks/useSync' +import { setSyncLocalStorage, useSyncLocalStorage } from '@/lib/hooks/useSync' import { init } from '@/lib/store' import useInitAuth from '@/hooks/useInitAuth' +import { useEffect } from 'react' init() // Source: https://github.com/mui/material-ui/tree/master/examples/material-next @@ -22,7 +23,11 @@ export default function MyApp(props) { const activeTheme = useSyncLocalStorage('activeTheme') const { Component, emotionCache = clientSideEmotionCache, pageProps } = props useInitAuth() - + useEffect(()=>{ + if (typeof activeTheme === 'undefined') { + setSyncLocalStorage('activeTheme', 'light') + } + }, [activeTheme]) return ( From 35decaa680e0222e0b654612875c0bfbba0153d6 Mon Sep 17 00:00:00 2001 From: vikyw Date: Tue, 2 May 2023 12:36:09 +0800 Subject: [PATCH 4/5] - updated useSyncV, better snippet and autoComplete for config - moved loading to separate components, listening to change to - contacts loading - useRouter() change of url - temporarily removed loading true in [doc_id] route, to prevent infinite loading - fix console error on login/ and contacts/ and / route --- client/package-lock.json | 11 +++--- client/package.json | 2 +- client/src/common/layout/page/index.js | 11 +++++- client/src/common/ui/loadingLinear/index.js | 41 +++++++++++++++++---- client/src/components/contacts/index.js | 5 --- client/src/pages/_app.js | 19 ++++++---- client/src/pages/contacts/edit/[doc_id].js | 4 +- client/src/pages/contacts/index.js | 7 ++-- client/src/pages/index.js | 5 ++- client/src/pages/login/index.js | 7 ++-- 10 files changed, 73 insertions(+), 39 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index d3d3fea..86ea40d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -22,7 +22,7 @@ "next": "13.2.1", "react": "18.2.0", "react-dom": "18.2.0", - "use-sync-v": "^2.0.19" + "use-sync-v": "^2.2.0" }, "engines": { "node": "18.14.2", @@ -4962,13 +4962,14 @@ } }, "node_modules/use-sync-v": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/use-sync-v/-/use-sync-v-2.0.19.tgz", - "integrity": "sha512-Ah6ztsfgYrtzdzaDBNozvwqp1sQw8TX1emozd8Dn4OmyGOA/k6ovo7ujmXEVPYlVXDNViqVIFgURVZM7zqVEQA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/use-sync-v/-/use-sync-v-2.2.0.tgz", + "integrity": "sha512-jjRfYr/zQhgWlpQbVu6KlRDSej60Pk+3osmvjpo6u4v6iN06VKW6cTew2dw+QG1ju0L2NKSsOBXWV7jQ4XTXkg==", "dependencies": { "diff": "^5.1.0", "lodash-es": "^4.17.21", - "react": "^18.2.0" + "react": "^18.2.0", + "use-sync-v": "^2.1.4" } }, "node_modules/util-deprecate": { diff --git a/client/package.json b/client/package.json index d2ac73e..d256ab5 100644 --- a/client/package.json +++ b/client/package.json @@ -29,6 +29,6 @@ "next": "13.2.1", "react": "18.2.0", "react-dom": "18.2.0", - "use-sync-v": "^2.0.19" + "use-sync-v": "^2.2.0" } } diff --git a/client/src/common/layout/page/index.js b/client/src/common/layout/page/index.js index d253813..50e2cbf 100644 --- a/client/src/common/layout/page/index.js +++ b/client/src/common/layout/page/index.js @@ -6,6 +6,7 @@ import { Sidebar } from '../sidebar' import { updateSyncV, useSyncV } from 'use-sync-v' import { Divider, useMediaQuery } from '@mui/material' import { useEffect } from 'react' +import { LoadingLinear } from '@/common/ui/loadingLinear' const Background = () => { const activeTheme = useSyncLocalStorage('activeTheme') @@ -71,6 +72,9 @@ function Page({ children }) { const animate = useSyncLocalStorage('animate') const showSidebar = useSyncV('show.sidebar') const isMobile = useMediaQuery('(max-width:900px)') + const loading = useSyncV('show.loading') + + useEffect(() => { if (isMobile) { updateSyncV('show.sidebar', false) @@ -78,11 +82,13 @@ function Page({ children }) { updateSyncV('show.sidebar', true) } }, [isMobile]) + useEffect(() => { if (typeof animate === 'undefined') { setSyncLocalStorage('animate', true) } - },[animate]) + }, [animate]) + return ( <> {animate && } @@ -98,6 +104,7 @@ function Page({ children }) { }} >
+ - {showSidebar && auth.authStatus === 'signedIn' && } + {(!loading && showSidebar && auth.authStatus === 'signedIn' && !auth.authLoading) && }
{children}
diff --git a/client/src/common/ui/loadingLinear/index.js b/client/src/common/ui/loadingLinear/index.js index 9c3b71f..a511824 100644 --- a/client/src/common/ui/loadingLinear/index.js +++ b/client/src/common/ui/loadingLinear/index.js @@ -1,12 +1,39 @@ -import { LinearProgress } from '@mui/material' +import { Box, LinearProgress } from '@mui/material' +import { useRouter } from 'next/router' +import { useEffect } from 'react' +import { updateSyncV, useSyncV } from 'use-sync-v' export const LoadingLinear = () => { + const authLoading = useSyncV('auth.loading') + const loadingContacts = useSyncV('contacts.loading') + const loading = useSyncV('show.loading') + + const router = useRouter() + useEffect(() => { + const changeStartHandler = () => { + updateSyncV('show.loading', true) + } + const changeCompleteHandler = () => { + updateSyncV('show.loading', false) + } + router.events.on('routeChangeStart', changeStartHandler) + router.events.on('routeChangeComplete', changeCompleteHandler) + // If the component is unmounted, unsubscribe + // from the event with the `off` method: + return () => { + router.events.off('routeChangeStart', changeStartHandler) + router.events.off('routeChangeComplete', changeCompleteHandler) + } + }, [router]) return ( - + + {(loadingContacts || authLoading || loading) && } + + ) } \ No newline at end of file diff --git a/client/src/components/contacts/index.js b/client/src/components/contacts/index.js index b93183f..a3458c0 100644 --- a/client/src/components/contacts/index.js +++ b/client/src/components/contacts/index.js @@ -1,15 +1,10 @@ import Page from '@/common/layout/page' -import { LoadingLinear } from '@/common/ui/loadingLinear' import { Box } from '@mui/material' -import { useAsyncV } from 'use-sync-v' import { ContactListDisplay } from './display' function ContactsComponent() { - const { loading } = useAsyncV('contacts') - return ( - {loading && } { if (typeof activeTheme === 'undefined') { setSyncLocalStorage('activeTheme', 'light') } }, [activeTheme]) + return ( diff --git a/client/src/pages/contacts/edit/[doc_id].js b/client/src/pages/contacts/edit/[doc_id].js index 5a0467c..9dfe98e 100644 --- a/client/src/pages/contacts/edit/[doc_id].js +++ b/client/src/pages/contacts/edit/[doc_id].js @@ -1,6 +1,6 @@ import { useRouter } from 'next/router' import { useEffect, useMemo, useState } from 'react' -import { deleteSyncV, updateSyncV, useAsyncV, useSyncV } from 'use-sync-v' +import { deleteSyncV, useAsyncV, useSyncV } from 'use-sync-v' import { FirebaseFirestore } from '@/lib/utils/firebase/firestore' import { deleteFileFromStorage, uploadFileToStorage } from '@/lib/utils/firebase/storageutils' @@ -87,7 +87,7 @@ const EditContact = () => { setIsFormChanged(true) } } - updateSyncV('contacts.loading', true) + const saveHandler = async () => { const createdContact = { ...form, diff --git a/client/src/pages/contacts/index.js b/client/src/pages/contacts/index.js index b2ba124..0dc8262 100644 --- a/client/src/pages/contacts/index.js +++ b/client/src/pages/contacts/index.js @@ -7,12 +7,12 @@ import { useEffect } from 'react' import { updateAsyncV, useSyncV } from 'use-sync-v' function Contacts() { - const user = useSyncV('auth') const showExportPopup = useSyncV('show.exportPopup') + const auth = useSyncV('auth') useEffect(() => { FirebaseFirestore.subscribeCol( - `users/${user.authUser.uid}/contacts`, + `users/${auth.authUser.uid}/contacts`, (querySnapshot) => { const data = [] querySnapshot.forEach((doc) => { @@ -24,8 +24,7 @@ function Contacts() { }, orderBy('sorting') ) - }, [user.authUser.uid]) - + }, [auth.authUser.uid]) return ( <> {showExportPopup && } diff --git a/client/src/pages/index.js b/client/src/pages/index.js index 2e55f7e..b0d1dff 100644 --- a/client/src/pages/index.js +++ b/client/src/pages/index.js @@ -17,12 +17,13 @@ function Index() { const router = useRouter() useEffect(()=>{ - if (auth.authUser) { + if (auth.authLoading) return + if (auth.authStatus === 'signedIn') { router.push('/contacts') } else { router.push('/login') } - }) + },[auth.authLoading, auth.authStatus, router]) useEffect(()=>{ (async()=>{ const randomJoke = await getRandomJoke() diff --git a/client/src/pages/login/index.js b/client/src/pages/login/index.js index 3e2a878..74bd01b 100644 --- a/client/src/pages/login/index.js +++ b/client/src/pages/login/index.js @@ -27,7 +27,7 @@ function Login () { const [state, setState] = useState(defaultState) const { username, password } = state const router = useRouter() - const { authUser } = useSyncV('auth') + const auth = useSyncV('auth') class eventsHandler { static usernameHandler = (e) => { @@ -84,10 +84,11 @@ function Login () { },[]) useEffect(() => { - if (authUser) { + if (auth.authLoading) return + if (auth.authStatus === 'signedIn') { router.push('/contacts') } - }, [authUser, router]) + }, [auth.authStatus, auth.authLoading, router]) const resetError = () => { updateSyncV('auth', (p) => ({ ...p, authError: '' })) From 85d624bf0765835965ae2280fc9370c556769284 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Tue, 2 May 2023 16:23:42 +0800 Subject: [PATCH 5/5] fix: auth.authLoading --- client/src/common/ui/loadingLinear/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/common/ui/loadingLinear/index.js b/client/src/common/ui/loadingLinear/index.js index a511824..df1d904 100644 --- a/client/src/common/ui/loadingLinear/index.js +++ b/client/src/common/ui/loadingLinear/index.js @@ -4,7 +4,7 @@ import { useEffect } from 'react' import { updateSyncV, useSyncV } from 'use-sync-v' export const LoadingLinear = () => { - const authLoading = useSyncV('auth.loading') + const authLoading = useSyncV('auth.authLoading') const loadingContacts = useSyncV('contacts.loading') const loading = useSyncV('show.loading')