Skip to content

Commit af48f06

Browse files
authored
Improvements (#194)
* redirect authenticated users to app when visiting auth pages * prefetch comments * small refactor * update docs
1 parent 701820e commit af48f06

File tree

15 files changed

+93
-66
lines changed

15 files changed

+93
-66
lines changed

apps/react-vite/__mocks__/zustand.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { act } from '@testing-library/react';
2+
import { afterEach, vi } from 'vitest';
23
import * as zustand from 'zustand';
34

45
const { create: actualCreate, createStore: actualCreateStore } =
@@ -18,8 +19,6 @@ const createUncurried = <T>(stateCreator: zustand.StateCreator<T>) => {
1819

1920
// when creating a store, we get its initial state, create a reset function and add it in the set
2021
export const create = (<T>(stateCreator: zustand.StateCreator<T>) => {
21-
console.log('zustand create mock');
22-
2322
// to support curried version of create
2423
return typeof stateCreator === 'function'
2524
? createUncurried(stateCreator)
@@ -37,8 +36,6 @@ const createStoreUncurried = <T>(stateCreator: zustand.StateCreator<T>) => {
3736

3837
// when creating a store, we get its initial state, create a reset function and add it in the set
3938
export const createStore = (<T>(stateCreator: zustand.StateCreator<T>) => {
40-
console.log('zustand createStore mock');
41-
4239
// to support curried version of createStore
4340
return typeof stateCreator === 'function'
4441
? createStoreUncurried(stateCreator)

apps/react-vite/src/app/index.tsx

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,10 @@
1-
import { useQueryClient } from '@tanstack/react-query';
2-
import { useMemo } from 'react';
3-
import { RouterProvider } from 'react-router-dom';
1+
import { AppProvider } from './provider';
2+
import { AppRouter } from './router';
43

5-
import { AppProvider } from './main-provider';
6-
import { createRouter } from './routes';
7-
8-
const AppRouter = () => {
9-
const queryClient = useQueryClient();
10-
11-
const router = useMemo(() => createRouter(queryClient), [queryClient]);
12-
13-
return <RouterProvider router={router} />;
14-
};
15-
16-
function App() {
4+
export const App = () => {
175
return (
186
<AppProvider>
197
<AppRouter />
208
</AppProvider>
219
);
22-
}
23-
24-
export default App;
10+
};

apps/react-vite/src/app/main-provider.tsx renamed to apps/react-vite/src/app/provider.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { QueryClientProvider } from '@tanstack/react-query';
1+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
22
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
33
import * as React from 'react';
44
import { ErrorBoundary } from 'react-error-boundary';
@@ -8,13 +8,20 @@ import { MainErrorFallback } from '@/components/errors/main';
88
import { Notifications } from '@/components/ui/notifications';
99
import { Spinner } from '@/components/ui/spinner';
1010
import { AuthLoader } from '@/lib/auth';
11-
import { queryClient } from '@/lib/react-query';
11+
import { queryConfig } from '@/lib/react-query';
1212

1313
type AppProviderProps = {
1414
children: React.ReactNode;
1515
};
1616

1717
export const AppProvider = ({ children }: AppProviderProps) => {
18+
const [queryClient] = React.useState(
19+
() =>
20+
new QueryClient({
21+
defaultOptions: queryConfig,
22+
}),
23+
);
24+
1825
return (
1926
<React.Suspense
2027
fallback={
Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,35 @@
1-
import { QueryClient } from '@tanstack/react-query';
2-
import { LoaderFunctionArgs, createBrowserRouter } from 'react-router-dom';
1+
import { QueryClient, useQueryClient } from '@tanstack/react-query';
2+
import { useMemo } from 'react';
3+
import {
4+
LoaderFunctionArgs,
5+
RouterProvider,
6+
createBrowserRouter,
7+
} from 'react-router-dom';
38

49
import { ProtectedRoute } from '@/lib/auth';
510

6-
import { AppRoot } from './app/root';
11+
import { AppRoot } from './routes/app/root';
712

8-
export const createRouter = (queryClient: QueryClient) =>
13+
export const createAppRouter = (queryClient: QueryClient) =>
914
createBrowserRouter([
1015
{
1116
path: '/',
1217
lazy: async () => {
13-
const { LandingRoute } = await import('./landing');
18+
const { LandingRoute } = await import('./routes/landing');
1419
return { Component: LandingRoute };
1520
},
1621
},
1722
{
1823
path: '/auth/register',
1924
lazy: async () => {
20-
const { RegisterRoute } = await import('./auth/register');
25+
const { RegisterRoute } = await import('./routes/auth/register');
2126
return { Component: RegisterRoute };
2227
},
2328
},
2429
{
2530
path: '/auth/login',
2631
lazy: async () => {
27-
const { LoginRoute } = await import('./auth/login');
32+
const { LoginRoute } = await import('./routes/auth/login');
2833
return { Component: LoginRoute };
2934
},
3035
},
@@ -40,13 +45,13 @@ export const createRouter = (queryClient: QueryClient) =>
4045
path: 'discussions',
4146
lazy: async () => {
4247
const { DiscussionsRoute } = await import(
43-
'./app/discussions/discussions'
48+
'./routes/app/discussions/discussions'
4449
);
4550
return { Component: DiscussionsRoute };
4651
},
4752
loader: async () => {
4853
const { discussionsLoader } = await import(
49-
'./app/discussions/discussions'
54+
'./routes/app/discussions/discussions'
5055
);
5156
return discussionsLoader(queryClient)();
5257
},
@@ -55,41 +60,41 @@ export const createRouter = (queryClient: QueryClient) =>
5560
path: 'discussions/:discussionId',
5661
lazy: async () => {
5762
const { DiscussionRoute } = await import(
58-
'./app/discussions/discussion'
63+
'./routes/app/discussions/discussion'
5964
);
6065
return { Component: DiscussionRoute };
6166
},
6267

6368
loader: async (args: LoaderFunctionArgs) => {
6469
const { discussionLoader } = await import(
65-
'./app/discussions/discussion'
70+
'./routes/app/discussions/discussion'
6671
);
6772
return discussionLoader(queryClient)(args);
6873
},
6974
},
7075
{
7176
path: 'users',
7277
lazy: async () => {
73-
const { UsersRoute } = await import('./app/users');
78+
const { UsersRoute } = await import('./routes/app/users');
7479
return { Component: UsersRoute };
7580
},
7681

7782
loader: async () => {
78-
const { usersLoader } = await import('./app/users');
83+
const { usersLoader } = await import('./routes/app/users');
7984
return usersLoader(queryClient)();
8085
},
8186
},
8287
{
8388
path: 'profile',
8489
lazy: async () => {
85-
const { ProfileRoute } = await import('./app/profile');
90+
const { ProfileRoute } = await import('./routes/app/profile');
8691
return { Component: ProfileRoute };
8792
},
8893
},
8994
{
9095
path: '',
9196
lazy: async () => {
92-
const { DashboardRoute } = await import('./app/dashboard');
97+
const { DashboardRoute } = await import('./routes/app/dashboard');
9398
return { Component: DashboardRoute };
9499
},
95100
},
@@ -98,8 +103,16 @@ export const createRouter = (queryClient: QueryClient) =>
98103
{
99104
path: '*',
100105
lazy: async () => {
101-
const { NotFoundRoute } = await import('./not-found');
106+
const { NotFoundRoute } = await import('./routes/not-found');
102107
return { Component: NotFoundRoute };
103108
},
104109
},
105110
]);
111+
112+
export const AppRouter = () => {
113+
const queryClient = useQueryClient();
114+
115+
const router = useMemo(() => createAppRouter(queryClient), [queryClient]);
116+
117+
return <RouterProvider router={router} />;
118+
};

apps/react-vite/src/app/routes/app/discussions/discussions.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { QueryClient } from '@tanstack/react-query';
1+
import { QueryClient, useQueryClient } from '@tanstack/react-query';
22

33
import { ContentLayout } from '@/components/layouts';
4+
import { getCommentsQueryOptions } from '@/features/comments/api/get-comments';
45
import { getDiscussionsQueryOptions } from '@/features/discussions/api/get-discussions';
56
import { CreateDiscussion } from '@/features/discussions/components/create-discussion';
67
import { DiscussionsList } from '@/features/discussions/components/discussions-list';
@@ -15,13 +16,19 @@ export const discussionsLoader = (queryClient: QueryClient) => async () => {
1516
};
1617

1718
export const DiscussionsRoute = () => {
19+
const queryClient = useQueryClient();
1820
return (
1921
<ContentLayout title="Discussions">
2022
<div className="flex justify-end">
2123
<CreateDiscussion />
2224
</div>
2325
<div className="mt-4">
24-
<DiscussionsList />
26+
<DiscussionsList
27+
onDiscussionPrefetch={(id) => {
28+
// Prefetch the comments data when the user hovers over the link in the list
29+
queryClient.prefetchQuery(getCommentsQueryOptions(id));
30+
}}
31+
/>
2532
</div>
2633
</ContentLayout>
2734
);
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useNavigate, useSearchParams } from 'react-router-dom';
22

3-
import { Layout } from '@/components/layouts/auth-layout';
3+
import { AuthLayout } from '@/components/layouts/auth-layout';
44
import { LoginForm } from '@/features/auth/components/login-form';
55

66
export const LoginRoute = () => {
@@ -9,14 +9,14 @@ export const LoginRoute = () => {
99
const redirectTo = searchParams.get('redirectTo');
1010

1111
return (
12-
<Layout title="Log in to your account">
12+
<AuthLayout title="Log in to your account">
1313
<LoginForm
1414
onSuccess={() =>
1515
navigate(`${redirectTo ? `${redirectTo}` : '/app'}`, {
1616
replace: true,
1717
})
1818
}
1919
/>
20-
</Layout>
20+
</AuthLayout>
2121
);
2222
};

apps/react-vite/src/app/routes/auth/register.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState } from 'react';
22
import { useNavigate, useSearchParams } from 'react-router-dom';
33

4-
import { Layout } from '@/components/layouts/auth-layout';
4+
import { AuthLayout } from '@/components/layouts/auth-layout';
55
import { RegisterForm } from '@/features/auth/components/register-form';
66
import { useTeams } from '@/features/teams/api/get-teams';
77

@@ -18,7 +18,7 @@ export const RegisterRoute = () => {
1818
});
1919

2020
return (
21-
<Layout title="Register your account">
21+
<AuthLayout title="Register your account">
2222
<RegisterForm
2323
onSuccess={() =>
2424
navigate(`${redirectTo ? `${redirectTo}` : '/app'}`, {
@@ -29,6 +29,6 @@ export const RegisterRoute = () => {
2929
setChooseTeam={() => setChooseTeam(!chooseTeam)}
3030
teams={teamsQuery.data}
3131
/>
32-
</Layout>
32+
</AuthLayout>
3333
);
3434
};

apps/react-vite/src/components/layouts/auth-layout.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
11
import * as React from 'react';
2+
import { useEffect } from 'react';
3+
import { useNavigate } from 'react-router-dom';
24

35
import logo from '@/assets/logo.svg';
46
import { Head } from '@/components/seo';
57
import { Link } from '@/components/ui/link';
8+
import { useUser } from '@/lib/auth';
69

710
type LayoutProps = {
811
children: React.ReactNode;
912
title: string;
1013
};
1114

12-
export const Layout = ({ children, title }: LayoutProps) => {
15+
export const AuthLayout = ({ children, title }: LayoutProps) => {
16+
const user = useUser();
17+
18+
const navigate = useNavigate();
19+
20+
useEffect(() => {
21+
if (user.data) {
22+
navigate('/app', {
23+
replace: true,
24+
});
25+
}
26+
}, [user.data, navigate]);
27+
1328
return (
1429
<>
1530
<Head title={title} />

apps/react-vite/src/features/discussions/components/discussions-list.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ import { useDiscussions } from '../api/get-discussions';
1010

1111
import { DeleteDiscussion } from './delete-discussion';
1212

13-
export const DiscussionsList = () => {
13+
export type DiscussionsListProps = {
14+
onDiscussionPrefetch?: (id: string) => void;
15+
};
16+
17+
export const DiscussionsList = ({
18+
onDiscussionPrefetch,
19+
}: DiscussionsListProps) => {
1420
const discussionsQuery = useDiscussions();
1521
const queryClient = useQueryClient();
1622

@@ -48,6 +54,7 @@ export const DiscussionsList = () => {
4854
onMouseEnter={() => {
4955
// Prefetch the discussion data when the user hovers over the link
5056
queryClient.prefetchQuery(getDiscussionQueryOptions(id));
57+
onDiscussionPrefetch?.(id);
5158
}}
5259
to={`./${id}`}
5360
>

apps/react-vite/src/lib/react-query.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import {
2-
UseMutationOptions,
3-
DefaultOptions,
4-
QueryClient,
5-
} from '@tanstack/react-query';
1+
import { UseMutationOptions, DefaultOptions } from '@tanstack/react-query';
62

73
export const queryConfig = {
84
queries: {
@@ -13,10 +9,6 @@ export const queryConfig = {
139
},
1410
} satisfies DefaultOptions;
1511

16-
export const queryClient = new QueryClient({
17-
defaultOptions: queryConfig,
18-
});
19-
2012
export type ApiFnReturnType<FnType extends (...args: any) => Promise<any>> =
2113
Awaited<ReturnType<FnType>>;
2214

0 commit comments

Comments
 (0)