all repos — caroster @ d8b50fcea8125de6998c9781d73e18b9372aff9e

[Octree] Group carpool to your event https://caroster.io

fix: :bug: Use cookie for banner visbility

#361
Tim Izzo tim@octree.ch
Tue, 27 Sep 2022 11:24:29 +0200
commit

d8b50fcea8125de6998c9781d73e18b9372aff9e

parent

8a3e8f58e43a20afa617f25756e5a3de0b2d0300

M frontend/components/Banner/index.tsxfrontend/components/Banner/index.tsx

@@ -1,28 +1,36 @@

import {Icon} from '@material-ui/core'; import Button from '@material-ui/core/Button'; import {makeStyles} from '@material-ui/core/styles'; +import {useState} from 'react'; +import {setCookie} from '../../lib/cookies'; import Markdown from '../Markdown'; +const ANNOUNCEMENT_COOKIE = 'lastAnnouncementSeen'; + interface Props { - message: string; - open: boolean; - onClear?: () => void; + announcement?: string; } const Banner = (props: Props) => { - const {message, open, onClear} = props; + const {announcement} = props; const classes = useStyles(); + const [showBanner, setShowBanner] = useState(!!announcement); - if (!open) return null; + const onBannerClear = () => { + setCookie(ANNOUNCEMENT_COOKIE, `${announcement}`); + setShowBanner(false); + }; + + if (!showBanner) return null; return ( <div className={classes.banner}> - <Markdown className={classes.htmlReset}>{message}</Markdown> + <Markdown className={classes.htmlReset}>{announcement}</Markdown> <Button className={classes.clear} onClick={e => { e.stopPropagation(); - onClear(); + onBannerClear(); }} > <Icon>close</Icon>
M frontend/components/Markdown/index.tsxfrontend/components/Markdown/index.tsx

@@ -1,5 +1,5 @@

-import { styled } from '@material-ui/core/styles'; -import Typography, { TypographyProps } from '@material-ui/core/Typography'; +import {styled} from '@material-ui/core/styles'; +import Typography, {TypographyProps} from '@material-ui/core/Typography'; import {marked} from 'marked'; const Markdown = (props: TypographyProps) => {

@@ -7,10 +7,14 @@ const {children, ...typographyProps} = props;

if (!children) return null; + const htmlContent = marked(children).replace(/\<\/?p\>/g, ''); + return ( <Text {...typographyProps} - dangerouslySetInnerHTML={{__html: marked(children)}} + dangerouslySetInnerHTML={{ + __html: htmlContent, + }} /> ); };
M frontend/hooks/useSettings.tsfrontend/hooks/useSettings.ts

@@ -6,7 +6,7 @@

const useSettings = () => { const {locale} = useLocale(); const {data = defaulData} = useSettingQuery({variables: {locale}}); - return data?.setting?.data?.attributes; + return data?.setting?.data?.attributes || {}; }; export default useSettings;
M frontend/layouts/Default.tsxfrontend/layouts/Default.tsx

@@ -1,15 +1,12 @@

-import {ReactNode, useState} from 'react'; +import {ReactNode} from 'react'; import {Helmet} from 'react-helmet'; import useGTM from '../hooks/useGTM'; import GenericToolbar from '../containers/GenericToolbar'; import {ActionType} from '../containers/GenericMenu/Action'; import Box from '@material-ui/core/Box'; import Banner from '../components/Banner'; -import useSettings from '../hooks/useSettings'; import useMatomo from '../hooks/useMatomo'; -const ANNOUNCEMENT_STORAGE_KEY = 'lastAnnouncementSeen'; - interface Props { children: ReactNode; Topbar?: ReactNode;

@@ -19,6 +16,7 @@ menuActions?: Array<ActionType>;

pageTitle?: string; displayMenu?: boolean; goBack?: () => void; + announcement?: string; } const DefaultLayout = (props: Props) => {

@@ -33,17 +31,8 @@ displayMenu = true,

menuTitle = 'Caroster', menuActions, goBack = null, + announcement, } = props; - const {announcement = ''} = useSettings() || {}; - const [lastAnnouncementSeen, setLastAnnouncementSeen] = useState( - getStoredValue(ANNOUNCEMENT_STORAGE_KEY) - ); - const showBanner = !!announcement && announcement !== lastAnnouncementSeen; - - const onBannerClear = () => { - setStoredValue(ANNOUNCEMENT_STORAGE_KEY, `${announcement}`); - setLastAnnouncementSeen(announcement); - }; return ( <div className={className}>

@@ -52,11 +41,7 @@ <title>{pageTitle || menuTitle}</title>

</Helmet> <Box display="flex" flexDirection="column" height="100vh" width="100%"> <Box position="sticky" top={0} zIndex={1100}> - <Banner - message={announcement} - open={showBanner} - onClear={onBannerClear} - /> + <Banner announcement={announcement} /> {Topbar && <Topbar />} </Box> {displayMenu && (menuTitle || menuActions) && (

@@ -72,12 +57,5 @@ </Box>

</div> ); }; - -const getStoredValue = (storageKey: string) => - typeof localStorage !== 'undefined' ? localStorage.getItem(storageKey) : ''; - -const setStoredValue = (storageKey: string, value: string) => - typeof localStorage !== 'undefined' && - localStorage.setItem(storageKey, value); export default DefaultLayout;
M frontend/layouts/Event.tsxfrontend/layouts/Event.tsx

@@ -23,7 +23,7 @@ Tab: TabComponent;

} const EventLayout = (props: PropsWithChildren<Props>) => { - const {eventUUID, Tab} = props; + const {eventUUID, Tab, ...pageProps} = props; const {t} = useTranslation(); const theme = useTheme(); const classes = useStyles();

@@ -49,6 +49,7 @@ pageTitle={t('event.title', {title: event.name})}

menuTitle={t('event.title', {title: event.name})} displayMenu={false} Topbar={() => <EventBar event={event} onAdd={setIsAddToMyEvent} />} + {...pageProps} > <Box flex={1}
A frontend/lib/cookies.ts

@@ -0,0 +1,26 @@

+export const getCookie = (cname: string, cookieHeader?: string) => { + const cookieString = + typeof document === 'undefined' ? cookieHeader : document.cookie; + let name = cname + '='; + let decodedCookie = decodeURIComponent(cookieString || ''); + let ca = decodedCookie.split(';'); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return ''; +}; + +export const setCookie = (cname: string, cvalue: string, exdays = 30) => { + if (typeof document !== 'undefined') { + const d = new Date(); + d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000); + let expires = 'expires=' + d.toUTCString(); + document.cookie = cname + '=' + cvalue + ';' + expires + ';path=/'; + } +};
M frontend/lib/pageUtils.tsfrontend/lib/pageUtils.ts

@@ -2,6 +2,7 @@ import {ApolloClient} from '@apollo/client';

import {getSession} from 'next-auth/react'; import {ProfileDocument, SettingDocument} from '../generated/graphql'; import {initializeApollo, APOLLO_STATE_PROP_NAME} from './apolloClient'; +import {getCookie} from './cookies'; type ServerSideExtension = ( context: any,

@@ -12,17 +13,24 @@ const getServerSideProps =

(extension?: ServerSideExtension) => async (context: any) => { const session = await getSession(context); const {STRAPI_URL = 'http://localhost:1337'} = process.env; - const apolloClient = await initializeApollo( - `${STRAPI_URL}/graphql`, - session - ); + const apolloClient = initializeApollo(`${STRAPI_URL}/graphql`, session); const locale = session?.user?.lang || 'fr'; try { - await apolloClient.query({ + const { + data: {setting = {}}, + } = await apolloClient.query({ query: SettingDocument, variables: {locale}, }); + let announcement = setting?.data?.attributes?.announcement || ''; + const lastAnnouncementSeen = getCookie( + 'lastAnnouncementSeen', + context.req.headers.cookie + ); + + if (!announcement || announcement === lastAnnouncementSeen) + announcement = null; if (session) await apolloClient.query({

@@ -36,6 +44,7 @@

return { props: { session, + announcement, [APOLLO_STATE_PROP_NAME]: apolloClient.cache.extract(), ...extensionProps, },
M frontend/pages/dashboard.tsxfrontend/pages/dashboard.tsx

@@ -11,7 +11,11 @@ import Fab from '../containers/Fab';

import pageUtils from '../lib/pageUtils'; import {getSession} from 'next-auth/react'; -const Dashboard = () => { +interface PageProps { + announcement?: string; +} + +const Dashboard = (props: PageProps) => { const {t} = useTranslation(); const router = useRouter(); const {profile, isReady} = useProfile();

@@ -55,13 +59,17 @@ ];

if (!events || !isReady) return ( - <LayoutDefault menuTitle={t('dashboard.title')}> + <LayoutDefault menuTitle={t('dashboard.title')} {...props}> <Loading /> </LayoutDefault> ); return ( - <LayoutDefault menuActions={menuActions} menuTitle={t('dashboard.title')}> + <LayoutDefault + menuActions={menuActions} + menuTitle={t('dashboard.title')} + {...props} + > {events.length === 0 ? ( <DashboardEmpty /> ) : (
M frontend/pages/e/[uuid]/details.tsxfrontend/pages/e/[uuid]/details.tsx

@@ -24,6 +24,7 @@ import pageUtils from '../../../lib/pageUtils';

interface Props { eventUUID: string; + announcement?: string; } const Page = (props: PropsWithChildren<Props>) => {
M frontend/pages/e/[uuid]/index.tsxfrontend/pages/e/[uuid]/index.tsx

@@ -16,6 +16,7 @@ import {getSession, useSession} from 'next-auth/react';

interface Props { eventUUID: string; + announcement?: string; } const Page = (props: PropsWithChildren<Props>) => {
M frontend/pages/e/[uuid]/waitingList.tsxfrontend/pages/e/[uuid]/waitingList.tsx

@@ -12,6 +12,7 @@ }

interface Props { eventUUID: string; + announcement?: string; } const Page = (props: PropsWithChildren<Props>) => {
M frontend/pages/index.tsxfrontend/pages/index.tsx

@@ -10,7 +10,11 @@ import pageUtils from '../lib/pageUtils';

import {useEffect} from 'react'; import useRedirectUrlStore from '../stores/useRedirectUrl'; -const Home = () => { +interface PageProps { + announcement?: string; +} + +const Home = (props: PageProps) => { const {t} = useTranslation(); const router = useRouter(); const session = useSession();

@@ -58,6 +62,7 @@ <Layout

menuTitle={t('event.creation.title')} menuActions={menuActions} displayMenu={isAuthenticated} + {...props} > <Paper> <Logo />
M frontend/pages/profile.tsxfrontend/pages/profile.tsx

@@ -7,7 +7,11 @@ import {useSession, signOut, getSession} from 'next-auth/react';

import pageUtils from '../lib/pageUtils'; import useProfile from '../hooks/useProfile'; -const ProfilePage = () => { +interface PageProps { + announcement?: string; +} + +const ProfilePage = (props: PageProps) => { const router = useRouter(); const {t} = useTranslation(); const session = useSession();

@@ -29,7 +33,12 @@

if (session.status === 'loading') return <Loading />; return ( - <Layout menuTitle={t('profile.title')} menuActions={menuActions} goBack> + <Layout + menuTitle={t('profile.title')} + menuActions={menuActions} + goBack + {...props} + > {profile && <Profile profile={profile} logout={signOut} />} </Layout> );