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 851fc1d..50e2cbf 100644
--- a/client/src/common/layout/page/index.js
+++ b/client/src/common/layout/page/index.js
@@ -1,11 +1,12 @@
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'
import { useEffect } from 'react'
+import { LoadingLinear } from '@/common/ui/loadingLinear'
const Background = () => {
const activeTheme = useSyncLocalStorage('activeTheme')
@@ -20,8 +21,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': {
@@ -72,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)
@@ -79,6 +82,13 @@ function Page({ children }) {
updateSyncV('show.sidebar', true)
}
}, [isMobile])
+
+ useEffect(() => {
+ if (typeof animate === 'undefined') {
+ setSyncLocalStorage('animate', true)
+ }
+ }, [animate])
+
return (
<>
{animate && }
@@ -94,14 +104,15 @@ function Page({ children }) {
}}
>
+
- {showSidebar && auth.authStatus === 'signedIn' && }
+ {(!loading && showSidebar && auth.authStatus === 'signedIn' && !auth.authLoading) && }
diff --git a/client/src/common/ui/loadingLinear/index.js b/client/src/common/ui/loadingLinear/index.js
index 9c3b71f..df1d904 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.authLoading')
+ 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 && }
{
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..8bee474 100644
--- a/client/src/pages/_app.js
+++ b/client/src/pages/_app.js
@@ -1,16 +1,18 @@
-import PropTypes from 'prop-types'
-import Head from 'next/head'
-import { ThemeProvider } from '@mui/material/styles'
-import CssBaseline from '@mui/material/CssBaseline'
-import { CacheProvider } from '@emotion/react'
import '@/styles/globals.css'
+import { CacheProvider } from '@emotion/react'
+import CssBaseline from '@mui/material/CssBaseline'
+import { ThemeProvider } from '@mui/material/styles'
+import Head from 'next/head'
+import PropTypes from 'prop-types'
// MUI
+import useInitAuth from '@/hooks/useInitAuth'
+import { setSyncLocalStorage, useSyncLocalStorage } from '@/lib/hooks/useSync'
import createEmotionCache from '@/lib/mui/createEmotionCache'
-import { lightTheme, darkTheme } from '@/lib/mui/theme'
-import { useSyncLocalStorage } from '@/lib/hooks/useSync'
+import { darkTheme, lightTheme } from '@/lib/mui/theme'
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
@@ -21,7 +23,13 @@ const clientSideEmotionCache = createEmotionCache()
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 (
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 b6113c0..b0d1dff 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 { useSyncV } from 'use-sync-v'
const defaultState = {
joke:undefined,
@@ -11,7 +13,17 @@ const defaultState = {
function Index() {
const [state, setState] = useState(defaultState)
const activeTheme = useSyncLocalStorage('activeTheme')
+ const auth = useSyncV('auth')
+ const router = useRouter()
+ useEffect(()=>{
+ 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 4dbb4ef..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, authLoading } = useSyncV('auth')
+ const auth = useSyncV('auth')
class eventsHandler {
static usernameHandler = (e) => {
@@ -84,10 +84,11 @@ function Login () {
},[])
useEffect(() => {
- if (!authLoading && authUser) {
+ if (auth.authLoading) return
+ if (auth.authStatus === 'signedIn') {
router.push('/contacts')
}
- }, [router, authUser, authLoading])
+ }, [auth.authStatus, auth.authLoading, router])
const resetError = () => {
updateSyncV('auth', (p) => ({ ...p, authError: '' }))