all repos — caroster @ 1eff8c695a32beae4a1bd07c45c45ec05f27a0f4

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

🔍 Set dynamic meta
Tim Izzo tim@octree.ch
Tue, 22 Jun 2021 15:57:57 +0200
commit

1eff8c695a32beae4a1bd07c45c45ec05f27a0f4

parent

1469af7c7664fa1069a420382188dbca6499f78d

A frontend/containers/Metas/index.tsx

@@ -0,0 +1,54 @@

+import Head from 'next/head'; + +type Metas = { + title: string; + url: string; +}; + +interface Props { + metas: Metas; +} + +const Meta = (props: Props) => { + const {metas} = props; + + const siteName = 'Caroster - Covoiturage de groupe'; + const title = metas?.title + ? `${metas.title} - Caroster` + : 'Caroster - Covoiturage de groupe'; + const description = + 'Covoiturez à un événement en proposant une voiture ou en prenant une place.'; + const socialImage = '/assets/Caroster_Octree_Social.jpg'; + + return ( + <Head> + {/* General */} + <title>{title}</title> + <meta itemProp="name" content={siteName} /> + {metas?.url && <meta itemProp="url" content={metas.url} />} + <meta itemProp="thumbnailUrl" content={socialImage} /> + <link rel="image_src" href={socialImage} /> + <meta itemProp="image" content={socialImage} /> + <meta name="description" content={description} /> + + {/* OpenGraph */} + <meta property="og:site_name" content="Caroster" /> + <meta property="og:title" content={title} /> + {metas?.url && <meta property="og:url" content={metas.url} />} + <meta property="og:type" content="website" /> + <meta property="og:description" content={description} /> + <meta property="og:image" content={socialImage} /> + <meta property="og:image:width" content="1500" /> + <meta property="og:image:height" content="843" /> + + {/* Twitter */} + <meta name="twitter:title" content={title} /> + <meta name="twitter:image" content={socialImage} /> + {metas?.url && <meta name="twitter:url" content={metas.url} />} + <meta name="twitter:card" content="summary" /> + <meta name="twitter:description" content={description} /> + </Head> + ); +}; + +export default Meta;
M frontend/containers/WaitingList/index.jsfrontend/containers/WaitingList/index.js

@@ -17,12 +17,12 @@ useUpdateEventMutation,

