diff --git a/package-lock.json b/package-lock.json index ab7043d..6b30e54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "maru", - "version": "0.1.0", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 6c4b318..f807c50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maru", - "version": "0.2.0", + "version": "0.3.0", "private": true, "scripts": { "dev": "next dev", diff --git a/src/app/lib/providers/AuthProvider.tsx b/src/app/lib/providers/AuthProvider.tsx index 9838cc8..1941ed1 100644 --- a/src/app/lib/providers/AuthProvider.tsx +++ b/src/app/lib/providers/AuthProvider.tsx @@ -8,7 +8,7 @@ import { useAuthActions, useAuthValue, } from '@/features/auth'; -import { load } from '@/shared/persist'; +import { load } from '@/shared/storage'; export function AuthProvider({ children }: { children: React.ReactNode }) { const auth = useAuthValue(); diff --git a/src/app/pages/main-page.tsx b/src/app/pages/main-page.tsx index 0e360d7..7e7b4ad 100644 --- a/src/app/pages/main-page.tsx +++ b/src/app/pages/main-page.tsx @@ -57,7 +57,7 @@ export function MainPage() { const { data } = useQuery({ queryKey: ['/api/auth/initial/info'], queryFn: getUserData, - enabled: auth?.accessToken !== undefined, + enabled: auth !== null, }); useEffect(() => { diff --git a/src/app/pages/shared-posts.tsx b/src/app/pages/shared-posts.tsx index 6dc4a0c..ef04b01 100644 --- a/src/app/pages/shared-posts.tsx +++ b/src/app/pages/shared-posts.tsx @@ -52,7 +52,7 @@ export function SharedPostsPage() { const { data } = useQuery({ queryKey: ['/api/auth/initial/info'], queryFn: getUserData, - enabled: auth?.accessToken !== undefined, + enabled: auth !== null, }); useEffect(() => { diff --git a/src/components/NavigationBar.tsx b/src/components/NavigationBar.tsx index 48809fa..443758c 100644 --- a/src/components/NavigationBar.tsx +++ b/src/components/NavigationBar.tsx @@ -12,7 +12,7 @@ import { useAuthIsLogin, useAuthValue, } from '@/features/auth'; -import { load } from '@/shared/persist'; +import { load } from '@/shared/storage'; const styles = { container: styled.nav` diff --git a/src/features/auth/auth.action.ts b/src/features/auth/auth.action.ts index eb61f4d..931d17c 100644 --- a/src/features/auth/auth.action.ts +++ b/src/features/auth/auth.action.ts @@ -6,17 +6,21 @@ import { authState } from './auth.atom'; import { type Auth } from './auth.model'; import { type User } from '@/entities/user'; -import { remove, save } from '@/shared/persist'; +import { remove, save } from '@/shared/storage'; export const useAuthActions = () => { const [, setAuth] = useRecoilState(authState); const login = useCallback( - (auth: Auth) => { + (auth: Auth & { accessToken: string }) => { axios.defaults.headers.common.Authorization = `Bearer ${auth.accessToken}`; save({ type: 'local', key: 'refreshToken', value: auth.refreshToken }); save({ type: 'local', key: 'expiresIn', value: `${auth.expiresIn}` }); - setAuth(auth); + setAuth({ + expiresIn: auth.expiresIn, + refreshToken: auth.refreshToken, + user: auth.user, + }); }, [setAuth], ); diff --git a/src/features/auth/auth.atom.ts b/src/features/auth/auth.atom.ts index 5637339..458e679 100644 --- a/src/features/auth/auth.atom.ts +++ b/src/features/auth/auth.atom.ts @@ -2,7 +2,15 @@ import { atom } from 'recoil'; import { type Auth } from './auth.model'; +import { storageEffect } from '@/shared/persist'; + export const authState = atom({ key: 'authState', default: null, + effects: [ + storageEffect({ + key: 'auth-state', + storageType: 'session', + }), + ], }); diff --git a/src/features/auth/auth.model.ts b/src/features/auth/auth.model.ts index ba2c3c1..4bc6f80 100644 --- a/src/features/auth/auth.model.ts +++ b/src/features/auth/auth.model.ts @@ -1,7 +1,6 @@ import { type User } from '@/entities/user'; export interface Auth { - accessToken: string; refreshToken: string; expiresIn: number; user?: User; diff --git a/src/shared/persist/effect.ts b/src/shared/persist/effect.ts new file mode 100644 index 0000000..9cad087 --- /dev/null +++ b/src/shared/persist/effect.ts @@ -0,0 +1,28 @@ +import { type AtomEffect, type DefaultValue } from 'recoil'; + +import { load, remove, save, type StorageType } from '@/shared/storage'; + +export const storageEffect = + ({ + key, + storageType, + }: { + key: string; + storageType: StorageType; + }): AtomEffect => + ({ setSelf, onSet }) => { + if (typeof window === 'undefined') return; + + const savedValue = load({ type: storageType, key }); + if (savedValue != null) { + setSelf(load({ type: storageType, key }) as StoredType); + } + + onSet((newValue: StoredType | DefaultValue, _, isReset: boolean) => { + if (isReset) { + remove({ type: storageType, key }); + } else { + save({ type: storageType, key, value: JSON.stringify(newValue) }); + } + }); + }; diff --git a/src/shared/persist/index.ts b/src/shared/persist/index.ts index d406816..9f0d90c 100644 --- a/src/shared/persist/index.ts +++ b/src/shared/persist/index.ts @@ -1 +1 @@ -export * from './store'; +export * from './effect'; diff --git a/src/shared/storage/index.ts b/src/shared/storage/index.ts new file mode 100644 index 0000000..85674ee --- /dev/null +++ b/src/shared/storage/index.ts @@ -0,0 +1 @@ +export * from './storage'; diff --git a/src/shared/persist/store.ts b/src/shared/storage/storage.ts similarity index 56% rename from src/shared/persist/store.ts rename to src/shared/storage/storage.ts index 747a8bf..5d54a99 100644 --- a/src/shared/persist/store.ts +++ b/src/shared/storage/storage.ts @@ -1,14 +1,16 @@ -type storeType = 'session' | 'local'; +export type StorageType = 'session' | 'local'; export const save = ({ type, key, value, }: { - type: storeType; + type: StorageType; key: string; value: string; }) => { + if (typeof window === 'undefined') return; + if (type === 'local') { localStorage.setItem(key, value); } else { @@ -16,14 +18,18 @@ export const save = ({ } }; -export const load = ({ type, key }: { type: storeType; key: string }) => { +export const load = ({ type, key }: { type: StorageType; key: string }) => { + if (typeof window === 'undefined') return null; + if (type === 'local') { return localStorage.getItem(key); } return sessionStorage.getItem(key); }; -export const remove = ({ type, key }: { type: storeType; key: string }) => { +export const remove = ({ type, key }: { type: StorageType; key: string }) => { + if (typeof window === 'undefined') return; + if (type === 'local') { localStorage.removeItem(key); } else {