From 2ad4065b981141a85e376306e64f17b297973f57 Mon Sep 17 00:00:00 2001 From: Mathieu Mure Date: Wed, 22 Oct 2025 20:48:31 +0200 Subject: [PATCH] feat(api): migrate to meetup api v2 --- app/evenement/[slug]/page.tsx | 2 +- data/data-override.ts | 74 ++++++++-------- modules/event/components/EventCard.tsx | 8 +- modules/event/components/EventDetail.tsx | 4 +- modules/event/components/EventMarkup.tsx | 89 +++++++++++--------- modules/event/components/EventTile.tsx | 2 +- modules/event/components/Location.tsx | 29 +++---- modules/event/types.ts | 12 ++- modules/meetup/api.ts | 2 +- modules/meetup/queries/event.api.ts | 23 +++-- modules/meetup/queries/next-event.api.ts | 20 +++-- modules/meetup/queries/numbers.api.ts | 45 +++++----- modules/meetup/queries/past-events.api.ts | 20 ++++- modules/meetup/queries/years-with-meetups.ts | 2 +- 14 files changed, 183 insertions(+), 149 deletions(-) diff --git a/app/evenement/[slug]/page.tsx b/app/evenement/[slug]/page.tsx index 74140a55..e58a793e 100644 --- a/app/evenement/[slug]/page.tsx +++ b/app/evenement/[slug]/page.tsx @@ -38,7 +38,7 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str try { const event = overrideEvent(await fetchEvent(eventId)); const title = `LyonJS | ${event.title}`; - const description = `Évènement LyonJS: ${event.shortDescription || event.description.slice(0, 250)}...`; + const description = `Évènement LyonJS: ${event.description.slice(0, 250)}...`; return { title, diff --git a/data/data-override.ts b/data/data-override.ts index e6205183..4b546fd7 100644 --- a/data/data-override.ts +++ b/data/data-override.ts @@ -17,7 +17,7 @@ import { } from './sponsors'; export const dataOverride: { [key: string]: Partial } = { - 'https://www.meetup.com/lyonjs/events/311188398': { + 'https://www.meetup.com/lyonjs/events/311188398/': { sponsor: indy, talks: [ { @@ -36,7 +36,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/310652398': { + 'https://www.meetup.com/lyonjs/events/310652398/': { sponsor: shodo, talks: [ { @@ -63,13 +63,13 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/310653880': { + 'https://www.meetup.com/lyonjs/events/310653880/': { sponsor: adaTechSchool, }, - 'https://www.meetup.com/lyonjs/events/308433948': { + 'https://www.meetup.com/lyonjs/events/308433948/': { sponsor: adaTechSchool, }, - 'https://www.meetup.com/lyonjs/events/308003912': { + 'https://www.meetup.com/lyonjs/events/308003912/': { sponsor: bedrock, talks: [ { @@ -102,7 +102,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/307652646': { + 'https://www.meetup.com/lyonjs/events/307652646/': { sponsor: zenika, talks: [ { @@ -121,7 +121,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/306992187': { + 'https://www.meetup.com/lyonjs/events/306992187/': { sponsor: fulll, talks: [ { @@ -149,7 +149,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/306419959': { + 'https://www.meetup.com/lyonjs/events/306419959/': { sponsor: axopen, talks: [ { @@ -178,7 +178,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/304691839': { + 'https://www.meetup.com/lyonjs/events/304691839/': { sponsor: CBTW, talks: [ { @@ -193,7 +193,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/304182290': { + 'https://www.meetup.com/lyonjs/events/304182290/': { sponsor: theodo, talks: [ { @@ -218,7 +218,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/303395273': { + 'https://www.meetup.com/lyonjs/events/303395273/': { sponsor: shodo, talks: [ { @@ -243,7 +243,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/303049235': { + 'https://www.meetup.com/lyonjs/events/303049235/': { sponsor: indy, talks: [ { @@ -268,7 +268,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/301751075': { + 'https://www.meetup.com/lyonjs/events/301751075/': { sponsor: wildCodeSchool, talks: [ { @@ -287,7 +287,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/301132346': { + 'https://www.meetup.com/lyonjs/events/301132346/': { sponsor: leWagon, talks: [ { @@ -312,7 +312,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/300714737': { + 'https://www.meetup.com/lyonjs/events/300714737/': { sponsor: bedrock, talks: [ { @@ -327,7 +327,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/299916483': { + 'https://www.meetup.com/lyonjs/events/299916483/': { sponsor: wanadev, talks: [ { @@ -352,7 +352,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/299527784': { + 'https://www.meetup.com/lyonjs/events/299527784/': { sponsor: zenika, talks: [ { @@ -377,7 +377,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/298932058': { + 'https://www.meetup.com/lyonjs/events/298932058/': { sponsor: indy, talks: [ { @@ -406,7 +406,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/298162224': { + 'https://www.meetup.com/lyonjs/events/298162224/': { sponsor: zenika, talks: [ { @@ -431,7 +431,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/297675405': { + 'https://www.meetup.com/lyonjs/events/297675405/': { sponsor: indy, talks: [ { @@ -460,7 +460,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/296967782': { + 'https://www.meetup.com/lyonjs/events/296967782/': { talks: [ { title: "Tout ce que vous n'avez jamais voulu savoir sur les fuseaux horaires", @@ -483,7 +483,7 @@ export const dataOverride: { [key: string]: Partial } = { ], sponsor: zenika, }, - 'https://www.meetup.com/lyonjs/events/296202933': { + 'https://www.meetup.com/lyonjs/events/296202933/': { sponsor: indy, talks: [ { @@ -512,7 +512,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/295583450': { + 'https://www.meetup.com/lyonjs/events/295583450/': { talks: [ { title: 'Nuxt 3 - Réussir sa migration', @@ -527,10 +527,10 @@ export const dataOverride: { [key: string]: Partial } = { ], sponsor: zenika, }, - 'https://www.meetup.com/lyonjs/events/293687276': { + 'https://www.meetup.com/lyonjs/events/293687276/': { sponsor: zenika, }, - 'https://www.meetup.com/lyonjs/events/261928293': { + 'https://www.meetup.com/lyonjs/events/261928293/': { talks: [ { title: 'Timeboxed TDD & TCR : Boostez votre Time to Market en dansant le Limbo', @@ -543,7 +543,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/274713264': { + 'https://www.meetup.com/lyonjs/events/274713264/': { sponsor: zenika, talks: [ { @@ -560,7 +560,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/266113861': { + 'https://www.meetup.com/lyonjs/events/266113861/': { sponsor: malt, talks: [ { @@ -577,7 +577,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/292744601': { + 'https://www.meetup.com/lyonjs/events/292744601/': { sponsor: malt, talks: [ { @@ -592,7 +592,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/291958869': { + 'https://www.meetup.com/lyonjs/events/291958869/': { talks: [ { title: 'Playwright 🎭, the Cypress killer by Microsoft', @@ -601,7 +601,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/291728436': { + 'https://www.meetup.com/lyonjs/events/291728436/': { sponsor: indy, talks: [ { @@ -616,7 +616,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/290762638': { + 'https://www.meetup.com/lyonjs/events/290762638/': { sponsor: zenika, talks: [ { @@ -631,7 +631,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/289494397': { + 'https://www.meetup.com/lyonjs/events/289494397/': { sponsor: indy, talks: [ { @@ -644,7 +644,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/289164485': { + 'https://www.meetup.com/lyonjs/events/289164485/': { sponsor: smile, talks: [ { @@ -653,7 +653,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/288153025': { + 'https://www.meetup.com/lyonjs/events/288153025/': { sponsor: indy, talks: [ { @@ -665,7 +665,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/285497869': { + 'https://www.meetup.com/lyonjs/events/285497869/': { sponsor: bedrock, talks: [ { @@ -680,7 +680,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/284851207': { + 'https://www.meetup.com/lyonjs/events/284851207/': { sponsor: indy, talks: [ { @@ -693,7 +693,7 @@ export const dataOverride: { [key: string]: Partial } = { }, ], }, - 'https://www.meetup.com/lyonjs/events/284549533': { + 'https://www.meetup.com/lyonjs/events/284549533/': { sponsor: zenika, talks: [ { diff --git a/modules/event/components/EventCard.tsx b/modules/event/components/EventCard.tsx index a1bd4e39..f285b556 100644 --- a/modules/event/components/EventCard.tsx +++ b/modules/event/components/EventCard.tsx @@ -34,14 +34,16 @@ export const EventCard: FC = ({ event }) => {
{event.title} - {event.venue && } + {event.venues && ( + + )}
{event.sponsor && ( @@ -61,7 +63,7 @@ export const EventCard: FC = ({ event }) => { className={styles.participate} onClick={() => va.track('NextEventRegister', { eventUrl: event.eventUrl })} > - {event.going ? `Rejoindre les ${event.going} participant·e·s` : 'Participez'} + {event.rsvps.yesCount ? `Rejoindre les ${event.rsvps.yesCount} participant·e·s` : 'Participez'} diff --git a/modules/event/components/EventDetail.tsx b/modules/event/components/EventDetail.tsx index af8b952a..828017cb 100644 --- a/modules/event/components/EventDetail.tsx +++ b/modules/event/components/EventDetail.tsx @@ -51,8 +51,8 @@ export const EventDetail: React.FC = async ({ event }) => { {event.description}
- - {event.venue && } + + {event.venues && }
{replays} {images} diff --git a/modules/event/components/EventMarkup.tsx b/modules/event/components/EventMarkup.tsx index 641a0c85..5801d20f 100644 --- a/modules/event/components/EventMarkup.tsx +++ b/modules/event/components/EventMarkup.tsx @@ -5,44 +5,51 @@ import { JsonLD } from '../../seo/JsonLD'; import type { Event } from '../types'; type Props = { event: Event }; -export const EventMarkup: React.FC = ({ event }) => ( - image.source) : [])], - description: event.description, - organizer: ORGANISATION_MARKUP, - }} - /> -); +export const EventMarkup: React.FC = ({ event }) => { + const venue = Array.isArray(event.venues) ? event.venues[0] : event.venues; + return ( + image.source) : []), + ], + description: event.description, + organizer: ORGANISATION_MARKUP, + }} + /> + ); +}; diff --git a/modules/event/components/EventTile.tsx b/modules/event/components/EventTile.tsx index 27c164b9..c40f98d6 100644 --- a/modules/event/components/EventTile.tsx +++ b/modules/event/components/EventTile.tsx @@ -13,7 +13,7 @@ export const EventTile: React.FC = ({ event }) => { return (
- +

{event.title}

{formattedDayAndMonth}
diff --git a/modules/event/components/Location.tsx b/modules/event/components/Location.tsx index 2ccb9644..0e645c49 100644 --- a/modules/event/components/Location.tsx +++ b/modules/event/components/Location.tsx @@ -8,18 +8,17 @@ type Props = { venue: Venue; className?: string; }; -export const Location: FC = ({ venue, className }) => ( - - -
-

{venue.name}

-

{venue.address}

-

{venue.city}

-
-
-); +export const Location: FC = ({ venue, className }) => { + const query = encodeURIComponent(`${venue.name}, ${venue.address}, ${venue.city}, ${venue.country}`); + const mapsUrl = `https://www.google.com/maps/search/?api=1&query=${query}`; + return ( + + +
+

{venue.name}

+

{venue.address}

+

{venue.city}

+
+
+ ); +}; diff --git a/modules/event/types.ts b/modules/event/types.ts index 965dfcc5..d5bcbc06 100644 --- a/modules/event/types.ts +++ b/modules/event/types.ts @@ -26,21 +26,25 @@ export type Venue = { postalCode: string; lat: string; lng: string; + country: string; }; export type Event = { id: string; title: string; - shortDescription: string; description: string; eventUrl: string; dateTime: string; - imageUrl: string; - going: number; + featuredEventPhoto: { + highResUrl: string; + }; + rsvps: { + yesCount: number; + }; group: { id: string; }; - venue?: Venue; + venues: Venue[] | Venue; photoAlbum?: { photoSample: PhotoSample[]; }; diff --git a/modules/meetup/api.ts b/modules/meetup/api.ts index 162994c7..6c45103f 100644 --- a/modules/meetup/api.ts +++ b/modules/meetup/api.ts @@ -1,5 +1,5 @@ import { GraphQLClient } from 'graphql-request'; -export const client = new GraphQLClient('https://www.meetup.com/gql', { fetch }); +export const client = new GraphQLClient('https://www.meetup.com/gql2', { fetch }); export const LYONJS_MEETUP_ID = 18305583; export type Edges = { diff --git a/modules/meetup/queries/event.api.ts b/modules/meetup/queries/event.api.ts index bd95ec88..f4b17cd5 100644 --- a/modules/meetup/queries/event.api.ts +++ b/modules/meetup/queries/event.api.ts @@ -5,26 +5,25 @@ import { client, LYONJS_MEETUP_ID } from '../api'; const query = gql` query meetup($id: ID!) { event(id: $id) { + id title description eventUrl dateTime - imageUrl - photoAlbum { - photoSample(amount: 20) { - source - } + featuredEventPhoto { + highResUrl } - group { - id - } - venue { + venues { name address - city postalCode - lat - lng + country + } + rsvps { + yesCount + } + group { + id } } } diff --git a/modules/meetup/queries/next-event.api.ts b/modules/meetup/queries/next-event.api.ts index 5cf65439..f4d50a6f 100644 --- a/modules/meetup/queries/next-event.api.ts +++ b/modules/meetup/queries/next-event.api.ts @@ -5,14 +5,14 @@ import { client, LYONJS_MEETUP_ID } from '../api'; type NextEventsQueryResponse = { group: { - upcomingEvents: Edges; + events: Edges; }; }; const queryForNextEvents = gql` query meetupEvents($id: ID!) { group(id: $id) { - upcomingEvents(input: {}) { + events { edges { node { id @@ -20,14 +20,18 @@ const queryForNextEvents = gql` description eventUrl dateTime - imageUrl - going - venue { + featuredEventPhoto { + highResUrl + } + venues { name address + postalCode city - lat - lng + country + } + rsvps { + yesCount } } } @@ -38,5 +42,5 @@ const queryForNextEvents = gql` export const fetchNextEvent = async (): Promise => { const response = await client.request(queryForNextEvents, { id: LYONJS_MEETUP_ID }); - return response?.group?.upcomingEvents?.edges?.map((it) => it.node) || null; + return response?.group?.events?.edges?.map((it) => it.node) || null; }; diff --git a/modules/meetup/queries/numbers.api.ts b/modules/meetup/queries/numbers.api.ts index 8f5a06f8..0d2bd8dc 100644 --- a/modules/meetup/queries/numbers.api.ts +++ b/modules/meetup/queries/numbers.api.ts @@ -1,13 +1,30 @@ import { gql } from 'graphql-request'; -import { client, LYONJS_MEETUP_ID } from '../api'; +import { client, type Edges, LYONJS_MEETUP_ID } from '../api'; +import type { Event } from '../../event/types'; const query = gql` - query meetup($id: ID!) { + query meetupEvents($id: ID!) { group(id: $id) { - pastEvents(input: { first: 5000 }) { + events(first: 1000, status: PAST, sort: DESC) { edges { node { - going + id + title + description + eventUrl + dateTime + featuredEventPhoto { + highResUrl + } + venues { + name + address + postalCode + country + } + rsvps { + yesCount + } } } } @@ -15,18 +32,6 @@ const query = gql` } `; -type Node = { - node: { - going: number; - }; -}; - -type PastEvents = { - pastEvents: { - edges: Node[]; - }; -}; - const getAge = (): number => { const today = new Date(); const birthDate = new Date('2011-10-25'); @@ -39,7 +44,9 @@ const getAge = (): number => { }; type Response = { - group: PastEvents; + group: { + events: Edges; + }; }; type Numbers = { @@ -48,10 +55,10 @@ type Numbers = { }; export const fetchNumbers = async (): Promise => { const response = await client.request(query, { id: LYONJS_MEETUP_ID }); - const pastEvents = response.group.pastEvents; + const pastEvents = response.group.events; const numberOfEvents = pastEvents.edges.length + 27; const goingToEventCount = pastEvents.edges.reduce((acc, { node }) => { - acc += node.going; + acc += node.rsvps.yesCount; return acc; }, 0); diff --git a/modules/meetup/queries/past-events.api.ts b/modules/meetup/queries/past-events.api.ts index b2132e40..d1969577 100644 --- a/modules/meetup/queries/past-events.api.ts +++ b/modules/meetup/queries/past-events.api.ts @@ -4,21 +4,33 @@ import { client, Edges, LYONJS_MEETUP_ID } from '../api'; type ResponseType = { group: { - pastEvents: Edges; + events: Edges; }; }; const query = gql` query meetupEvents($id: ID!) { group(id: $id) { - pastEvents(input: { first: 5000 }) { + events(first: 1000, status: PAST, sort: ASC) { edges { node { id title + description eventUrl dateTime - imageUrl + featuredEventPhoto { + highResUrl + } + venues { + name + address + postalCode + country + } + rsvps { + yesCount + } } } } @@ -28,7 +40,7 @@ const query = gql` export const fetchPastEvents = async (): Promise> => { const meetupEventsResponse = await client.request(query, { id: LYONJS_MEETUP_ID }); - const pastEvents = meetupEventsResponse?.group?.pastEvents?.edges.map((it) => it.node).reverse() || []; + const pastEvents = meetupEventsResponse?.group?.events?.edges.map((it) => it.node).reverse() || []; return pastEvents; }; diff --git a/modules/meetup/queries/years-with-meetups.ts b/modules/meetup/queries/years-with-meetups.ts index 97c47cab..188713fc 100644 --- a/modules/meetup/queries/years-with-meetups.ts +++ b/modules/meetup/queries/years-with-meetups.ts @@ -10,7 +10,7 @@ type Response = { const queryForYears = gql` query meetupYears($id: ID!) { group(id: $id) { - pastEvents(input: { first: 5000 }) { + events(first: 1000, status: PAST, sort: ASC) { edges { node { dateTime