useUpdateCarMutation, } from '../../generated/graphql'; -const WaitingList = ({car}) => { +const WaitingList = () => { const classes = useStyles(); const {t} = useTranslation(); const event = useEventStore(s => s.event); const addToast = useToastStore(s => s.addToast); - const addToEvent = useAddToEvents(); + const {addToEvent} = useAddToEvents(); const [isEditing, toggleEditing] = useReducer(i => !i, false); const [removing, setRemoving] = useState(null); const [adding, setAdding] = useState(null);
M frontend/generated/graphql.tsxfrontend/generated/graphql.tsx

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

export type LoginMutationVariables = Exact<{ identifier: Scalars['String']; password: Scalars['String']; - provider?: Maybe<Scalars['String']>; }>;

@@ -1661,7 +1660,7 @@ )> }

)> } ); -export type EventFielsFragment = ( +export type EventFieldsFragment = ( { __typename?: 'Event' } & Pick<Event, 'id' | 'name' | 'email' | 'date' | 'address' | 'position' | 'waiting_list'> & { cars?: Maybe<Array<Maybe<(

@@ -1684,7 +1683,7 @@ & { createEvent?: Maybe<(

{ __typename?: 'createEventPayload' } & { event?: Maybe<( { __typename?: 'Event' } - & EventFielsFragment + & EventFieldsFragment )> } )> } );

@@ -1701,7 +1700,7 @@ & { updateEvent?: Maybe<(

{ __typename?: 'updateEventPayload' } & { event?: Maybe<( { __typename?: 'Event' } - & EventFielsFragment + & EventFieldsFragment )> } )> } );

@@ -1715,7 +1714,7 @@ export type EventQuery = (

{ __typename?: 'Query' } & { event?: Maybe<( { __typename?: 'Event' } - & EventFielsFragment + & EventFieldsFragment )> } );

@@ -1794,8 +1793,8 @@ name

} } `; -export const EventFielsFragmentDoc = gql` - fragment EventFiels on Event { +export const EventFieldsFragmentDoc = gql` + fragment EventFields on Event { id name email

@@ -1869,10 +1868,8 @@ export type RegisterMutationHookResult = ReturnType<typeof useRegisterMutation>;

export type RegisterMutationResult = Apollo.MutationResult<RegisterMutation>; export type RegisterMutationOptions = Apollo.BaseMutationOptions<RegisterMutation, RegisterMutationVariables>; export const LoginDocument = gql` - mutation login($identifier: String!, $password: String!, $provider: String = "local") { - login( - input: {identifier: $identifier, password: $password, provider: $provider} - ) { + mutation login($identifier: String!, $password: String!) { + login(input: {identifier: $identifier, password: $password}) { jwt user { ...MeFields

@@ -1897,7 +1894,6 @@ * const [loginMutation, { data, loading, error }] = useLoginMutation({

* variables: { * identifier: // value for 'identifier' * password: // value for 'password' - * provider: // value for 'provider' * }, * }); */

@@ -2090,11 +2086,11 @@ createEvent(

input: {data: {name: $name, email: $email, date: $date, address: $address}} ) { event { - ...EventFiels + ...EventFields } } } - ${EventFielsFragmentDoc}`; + ${EventFieldsFragmentDoc}`; export type CreateEventMutationFn = Apollo.MutationFunction<CreateEventMutation, CreateEventMutationVariables>; /**

@@ -2127,11 +2123,11 @@ export const UpdateEventDocument = gql`

mutation updateEvent($id: ID!, $eventUpdate: editEventInput) { updateEvent(input: {where: {id: $id}, data: $eventUpdate}) { event { - ...EventFiels + ...EventFields } } } - ${EventFielsFragmentDoc}`; + ${EventFieldsFragmentDoc}`; export type UpdateEventMutationFn = Apollo.MutationFunction<UpdateEventMutation, UpdateEventMutationVariables>; /**

@@ -2161,10 +2157,10 @@ export type UpdateEventMutationOptions = Apollo.BaseMutationOptions<UpdateEventMutation, UpdateEventMutationVariables>;

export const EventDocument = gql` query event($id: ID!) { event(id: $id) { - ...EventFiels + ...EventFields } } - ${EventFielsFragmentDoc}`; + ${EventFieldsFragmentDoc}`; /** * __useEventQuery__
M frontend/graphql/event.gqlfrontend/graphql/event.gql

@@ -1,4 +1,4 @@

-fragment EventFiels on Event { +fragment EventFields on Event { id name email

@@ -28,7 +28,7 @@ createEvent(

input: {data: {name: $name, email: $email, date: $date, address: $address}} ) { event { - ...EventFiels + ...EventFields } } }

@@ -36,13 +36,13 @@

mutation updateEvent($id: ID!, $eventUpdate: editEventInput) { updateEvent(input: {where: {id: $id}, data: $eventUpdate}) { event { - ...EventFiels + ...EventFields } } } query event($id: ID!) { event(id: $id) { - ...EventFiels + ...EventFields } }
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -23,13 +23,14 @@ "rejected": "Quelque chose c'est mal passé",

"bad_data": "Il manque quelque chose", "unauthorized": "Problème d'authentification", "forbidden": "Vous n'avez pas le droit de faire cette action", - "not_found": "Resource introuvable", + "not_found": "Ressource introuvable", "server": "Problème sur nos serveurs", "timeout": "Connexion instable, re-essayé plus tard" } }, "event": { "title": "{{title}} - Caroster", + "not_found": "Projet introuvable", "fields": { "starts_on": "Commence le", "address": "Adresse",
M frontend/pages/_app.tsxfrontend/pages/_app.tsx

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

import {useEffect, Fragment} from 'react'; -import Head from 'next/head'; import {AppProps} from 'next/app'; import {ThemeProvider} from '@material-ui/core/styles'; import {ApolloProvider} from '@apollo/client';

@@ -9,6 +8,7 @@ import theme from '../theme';

import Toasts from '../components/Toasts'; import 'moment/locale/fr-ch'; import '../i18n'; +import Metas from '../containers/Metas'; const App = function (props: AppProps) { const {Component, pageProps} = props;

@@ -23,13 +23,7 @@

return ( <ApolloProvider client={apolloClient}> <Fragment> - <Head> - <title>{pageProps.meta?.title || 'Caroster'}</title> - <meta - name="viewport" - content="minimum-scale=1, initial-scale=1, width=device-width" - /> - </Head> + <Metas metas={pageProps.metas} /> <ThemeProvider theme={theme}> <CssBaseline /> <Component {...pageProps} />
M frontend/pages/_document.tsxfrontend/pages/_document.tsx

@@ -8,6 +8,10 @@ render() {

return ( <Html lang="fr"> <Head> + <meta + name="viewport" + content="minimum-scale=1, initial-scale=1, width=device-width" + /> <meta name="theme-color" content={theme.palette.primary.main} /> <meta name="application-name" content="Caroster" /> <link rel="shortcut icon" href="/assets/favicon.ico" />

@@ -55,49 +59,6 @@ href="/assets/favicon-16x16.png"

/> <link rel="manifest" href="/manifest.json" /> <link rel="mask-icon" href="/assets/logo.svg" color="#5bbad5" /> - <meta property="og:site_name" content="Caroster" /> - <meta - property="og:title" - content="Caroster - Covoiturage de groupe" - /> - <meta property="og:url" content="%PUBLIC_URL%" /> - <meta property="og:type" content="website" /> - <meta - property="og:description" - content="Covoiturez à un événement en proposant une voiture ou en prenant une place." - /> - <meta - property="og:image" - content="/assets/Caroster_Octree_Social.jpg" - /> - <meta property="og:image:width" content="1500" /> - <meta property="og:image:height" content="843" /> - <meta itemProp="name" content="Caroster - Covoiturage de groupe" /> - <meta itemProp="url" content="%PUBLIC_URL%" /> - <meta - itemProp="thumbnailUrl" - content="/assets/Caroster_Octree_Social.jpg" - /> - <link rel="image_src" href="/assets/Caroster_Octree_Social.jpg" /> - <meta itemProp="image" content="/assets/Caroster_Octree_Social.jpg" /> - <meta - name="twitter:title" - content="Caroster - Covoiturage de groupe" - /> - <meta - name="twitter:image" - content="/assets/Caroster_Octree_Social.jpg" - /> - <meta name="twitter:url" content="%PUBLIC_URL%" /> - <meta name="twitter:card" content="summary" /> - <meta - name="twitter:description" - content="Covoiturez à un événement en proposant une voiture ou en prenant une place." - /> - <meta - name="description" - content="Covoiturez à un événement en proposant une voiture ou en prenant une place." - /> </Head> <body> <Main />
A frontend/pages/_error.tsx

@@ -0,0 +1,29 @@

+import {useTranslation} from 'react-i18next'; +import Box from '@material-ui/core/Box'; +import Typography from '@material-ui/core/Typography'; + +interface Props { + statusCode: number; + title?: string; +} + +const NotFoundPage = (props: Props) => { + const {t} = useTranslation(); + const {statusCode = 404, title = t`generic.errors.not_found`} = props; + + return ( + <Box + display="flex" + justifyContent="center" + alignItems="center" + width="100vw" + height="100vh" + > + <Typography variant="overline"> + {statusCode} - {title} + </Typography> + </Box> + ); +}; + +export default NotFoundPage;
M frontend/pages/e/[eventId].tsxfrontend/pages/e/[eventId].tsx

@@ -1,27 +1,39 @@

import {useState, useReducer, useEffect} from 'react'; import {useTranslation} from 'react-i18next'; -import {useRouter} from 'next/router'; import Layout from '../../layouts/Default'; import Fab from '../../containers/Fab'; import CarColumns from '../../containers/CarColumns'; import NewCarDialog from '../../containers/NewCarDialog'; import AddToMyEventDialog from '../../containers/AddToMyEventDialog'; -import Loading from '../../containers/Loading'; import EventBar from '../../containers/EventBar'; import useToastStore from '../../stores/useToastStore'; import {initializeApollo} from '../../lib/apolloClient'; +import ErrorPage from '../_error'; import { useUpdateEventMutation, Event as EventType, + useEventQuery, EventDocument, } from '../../generated/graphql'; import useEventStore from '../../stores/useEventStore'; +import Loading from '../../containers/Loading'; interface Props { event: EventType; + eventId: string; } -const Event = ({event}: Props) => { +const EventPage = props => { + const {event} = props; + const {t} = useTranslation(); + + if (!event) return <ErrorPage statusCode={404} title={t`event.not_found`} />; + + return <Event {...props} />; +}; + +const Event = (props: Props) => { + const {eventId} = props; const {t} = useTranslation(); const addToast = useToastStore(s => s.addToast); const setEvent = useEventStore(s => s.setEvent);

@@ -30,6 +42,9 @@ const setIsEditing = useEventStore(s => s.setIsEditing);

const [updateEvent] = useUpdateEventMutation(); const [isAddToMyEvent, setIsAddToMyEvent] = useState(false); const [openNewCar, toggleNewCar] = useReducer(i => !i, false); + const {data: {event} = {}, loading} = useEventQuery({ + variables: {id: eventId}, + }); useEffect(() => { if (event) setEvent(event as EventType);

@@ -64,6 +79,8 @@ return true;

} }; + if (!event) return <Loading />; + return ( <Layout pageTitle={t('event.title', {title: event.name})}

@@ -88,30 +105,26 @@ </Layout>

); }; -export async function getServerSideProps(context) { - const {eventId} = context.query; +export async function getServerSideProps(ctx) { + const {eventId} = ctx.query; const apolloClient = initializeApollo(); const {data = {}} = await apolloClient.query({ query: EventDocument, variables: {id: eventId}, }); const {event} = data; + const {host = ''} = ctx.req.headers; return { props: { event, - meta: { - title: event?.name, + eventId, + metas: { + title: event?.name || '', + url: `https://${host}${ctx.resolvedUrl}`, }, }, }; } -export default props => { - const router = useRouter(); - const {eventId} = router.query; - - if (!eventId) return null; - - return <Event {...props} eventId={eventId} />; -}; +export default EventPage;
M frontend/public/sw.js.mapfrontend/public/sw.js.map

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

-{"version":3,"file":"sw.js","sources":["../../../../../tmp/e1e9f9c52437340949d00fb32f332e2f/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from '/home/tim/Projets/caroster/frontend/node_modules/workbox-routing/registerRoute.mjs';\nimport {NetworkFirst as workbox_strategies_NetworkFirst} from '/home/tim/Projets/caroster/frontend/node_modules/workbox-strategies/NetworkFirst.mjs';\nimport {NetworkOnly as workbox_strategies_NetworkOnly} from '/home/tim/Projets/caroster/frontend/node_modules/workbox-strategies/NetworkOnly.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from '/home/tim/Projets/caroster/frontend/node_modules/workbox-core/clientsClaim.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\nimportScripts(\n \n);\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n\nworkbox_routing_registerRoute(\"/\", new workbox_strategies_NetworkFirst({ \"cacheName\":\"start-url\", plugins: [{ cacheWillUpdate: async ({request, response, event, state}) => { if (response && response.type === 'opaqueredirect') { return new Response(response.body, {status: 200, statusText: 'OK', headers: response.headers}); } return response; } }] }), 'GET');\nworkbox_routing_registerRoute(/.*/i, new workbox_strategies_NetworkOnly({ \"cacheName\":\"dev\", plugins: [] }), 'GET');\n\n\n\n\n"],"names":["importScripts","self","skipWaiting","workbox_core_clientsClaim","workbox_routing_registerRoute","workbox_strategies_NetworkFirst","plugins","cacheWillUpdate","request","response","event","state","type","Response","body","status","statusText","headers","workbox_strategies_NetworkOnly"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGyI;EACzI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAGAA,aAAa;EAUbC,IAAI,CAACC,WAAL;AAEAC,sBAAyB;AAIzBC,uBAA6B,CAAC,GAAD,EAAM,IAAIC,oBAAJ,CAAoC;EAAE,eAAY,WAAd;EAA2BC,EAAAA,OAAO,EAAE,CAAC;EAAEC,IAAAA,eAAe,EAAE,OAAO;EAACC,MAAAA,OAAD;EAAUC,MAAAA,QAAV;EAAoBC,MAAAA,KAApB;EAA2BC,MAAAA;EAA3B,KAAP,KAA6C;EAAE,UAAIF,QAAQ,IAAIA,QAAQ,CAACG,IAAT,KAAkB,gBAAlC,EAAoD;EAAE,eAAO,IAAIC,QAAJ,CAAaJ,QAAQ,CAACK,IAAtB,EAA4B;EAACC,UAAAA,MAAM,EAAE,GAAT;EAAcC,UAAAA,UAAU,EAAE,IAA1B;EAAgCC,UAAAA,OAAO,EAAER,QAAQ,CAACQ;EAAlD,SAA5B,CAAP;EAAiG;;EAAC,aAAOR,QAAP;EAAkB;EAA5O,GAAD;EAApC,CAApC,CAAN,EAAmU,KAAnU,CAA7B;AACAL,uBAA6B,CAAC,KAAD,EAAQ,IAAIc,mBAAJ,CAAmC;EAAE,eAAY,KAAd;EAAqBZ,EAAAA,OAAO,EAAE;EAA9B,CAAnC,CAAR,EAAgF,KAAhF,CAA7B;;"}+{"version":3,"file":"sw.js","sources":["../../../../../tmp/49b8229bab823f5e5b2c7e7fc1045a1b/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from '/home/tim/Projets/caroster/frontend/node_modules/workbox-routing/registerRoute.mjs';\nimport {NetworkFirst as workbox_strategies_NetworkFirst} from '/home/tim/Projets/caroster/frontend/node_modules/workbox-strategies/NetworkFirst.mjs';\nimport {NetworkOnly as workbox_strategies_NetworkOnly} from '/home/tim/Projets/caroster/frontend/node_modules/workbox-strategies/NetworkOnly.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from '/home/tim/Projets/caroster/frontend/node_modules/workbox-core/clientsClaim.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\nimportScripts(\n \n);\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n\nworkbox_routing_registerRoute(\"/\", new workbox_strategies_NetworkFirst({ \"cacheName\":\"start-url\", plugins: [{ cacheWillUpdate: async ({request, response, event, state}) => { if (response && response.type === 'opaqueredirect') { return new Response(response.body, {status: 200, statusText: 'OK', headers: response.headers}); } return response; } }] }), 'GET');\nworkbox_routing_registerRoute(/.*/i, new workbox_strategies_NetworkOnly({ \"cacheName\":\"dev\", plugins: [] }), 'GET');\n\n\n\n\n"],"names":["importScripts","self","skipWaiting","workbox_core_clientsClaim","workbox_routing_registerRoute","workbox_strategies_NetworkFirst","plugins","cacheWillUpdate","request","response","event","state","type","Response","body","status","statusText","headers","workbox_strategies_NetworkOnly"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGyI;EACzI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAGAA,aAAa;EAUbC,IAAI,CAACC,WAAL;AAEAC,sBAAyB;AAIzBC,uBAA6B,CAAC,GAAD,EAAM,IAAIC,oBAAJ,CAAoC;EAAE,eAAY,WAAd;EAA2BC,EAAAA,OAAO,EAAE,CAAC;EAAEC,IAAAA,eAAe,EAAE,OAAO;EAACC,MAAAA,OAAD;EAAUC,MAAAA,QAAV;EAAoBC,MAAAA,KAApB;EAA2BC,MAAAA;EAA3B,KAAP,KAA6C;EAAE,UAAIF,QAAQ,IAAIA,QAAQ,CAACG,IAAT,KAAkB,gBAAlC,EAAoD;EAAE,eAAO,IAAIC,QAAJ,CAAaJ,QAAQ,CAACK,IAAtB,EAA4B;EAACC,UAAAA,MAAM,EAAE,GAAT;EAAcC,UAAAA,UAAU,EAAE,IAA1B;EAAgCC,UAAAA,OAAO,EAAER,QAAQ,CAACQ;EAAlD,SAA5B,CAAP;EAAiG;;EAAC,aAAOR,QAAP;EAAkB;EAA5O,GAAD;EAApC,CAApC,CAAN,EAAmU,KAAnU,CAA7B;AACAL,uBAA6B,CAAC,KAAD,EAAQ,IAAIc,mBAAJ,CAAmC;EAAE,eAAY,KAAd;EAAqBZ,EAAAA,OAAO,EAAE;EAA9B,CAAnC,CAAR,EAAgF,KAAhF,CAA7B;;"}