From 3da1547871af4c7015fcf6ea0a8348c08693d06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Bul=C3=A1nek?= Date: Mon, 4 Nov 2024 14:50:30 +0100 Subject: [PATCH] fix: session redirect bug (#47) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Petr Bulánek --- src/app/(main)/layout.tsx | 14 +++++-- src/app/auth/rsc.tsx | 4 +- src/app/layout.tsx | 7 +--- src/layout/shell/UserProfile.tsx | 4 +- .../user-profile/UserProfileInitializer.tsx | 37 +++++++++++++++++++ src/store/user-profile/index.ts | 9 +++-- src/store/user-profile/types.ts | 4 +- 7 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 src/store/user-profile/UserProfileInitializer.tsx diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 3b2ae25..29bfb6c 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -15,13 +15,19 @@ */ import { ModalProvider } from '@/layout/providers/ModalProvider'; +import { UserProfileInitializer } from '@/store/user-profile/UserProfileInitializer'; import { PropsWithChildren } from 'react'; import { QueryProvider } from '../../layout/providers/QueryProvider'; +import { ensureSession } from '../auth/rsc'; + +export default async function MainLayout({ children }: PropsWithChildren) { + const session = await ensureSession(); -export default function MainLayout({ children }: PropsWithChildren) { return ( - - {children} - + + + {children} + + ); } diff --git a/src/app/auth/rsc.tsx b/src/app/auth/rsc.tsx index d3f549a..c27f621 100644 --- a/src/app/auth/rsc.tsx +++ b/src/app/auth/rsc.tsx @@ -15,7 +15,7 @@ */ import 'server-only'; -import { defaultUserProfileState } from '@/store/user-profile'; +import { dummyUserProfileState } from '@/store/user-profile'; import { JWT } from 'next-auth/jwt'; import { redirect } from 'next/navigation'; import { cache } from 'react'; @@ -33,7 +33,7 @@ export const ensureSession = async () => { user: { access_token: DUMMY_JWT_TOKEN, }, - userProfile: defaultUserProfileState, + userProfile: dummyUserProfileState, }; const session = await getSession(); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 57030ee..06f0065 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -22,7 +22,6 @@ import { StoreProvider } from '@/store/StoreProvider'; import type { Metadata } from 'next'; import { PropsWithChildren, ReactNode } from 'react'; import { IncludeGlobalStyles } from './IncludeGlobalStyles'; -import { ensureSession } from './auth/rsc'; const APP_NAME = process.env.NEXT_PUBLIC_APP_NAME!; @@ -31,12 +30,10 @@ export const metadata: Metadata = { icons: { icon: '//www.ibm.com/favicon.ico' }, }; -export default async function RootLayout({ +export default function RootLayout({ children, modal, }: PropsWithChildren<{ modal: ReactNode }>) { - const session = await ensureSession(); - return ( // suppressHydrationWarning is added because of ThemeProvider @@ -47,7 +44,7 @@ export default async function RootLayout({ - + diff --git a/src/layout/shell/UserProfile.tsx b/src/layout/shell/UserProfile.tsx index aa8b948..a17f932 100644 --- a/src/layout/shell/UserProfile.tsx +++ b/src/layout/shell/UserProfile.tsx @@ -20,7 +20,7 @@ import { ExternalLink } from '@/components/ExternalLink/ExternalLink'; import { Link } from '@/components/Link/Link'; import { CurrentUserAvatar } from '@/components/UserAvatar/UserAvatar'; import { useModal } from '@/layout/providers/ModalProvider'; -import { useUserProfile } from '@/store/user-profile'; +import { dummyUserProfileState, useUserProfile } from '@/store/user-profile'; import { PRIVACY_URL, TOU_TEXT } from '@/utils/constants'; import { isNotNull } from '@/utils/helpers'; import { Button, Popover, PopoverContent } from '@carbon/react'; @@ -45,7 +45,7 @@ export function UserProfile() { const name = useUserProfile((state) => state.name); const email = useUserProfile((state) => state.email); - const isDummyUser = userId === ''; + const isDummyUser = userId === dummyUserProfileState.id; useOnClickOutside(ref, () => { setOpen(false); diff --git a/src/store/user-profile/UserProfileInitializer.tsx b/src/store/user-profile/UserProfileInitializer.tsx new file mode 100644 index 0000000..eb20822 --- /dev/null +++ b/src/store/user-profile/UserProfileInitializer.tsx @@ -0,0 +1,37 @@ +/** + * Copyright 2024 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use client'; + +import { isNotNull } from '@/utils/helpers'; +import { ReactNode } from 'react'; +import { useUserProfile, useUserProfileActions } from '.'; +import { UserProfileState } from './types'; + +export function UserProfileInitializer({ + userProfile, + children, +}: { + userProfile: UserProfileState; + children: ReactNode; +}) { + const userId = useUserProfile((state) => state.id) as string | undefined; + const { setUserProfile } = useUserProfileActions(); + + setUserProfile(userProfile); + + return isNotNull(userId) ? children : null; +} diff --git a/src/store/user-profile/index.ts b/src/store/user-profile/index.ts index ed4c872..215196f 100644 --- a/src/store/user-profile/index.ts +++ b/src/store/user-profile/index.ts @@ -19,20 +19,21 @@ import { useStore } from '..'; import { StoreSelector } from '../types'; import { UserProfileSlice, UserProfileState } from './types'; -export const defaultUserProfileState: UserProfileState = { +export const dummyUserProfileState: UserProfileState = { id: '', name: 'Test User', firstName: 'Test', lastName: 'User', email: 'test@example.com', - metadata: {}, }; export const userProfileSlice = (initialState?: Partial) => lens((set, get) => ({ - ...defaultUserProfileState, + ...({} as UserProfileState), ...initialState, - actions: {}, + actions: { + setUserProfile: (userProfile) => set(userProfile), + }, })); export const useUserProfile: StoreSelector = (selector) => diff --git a/src/store/user-profile/types.ts b/src/store/user-profile/types.ts index 7498559..d51efa9 100644 --- a/src/store/user-profile/types.ts +++ b/src/store/user-profile/types.ts @@ -30,6 +30,8 @@ export type UserProfileState = { metadata?: UserMetadata; }; -export type UserProfileAction = {}; +export type UserProfileAction = { + setUserProfile: (userProfile: UserProfileState) => void; +}; export type UserProfileSlice = StoreSlice;