Skip to content

Commit

Permalink
Login integration
Browse files Browse the repository at this point in the history
  • Loading branch information
barshathakuri authored and AdityaKhatri committed Nov 21, 2024
1 parent 8785c25 commit 6950658
Show file tree
Hide file tree
Showing 15 changed files with 444 additions and 84 deletions.
2 changes: 1 addition & 1 deletion backend
Submodule backend updated 103 files
45 changes: 45 additions & 0 deletions src/App/Auth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
Fragment,
type ReactElement,
} from 'react';
import { Navigate } from 'react-router-dom';

import useAuth from '#hooks/domain/useAuth';

import { type ExtendedProps } from './routes/common';

interface Props {
children: ReactElement,
context: ExtendedProps,
absolutePath: string,
}
function Auth(props: Props) {
const {
context,
children,
absolutePath,
} = props;

const { isAuthenticated } = useAuth();

if (context.visibility === 'is-authenticated' && !isAuthenticated) {
return (
<Navigate to="/login" />
);
}
if (context.visibility === 'is-not-authenticated' && isAuthenticated) {
return (
<Navigate to="/" />
);
}

return (
<Fragment
key={absolutePath}
>
{children}
</Fragment>
);
}

export default Auth;
93 changes: 85 additions & 8 deletions src/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
createBrowserRouter,
RouterProvider,
} from 'react-router-dom';
import {
gql,
useQuery,
} from '@apollo/client';
import {
AlertContext,
AlertContextProps,
Expand All @@ -27,6 +31,11 @@ import mapboxgl from 'mapbox-gl';

import { mapboxToken } from '#config';
import RouteContext from '#contexts/route';
import UserContext, {
UserAuth,
UserContextProps,
} from '#contexts/user';
import { MeQuery } from '#generated/types/graphql';
import { KEY_LANGUAGE_STORAGE } from '#utils/constants';
import {
getFromStorage,
Expand All @@ -35,6 +44,25 @@ import {

import wrappedRoutes, { unwrappedRoutes } from './routes';

import styles from './styles.module.css';

const ME = gql`
query Me {
public {
me {
city
country
displayName
email
firstName
id
lastName
phoneNumber
}
}
}
`;

const router = createBrowserRouter(unwrappedRoutes);
mapboxgl.accessToken = mapboxToken;
mapboxgl.setRTLTextPlugin(
Expand Down Expand Up @@ -66,13 +94,51 @@ function App() {
}, []);

const setAndStoreCurrentLanguage = useCallback(
(newLanugage: Language) => {
setCurrentLanguage(newLanugage);
setToStorage(KEY_LANGUAGE_STORAGE, newLanugage);
(newLanguage: Language) => {
setCurrentLanguage(newLanguage);
setToStorage(KEY_LANGUAGE_STORAGE, newLanguage);
},
[],
);

// AUTH

const [userAuth, setUserAuth] = useState<UserAuth>();

const removeUserAuth = useCallback(() => {
setUserAuth(undefined);
}, []);

// Hydration
useEffect(() => {
const language = getFromStorage<Language>(KEY_LANGUAGE_STORAGE);
setCurrentLanguage(language ?? 'en');
}, []);

const {
loading: meLoading,
} = useQuery<MeQuery>(
ME,
{
onCompleted: (response) => {
if (response.public.me) {
setUserAuth(response.public.me);
} else {
removeUserAuth();
}
},
},
);

const userContextValue = useMemo<UserContextProps>(
() => ({
userAuth,
setUserAuth,
removeUserAuth,
}),
[userAuth, removeUserAuth],
);

const registerLanguageNamespace = useCallback(
(namespace: string, fallbackStrings: Record<string, string>) => {
setStrings(
Expand Down Expand Up @@ -185,13 +251,24 @@ function App() {
removeAlert,
}), [alerts, addAlert, updateAlert, removeAlert]);

if (meLoading) {
return (
// FIXME: Use translation
<div className={styles.loading}>
Checking user session...
</div>
);
}

return (
<RouteContext.Provider value={wrappedRoutes}>
<AlertContext.Provider value={alertContextValue}>
<LanguageContext.Provider value={languageContextValue}>
<RouterProvider router={router} />
</LanguageContext.Provider>
</AlertContext.Provider>
<UserContext.Provider value={userContextValue}>
<AlertContext.Provider value={alertContextValue}>
<LanguageContext.Provider value={languageContextValue}>
<RouterProvider router={router} />
</LanguageContext.Provider>
</AlertContext.Provider>
</UserContext.Provider>
</RouteContext.Provider>
);
}
Expand Down
47 changes: 47 additions & 0 deletions src/App/routes/common.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
type MyInputIndexRouteObject,
type MyInputNonIndexRouteObject,
type MyOutputIndexRouteObject,
type MyOutputNonIndexRouteObject,
wrapRoute,
} from '#utils/routes';
import { Component as RootLayout } from '#views/RootLayout';

import Auth from '../Auth';
import PageError from '../PageError';

export type ExtendedProps = {
title: string,
visibility: 'is-authenticated' | 'is-not-authenticated' | 'anything',
permissions?: (
params: Record<string, number | string | undefined | null> | undefined | null,
) => boolean;
};

interface CustomWrapRoute {
<T>(
myRouteOptions: MyInputIndexRouteObject<T, ExtendedProps>
): MyOutputIndexRouteObject<ExtendedProps>
<T>(
myRouteOptions: MyInputNonIndexRouteObject<T, ExtendedProps>
): MyOutputNonIndexRouteObject<ExtendedProps>
}

export const customWrapRoute: CustomWrapRoute = wrapRoute;

// NOTE: We should not use layout or index routes in links

export const rootLayout = customWrapRoute({
path: '/',
errorElement: <PageError />,
component: {
eagerLoad: true,
render: RootLayout,
props: {},
},
wrapperComponent: Auth,
context: {
title: 'IFRC Alert Hub',
visibility: 'anything',
},
});
40 changes: 21 additions & 19 deletions src/App/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import {
MyOutputIndexRouteObject,
MyOutputNonIndexRouteObject,
unwrapRoute,
wrapRoute,
} from '#utils/routes';
import { Component as RootLayout } from '#views/RootLayout';

import PageError from '../PageError';
import Auth from '../Auth';
import {
customWrapRoute,
rootLayout,
} from './common';

// NOTE: setting default ExtendedProps
export type ExtendedProps = {
Expand All @@ -27,22 +29,6 @@ export interface MyWrapRoute {
): MyOutputNonIndexRouteObject<ExtendedProps>
}

const customWrapRoute: MyWrapRoute = wrapRoute;

const rootLayout = customWrapRoute({
path: '/',
errorElement: <PageError />,
component: {
render: RootLayout,
eagerLoad: true,
props: {},
},
context: {
title: 'IFRC Alert Hub',
visibility: 'anything',
},
});

type DefaultHomeChild = 'map';
const homeLayout = customWrapRoute({
parent: rootLayout,
Expand All @@ -51,6 +37,7 @@ const homeLayout = customWrapRoute({
render: () => import('#views/Home'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'IFRC Alert Hub',
visibility: 'anything',
Expand All @@ -64,6 +51,7 @@ const mySubscription = customWrapRoute({
render: () => import('#views/MySubscription'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'My Subscriptions',
// TODO: Change visibility after login feature
Expand All @@ -82,6 +70,7 @@ const homeIndex = customWrapRoute({
replace: true,
},
},
wrapperComponent: Auth,
context: {
title: 'IFRC Alert Hub',
visibility: 'anything',
Expand All @@ -95,6 +84,7 @@ const homeMap = customWrapRoute({
render: () => import('#views/Home/AlertsMap'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'IFRC Alert Hub - Map',
visibility: 'anything',
Expand All @@ -108,6 +98,7 @@ const homeTable = customWrapRoute({
render: () => import('#views/Home/AlertsTable'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'IFRC Alert Hub - Table',
visibility: 'anything',
Expand All @@ -121,6 +112,7 @@ const preferences = customWrapRoute({
render: () => import('#views/Preferences'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Preferences',
visibility: 'anything',
Expand All @@ -134,6 +126,7 @@ const historicalAlerts = customWrapRoute({
render: () => import('#views/HistoricalAlerts'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Historical Alerts',
visibility: 'anything',
Expand All @@ -147,6 +140,7 @@ const about = customWrapRoute({
render: () => import('#views/About'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'About',
visibility: 'anything',
Expand All @@ -160,6 +154,7 @@ const resources = customWrapRoute({
render: () => import('#views/Resources'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Resources',
visibility: 'anything',
Expand All @@ -173,6 +168,7 @@ const alertDetails = customWrapRoute({
render: () => import('#views/AlertDetails'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Alert Details',
visibility: 'anything',
Expand All @@ -186,6 +182,7 @@ const allSourcesFeeds = customWrapRoute({
render: () => import('#views/AllSourcesFeeds'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Sources Feeds',
visibility: 'anything',
Expand All @@ -199,6 +196,7 @@ const pageNotFound = customWrapRoute({
render: () => import('#views/PageNotFound'),
props: {},
},
wrapperComponent: Auth,
context: {
title: '404',
visibility: 'anything',
Expand All @@ -212,6 +210,7 @@ const login = customWrapRoute({
render: () => import('#views/Login'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Login',
visibility: 'is-not-authenticated',
Expand All @@ -225,6 +224,7 @@ const recoverAccount = customWrapRoute({
render: () => import('#views/RecoverAccount'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Recover Account',
visibility: 'is-not-authenticated',
Expand All @@ -238,6 +238,7 @@ const resendValidationEmail = customWrapRoute({
render: () => import('#views/ResendValidationEmail'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Resend Validation Email',
visibility: 'is-not-authenticated',
Expand All @@ -251,6 +252,7 @@ const cookiePolicy = customWrapRoute({
render: () => import('#views/CookiePolicy'),
props: {},
},
wrapperComponent: Auth,
context: {
title: 'Cookie Policy',
visibility: 'anything',
Expand Down
Loading

0 comments on commit 6950658

Please sign in to comment.