Eagerly load admin nav with client-side cookie strategy
This commit is contained in:
parent
ac19ed2215
commit
2a0e898ba6
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -6,6 +6,7 @@
|
|||||||
"ARROWLEFT",
|
"ARROWLEFT",
|
||||||
"ARROWRIGHT",
|
"ARROWRIGHT",
|
||||||
"Astia",
|
"Astia",
|
||||||
|
"authjs",
|
||||||
"camelcase",
|
"camelcase",
|
||||||
"cloudflarestorage",
|
"cloudflarestorage",
|
||||||
"cmdk",
|
"cmdk",
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import { FiTag } from 'react-icons/fi';
|
|||||||
import { BiLockAlt } from 'react-icons/bi';
|
import { BiLockAlt } from 'react-icons/bi';
|
||||||
import AdminAppInfoIcon from './AdminAppInfoIcon';
|
import AdminAppInfoIcon from './AdminAppInfoIcon';
|
||||||
import { PiSignOutBold } from 'react-icons/pi';
|
import { PiSignOutBold } from 'react-icons/pi';
|
||||||
import { signOutAndRedirectAction } from '@/auth/actions';
|
import { signOutAction } from '@/auth/actions';
|
||||||
import { ComponentProps } from 'react';
|
import { ComponentProps } from 'react';
|
||||||
import { FaRegFolderOpen } from 'react-icons/fa';
|
import { FaRegFolderOpen } from 'react-icons/fa';
|
||||||
|
|
||||||
@ -34,6 +34,7 @@ export default function AdminAppMenu({
|
|||||||
tagsCount,
|
tagsCount,
|
||||||
selectedPhotoIds,
|
selectedPhotoIds,
|
||||||
setSelectedPhotoIds,
|
setSelectedPhotoIds,
|
||||||
|
clearAuthStateAndRedirect,
|
||||||
} = useAppState();
|
} = useAppState();
|
||||||
|
|
||||||
const isSelecting = selectedPhotoIds !== undefined;
|
const isSelecting = selectedPhotoIds !== undefined;
|
||||||
@ -107,7 +108,7 @@ export default function AdminAppMenu({
|
|||||||
}, {
|
}, {
|
||||||
label: 'Sign Out',
|
label: 'Sign Out',
|
||||||
icon: <PiSignOutBold size={15} />,
|
icon: <PiSignOutBold size={15} />,
|
||||||
action: signOutAndRedirectAction,
|
action: () => signOutAction().then(clearAuthStateAndRedirect),
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import RepoLink from '../components/RepoLink';
|
|||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import { PATH_ADMIN_PHOTOS, isPathAdmin, isPathSignIn } from './paths';
|
import { PATH_ADMIN_PHOTOS, isPathAdmin, isPathSignIn } from './paths';
|
||||||
import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus';
|
import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus';
|
||||||
import { signOutAndRedirectAction } from '@/auth/actions';
|
import { signOutAction } from '@/auth/actions';
|
||||||
import Spinner from '@/components/Spinner';
|
import Spinner from '@/components/Spinner';
|
||||||
import AnimateItems from '@/components/AnimateItems';
|
import AnimateItems from '@/components/AnimateItems';
|
||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
@ -17,7 +17,7 @@ import { useAppState } from '@/state/AppState';
|
|||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
const { userEmail, setUserEmail } = useAppState();
|
const { userEmail, clearAuthStateAndRedirect } = useAppState();
|
||||||
|
|
||||||
const showFooter = !isPathSignIn(pathname);
|
const showFooter = !isPathSignIn(pathname);
|
||||||
|
|
||||||
@ -48,8 +48,8 @@ export default function Footer() {
|
|||||||
)}>
|
)}>
|
||||||
{userEmail}
|
{userEmail}
|
||||||
</div>
|
</div>
|
||||||
<form action={() => signOutAndRedirectAction()
|
<form action={() => signOutAction()
|
||||||
.then(() => setUserEmail?.(undefined))}>
|
.then(clearAuthStateAndRedirect)}>
|
||||||
<SubmitButtonWithStatus styleAs="link">
|
<SubmitButtonWithStatus styleAs="link">
|
||||||
Sign out
|
Sign out
|
||||||
</SubmitButtonWithStatus>
|
</SubmitButtonWithStatus>
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { useAppState } from '@/state/AppState';
|
|||||||
import { GRID_HOMEPAGE_ENABLED } from './config';
|
import { GRID_HOMEPAGE_ENABLED } from './config';
|
||||||
import AdminAppMenu from '@/admin/AdminAppMenu';
|
import AdminAppMenu from '@/admin/AdminAppMenu';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
|
import Spinner from '@/components/Spinner';
|
||||||
|
|
||||||
export type SwitcherSelection = 'feed' | 'grid' | 'admin';
|
export type SwitcherSelection = 'feed' | 'grid' | 'admin';
|
||||||
|
|
||||||
@ -19,9 +20,13 @@ export default function ViewSwitcher({
|
|||||||
}: {
|
}: {
|
||||||
currentSelection?: SwitcherSelection
|
currentSelection?: SwitcherSelection
|
||||||
}) {
|
}) {
|
||||||
const { setIsCommandKOpen, isUserSignedIn } = useAppState();
|
const {
|
||||||
|
isUserSignedIn,
|
||||||
|
isUserSignedInEager,
|
||||||
|
setIsCommandKOpen,
|
||||||
|
} = useAppState();
|
||||||
|
|
||||||
const renderItemFeed = () =>
|
const renderItemFeed =
|
||||||
<SwitcherItem
|
<SwitcherItem
|
||||||
icon={<IconFeed />}
|
icon={<IconFeed />}
|
||||||
href={PATH_FEED_INFERRED}
|
href={PATH_FEED_INFERRED}
|
||||||
@ -29,7 +34,7 @@ export default function ViewSwitcher({
|
|||||||
noPadding
|
noPadding
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
const renderItemGrid = () =>
|
const renderItemGrid =
|
||||||
<SwitcherItem
|
<SwitcherItem
|
||||||
icon={<IconGrid />}
|
icon={<IconGrid />}
|
||||||
href={PATH_GRID_INFERRED}
|
href={PATH_GRID_INFERRED}
|
||||||
@ -40,19 +45,29 @@ export default function ViewSwitcher({
|
|||||||
return (
|
return (
|
||||||
<div className="flex gap-1 sm:gap-2">
|
<div className="flex gap-1 sm:gap-2">
|
||||||
<Switcher>
|
<Switcher>
|
||||||
{GRID_HOMEPAGE_ENABLED ? renderItemGrid() : renderItemFeed()}
|
{GRID_HOMEPAGE_ENABLED ? renderItemGrid : renderItemFeed}
|
||||||
{GRID_HOMEPAGE_ENABLED ? renderItemFeed() : renderItemGrid()}
|
{GRID_HOMEPAGE_ENABLED ? renderItemFeed : renderItemGrid}
|
||||||
|
{/* Show spinner if admin is suspected to be logged in */}
|
||||||
|
{(isUserSignedInEager && !isUserSignedIn) &&
|
||||||
|
<SwitcherItem
|
||||||
|
icon={<Spinner />}
|
||||||
|
isInteractive={false}
|
||||||
|
noPadding
|
||||||
|
/>}
|
||||||
{isUserSignedIn &&
|
{isUserSignedIn &&
|
||||||
<AdminAppMenu
|
<SwitcherItem
|
||||||
className="mt-3 ml-[-84px]"
|
icon={<AdminAppMenu
|
||||||
buttonClassName={clsx(
|
className="mt-3 ml-[-94px]"
|
||||||
'w-[40px] h-[28px]',
|
buttonClassName={clsx(
|
||||||
'flex items-center justify-center',
|
'bg-transparent dark:bg-transparent',
|
||||||
'active:bg-transparent',
|
'hover:bg-transparent dark:hover:bg-transparent',
|
||||||
currentSelection === 'admin'
|
'active:bg-transparent dark:active:bg-transparent',
|
||||||
? 'text-black dark:text-white'
|
currentSelection === 'admin'
|
||||||
: 'text-gray-400 dark:text-gray-600',
|
? 'text-black dark:text-white'
|
||||||
)}
|
: 'text-gray-400 dark:text-gray-600',
|
||||||
|
)}
|
||||||
|
/>}
|
||||||
|
noPadding
|
||||||
/>}
|
/>}
|
||||||
</Switcher>
|
</Switcher>
|
||||||
<Switcher type="borderless">
|
<Switcher type="borderless">
|
||||||
|
|||||||
@ -41,6 +41,9 @@ export const signInAction = async (
|
|||||||
redirect(formData.get(KEY_CALLBACK_URL) as string || PATH_ADMIN_PHOTOS);
|
redirect(formData.get(KEY_CALLBACK_URL) as string || PATH_ADMIN_PHOTOS);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const signOutAction = async () =>
|
||||||
|
signOut({ redirect: false });
|
||||||
|
|
||||||
export const signOutAndRedirectAction = async (redirectTo = PATH_SIGN_IN) =>
|
export const signOutAndRedirectAction = async (redirectTo = PATH_SIGN_IN) =>
|
||||||
signOut({ redirectTo });
|
signOut({ redirectTo });
|
||||||
|
|
||||||
|
|||||||
12
src/auth/client.ts
Normal file
12
src/auth/client.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { deleteCookie, getCookie, storeCookie } from '@/utility/cookie';
|
||||||
|
|
||||||
|
const KEY_AUTH_EMAIL = 'authjs.email';
|
||||||
|
|
||||||
|
export const storeAuthEmailCookie = (email: string) =>
|
||||||
|
storeCookie(KEY_AUTH_EMAIL, email);
|
||||||
|
|
||||||
|
export const clearAuthEmailCookie = () =>
|
||||||
|
deleteCookie(KEY_AUTH_EMAIL);
|
||||||
|
|
||||||
|
export const hasAuthEmailCookie = () =>
|
||||||
|
Boolean(getCookie(KEY_AUTH_EMAIL));
|
||||||
@ -11,6 +11,7 @@ export default function SwitcherItem({
|
|||||||
className: classNameProp,
|
className: classNameProp,
|
||||||
onClick,
|
onClick,
|
||||||
active,
|
active,
|
||||||
|
isInteractive = true,
|
||||||
noPadding,
|
noPadding,
|
||||||
prefetch = SHOULD_PREFETCH_ALL_LINKS,
|
prefetch = SHOULD_PREFETCH_ALL_LINKS,
|
||||||
}: {
|
}: {
|
||||||
@ -20,21 +21,24 @@ export default function SwitcherItem({
|
|||||||
className?: string
|
className?: string
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
active?: boolean
|
active?: boolean
|
||||||
|
isInteractive?: boolean
|
||||||
noPadding?: boolean
|
noPadding?: boolean
|
||||||
prefetch?: boolean
|
prefetch?: boolean
|
||||||
}) {
|
}) {
|
||||||
const className = clsx(
|
const className = clsx(
|
||||||
classNameProp,
|
'flex items-center justify-center',
|
||||||
|
'w-[42px] h-full',
|
||||||
'py-0.5 px-1.5',
|
'py-0.5 px-1.5',
|
||||||
'cursor-pointer',
|
isInteractive && 'cursor-pointer',
|
||||||
'hover:bg-gray-100/60 active:bg-gray-100',
|
isInteractive && 'hover:bg-gray-100/60 active:bg-gray-100',
|
||||||
'dark:hover:bg-gray-900/75 dark:active:bg-gray-900',
|
isInteractive && 'dark:hover:bg-gray-900/75 dark:active:bg-gray-900',
|
||||||
active
|
active
|
||||||
? 'text-black dark:text-white'
|
? 'text-black dark:text-white'
|
||||||
: 'text-gray-400 dark:text-gray-600',
|
: 'text-gray-400 dark:text-gray-600',
|
||||||
active
|
active
|
||||||
? 'hover:text-black dark:hover:text-white'
|
? 'hover:text-black dark:hover:text-white'
|
||||||
: 'hover:text-gray-700 dark:hover:text-gray-400',
|
: 'hover:text-gray-700 dark:hover:text-gray-400',
|
||||||
|
classNameProp,
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderIcon = () => noPadding
|
const renderIcon = () => noPadding
|
||||||
@ -54,6 +58,8 @@ export default function SwitcherItem({
|
|||||||
}}>
|
}}>
|
||||||
{renderIcon()}
|
{renderIcon()}
|
||||||
</LinkWithLoader>
|
</LinkWithLoader>
|
||||||
: <div {...{ title, onClick, className }}>{renderIcon()}</div>
|
: <div {...{ title, onClick, className }}>
|
||||||
|
{renderIcon()}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -39,7 +39,7 @@ import { searchPhotosAction } from '@/photo/actions';
|
|||||||
import { RiToolsFill } from 'react-icons/ri';
|
import { RiToolsFill } from 'react-icons/ri';
|
||||||
import { BiLockAlt, BiSolidUser } from 'react-icons/bi';
|
import { BiLockAlt, BiSolidUser } from 'react-icons/bi';
|
||||||
import { HiDocumentText } from 'react-icons/hi';
|
import { HiDocumentText } from 'react-icons/hi';
|
||||||
import { signOutAndRedirectAction } from '@/auth/actions';
|
import { signOutAction } from '@/auth/actions';
|
||||||
import { TbPhoto } from 'react-icons/tb';
|
import { TbPhoto } from 'react-icons/tb';
|
||||||
import { getKeywordsForPhoto, titleForPhoto } from '@/photo';
|
import { getKeywordsForPhoto, titleForPhoto } from '@/photo';
|
||||||
import PhotoDate from '@/photo/PhotoDate';
|
import PhotoDate from '@/photo/PhotoDate';
|
||||||
@ -101,7 +101,7 @@ export default function CommandKClient({
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
isUserSignedIn,
|
isUserSignedIn,
|
||||||
setUserEmail,
|
clearAuthStateAndRedirect,
|
||||||
isCommandKOpen: isOpen,
|
isCommandKOpen: isOpen,
|
||||||
photosCountHidden,
|
photosCountHidden,
|
||||||
uploadsCount,
|
uploadsCount,
|
||||||
@ -400,9 +400,7 @@ export default function CommandKClient({
|
|||||||
}
|
}
|
||||||
adminSection.items.push({
|
adminSection.items.push({
|
||||||
label: 'Sign Out',
|
label: 'Sign Out',
|
||||||
action: () => {
|
action: () => signOutAction().then(clearAuthStateAndRedirect),
|
||||||
signOutAndRedirectAction().then(() => setUserEmail?.(undefined));
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
adminSection.items.push({
|
adminSection.items.push({
|
||||||
|
|||||||
@ -197,7 +197,6 @@ export const formHasTextContent = ({
|
|||||||
// CREATE FORM DATA: FROM PHOTO
|
// CREATE FORM DATA: FROM PHOTO
|
||||||
|
|
||||||
export const convertPhotoToFormData = (photo: Photo): PhotoFormData => {
|
export const convertPhotoToFormData = (photo: Photo): PhotoFormData => {
|
||||||
console.log('convertPhotoToFormData', photo);
|
|
||||||
const valueForKey = (key: keyof Photo, value: any) => {
|
const valueForKey = (key: keyof Photo, value: any) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'tags':
|
case 'tags':
|
||||||
|
|||||||
@ -133,8 +133,6 @@ export const getGitHubPublicFork = async (): Promise<RepoParams> => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getGitHubMeta = async (params: RepoParams) => {
|
export const getGitHubMeta = async (params: RepoParams) => {
|
||||||
console.log('getGitHubMeta', params);
|
|
||||||
|
|
||||||
const urlOwner = getGitHubUrlOwner(params);
|
const urlOwner = getGitHubUrlOwner(params);
|
||||||
const urlRepo = getGitHubUrlRepo(params);
|
const urlRepo = getGitHubUrlRepo(params);
|
||||||
const urlBranch = getGitHubUrlBranch(params);
|
const urlBranch = getGitHubUrlBranch(params);
|
||||||
|
|||||||
@ -20,10 +20,13 @@ export interface AppStateContext {
|
|||||||
setIsCommandKOpen?: Dispatch<SetStateAction<boolean>>
|
setIsCommandKOpen?: Dispatch<SetStateAction<boolean>>
|
||||||
shareModalProps?: ShareModalProps
|
shareModalProps?: ShareModalProps
|
||||||
setShareModalProps?: Dispatch<SetStateAction<ShareModalProps | undefined>>
|
setShareModalProps?: Dispatch<SetStateAction<ShareModalProps | undefined>>
|
||||||
// ADMIN
|
// AUTH
|
||||||
userEmail?: string
|
userEmail?: string
|
||||||
setUserEmail?: Dispatch<SetStateAction<string | undefined>>
|
setUserEmail?: Dispatch<SetStateAction<string | undefined>>
|
||||||
isUserSignedIn?: boolean
|
isUserSignedIn?: boolean
|
||||||
|
isUserSignedInEager?: boolean
|
||||||
|
clearAuthStateAndRedirect?: () => void
|
||||||
|
// ADMIN
|
||||||
adminUpdateTimes?: Date[]
|
adminUpdateTimes?: Date[]
|
||||||
registerAdminUpdate?: () => void
|
registerAdminUpdate?: () => void
|
||||||
photosCount?: number
|
photosCount?: number
|
||||||
|
|||||||
@ -16,6 +16,13 @@ import { ShareModalProps } from '@/share';
|
|||||||
import { storeTimezoneCookie } from '@/utility/timezone';
|
import { storeTimezoneCookie } from '@/utility/timezone';
|
||||||
import { InsightIndicatorStatus } from '@/admin/insights';
|
import { InsightIndicatorStatus } from '@/admin/insights';
|
||||||
import { getAdminDataAction } from '@/admin/actions';
|
import { getAdminDataAction } from '@/admin/actions';
|
||||||
|
import {
|
||||||
|
storeAuthEmailCookie,
|
||||||
|
clearAuthEmailCookie,
|
||||||
|
hasAuthEmailCookie,
|
||||||
|
} from '@/auth/client';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { PATH_SIGN_IN } from '@/app/paths';
|
||||||
|
|
||||||
export default function AppStateProvider({
|
export default function AppStateProvider({
|
||||||
children,
|
children,
|
||||||
@ -24,6 +31,8 @@ export default function AppStateProvider({
|
|||||||
}) {
|
}) {
|
||||||
const { previousPathname } = usePathnames();
|
const { previousPathname } = usePathnames();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
// CORE
|
// CORE
|
||||||
const [hasLoaded, setHasLoaded] =
|
const [hasLoaded, setHasLoaded] =
|
||||||
useState(false);
|
useState(false);
|
||||||
@ -41,6 +50,9 @@ export default function AppStateProvider({
|
|||||||
// ADMIN
|
// ADMIN
|
||||||
const [userEmail, setUserEmail] =
|
const [userEmail, setUserEmail] =
|
||||||
useState<string>();
|
useState<string>();
|
||||||
|
const [isUserSignedInEager, setIsUserSignedInEager] =
|
||||||
|
useState(false);
|
||||||
|
// ADMIN
|
||||||
const [adminUpdateTimes, setAdminUpdateTimes] =
|
const [adminUpdateTimes, setAdminUpdateTimes] =
|
||||||
useState<Date[]>([]);
|
useState<Date[]>([]);
|
||||||
const [photosCount, setPhotosCount] =
|
const [photosCount, setPhotosCount] =
|
||||||
@ -77,6 +89,7 @@ export default function AppStateProvider({
|
|||||||
|
|
||||||
const { data: auth, error: authError } = useSWR('getAuth', getAuthAction);
|
const { data: auth, error: authError } = useSWR('getAuth', getAuthAction);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
setIsUserSignedInEager(hasAuthEmailCookie());
|
||||||
if (!authError) {
|
if (!authError) {
|
||||||
setUserEmail(auth?.user?.email ?? undefined);
|
setUserEmail(auth?.user?.email ?? undefined);
|
||||||
}
|
}
|
||||||
@ -91,7 +104,8 @@ export default function AppStateProvider({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isUserSignedIn) {
|
if (userEmail) {
|
||||||
|
storeAuthEmailCookie(userEmail);
|
||||||
if (adminData) {
|
if (adminData) {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
setPhotosCount(adminData.countPhotos);
|
setPhotosCount(adminData.countPhotos);
|
||||||
@ -105,7 +119,7 @@ export default function AppStateProvider({
|
|||||||
} else {
|
} else {
|
||||||
setPhotosCountHidden(0);
|
setPhotosCountHidden(0);
|
||||||
}
|
}
|
||||||
}, [adminData, adminError, isUserSignedIn]);
|
}, [adminData, adminError, userEmail]);
|
||||||
|
|
||||||
const registerAdminUpdate = useCallback(() =>
|
const registerAdminUpdate = useCallback(() =>
|
||||||
setAdminUpdateTimes(updates => [...updates, new Date()])
|
setAdminUpdateTimes(updates => [...updates, new Date()])
|
||||||
@ -116,6 +130,12 @@ export default function AppStateProvider({
|
|||||||
storeTimezoneCookie();
|
storeTimezoneCookie();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const clearAuthStateAndRedirect = useCallback((shouldRedirect = true) => {
|
||||||
|
setUserEmail(undefined);
|
||||||
|
clearAuthEmailCookie();
|
||||||
|
if (shouldRedirect) { router.push(PATH_SIGN_IN); }
|
||||||
|
}, [router]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppStateContext.Provider
|
<AppStateContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@ -135,10 +155,13 @@ export default function AppStateProvider({
|
|||||||
setIsCommandKOpen,
|
setIsCommandKOpen,
|
||||||
shareModalProps,
|
shareModalProps,
|
||||||
setShareModalProps,
|
setShareModalProps,
|
||||||
// ADMIN
|
// AUTH
|
||||||
userEmail,
|
userEmail,
|
||||||
setUserEmail,
|
setUserEmail,
|
||||||
isUserSignedIn,
|
isUserSignedIn,
|
||||||
|
isUserSignedInEager,
|
||||||
|
clearAuthStateAndRedirect,
|
||||||
|
// ADMIN
|
||||||
adminUpdateTimes,
|
adminUpdateTimes,
|
||||||
registerAdminUpdate,
|
registerAdminUpdate,
|
||||||
photosCount,
|
photosCount,
|
||||||
|
|||||||
@ -5,19 +5,25 @@ export const storeCookie = (
|
|||||||
maxAge = 63158400,
|
maxAge = 63158400,
|
||||||
sameSite = 'Lax',
|
sameSite = 'Lax',
|
||||||
) => {
|
) => {
|
||||||
document.cookie =
|
if (typeof document !== 'undefined') {
|
||||||
`${name}=${value};Path=${path};Max-Age=${maxAge};SameSite=${sameSite}`;
|
document.cookie =
|
||||||
|
`${name}=${value};Path=${path};Max-Age=${maxAge};SameSite=${sameSite}`;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCookie = (name: string) => {
|
export const getCookie = (name: string) => {
|
||||||
const cookie: Record<string, string> = {};
|
if (typeof document !== 'undefined') {
|
||||||
document.cookie.split(';').forEach(function(el) {
|
const cookie: Record<string, string> = {};
|
||||||
const split = el.split('=');
|
document.cookie.split(';').forEach(function(el) {
|
||||||
cookie[split[0].trim()] = split.slice(1).join('=');
|
const split = el.split('=');
|
||||||
});
|
cookie[split[0].trim()] = split.slice(1).join('=');
|
||||||
return cookie[name];
|
});
|
||||||
|
return cookie[name];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteCookie = (name: string) => {
|
export const deleteCookie = (name: string) => {
|
||||||
document.cookie = `${name}=;Max-Age=0`;
|
if (typeof document !== 'undefined') {
|
||||||
|
document.cookie = `${name}=;Max-Age=0`;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user