Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefetch event schedule data to improve initial load speed #108

Merged
merged 5 commits into from
Apr 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/screens/ExploreScreen/ExploreScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { groupBy } from "../../utils/groupBy"
import { customSortObjectKeys } from "../../utils/customSort"

const recommendationTypes = Object.values(WEBFLOW_MAP.recommendationType)
type RecommendationType = typeof recommendationTypes[number]
type RecommendationType = (typeof recommendationTypes)[number]
joshuayoes marked this conversation as resolved.
Show resolved Hide resolved
type GroupedRecommendations = Record<RecommendationType, RawRecommendations[]>

const initialRecs = recommendationTypes.reduce<GroupedRecommendations>(
Expand Down
2 changes: 1 addition & 1 deletion app/screens/InfoScreen/OurSponsorsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { SponsorCard } from "./SponsorCard"
import { groupBy } from "../../utils/groupBy"

const sponsorTiers = Object.values(WEBFLOW_MAP.sponsorTier)
type Tiers = typeof sponsorTiers[number]
type Tiers = (typeof sponsorTiers)[number]
joshuayoes marked this conversation as resolved.
Show resolved Hide resolved

const initialTiers = sponsorTiers.reduce<Record<Tiers, RawSponsor[]>>(
(acc, tier) => ({ ...acc, [tier]: [] }),
Expand Down
7 changes: 6 additions & 1 deletion app/screens/WelcomeScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from "react"
import React, { useLayoutEffect } from "react"
import { Dimensions, Image, ImageStyle, Platform, TextStyle, View, ViewStyle } from "react-native"
import { SafeAreaView } from "react-native-safe-area-context"
import { Button, Screen, Text } from "../components"
import { useAppNavigation } from "../hooks"
import { AppStackScreenProps } from "../navigators"
import { colors, spacing } from "../theme"
import { prefetchScheduledEvents } from "../services/api"

const welcomeLogo = require("../../assets/images/welcome-shapes.png")
const { width: screenWidth } = Dimensions.get("screen")
Expand All @@ -18,6 +19,10 @@ export const WelcomeScreen: React.FC<WelcomeScreenProps> = (_props) => {
navigation.navigate("Tabs", { screen: "Schedule" })
}

useLayoutEffect(() => {
prefetchScheduledEvents()
}, [])
joshuayoes marked this conversation as resolved.
Show resolved Hide resolved

return (
<Screen style={$container}>
<View style={$topContainer}>
Expand Down
118 changes: 63 additions & 55 deletions app/services/api/webflow-api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useQuery } from "@tanstack/react-query"
import { useQueries, useQuery, UseQueryOptions } from "@tanstack/react-query"
import { Schedule } from "../../screens"
import { axiosInstance, PaginatedItems } from "./axios"
import type {
Expand All @@ -13,6 +13,7 @@ import type {
RawWorkshop,
} from "./webflow-api.types"
import {
CollectionConst,
RECOMMENDATIONS,
RECURRING_EVENTS,
SCHEDULE,
Expand All @@ -31,76 +32,83 @@ import {
cleanedWorkshops,
convertScheduleToScheduleCard,
} from "./webflow-helpers"
import { queryClient } from "./react-query"

const useWebflowAPI = <T>(key: string, collectionId: string, enabled = true) =>
useQuery({
queryKey: [key],
queryFn: async () => {
const { data } = await axiosInstance.get<PaginatedItems<T>>(
`/collections/${collectionId}/items`,
)
return data.items
},
enabled,
})
mazenchami marked this conversation as resolved.
Show resolved Hide resolved

export const useRecommendations = () => {
return useWebflowAPI<RawRecommendations>(RECOMMENDATIONS.key, RECOMMENDATIONS.collectionId)
const getCollectionById = async <T>(collectionId: string) => {
const { data } = await axiosInstance.get<PaginatedItems<T>>(`/collections/${collectionId}/items`)
return data.items
}

export const useRecurringEvents = () => {
return useWebflowAPI<RawRecurringEvents>(RECURRING_EVENTS.key, RECURRING_EVENTS.collectionId)
}
const webflowOptions = <Payload, Collection extends CollectionConst = CollectionConst>(
collection: Collection,
) =>
({
queryKey: [collection.key, collection.collectionId] as const,
queryFn: async () => getCollectionById<Payload>(collection.collectionId),
} satisfies UseQueryOptions)

export const useSpeakers = () => {
return useWebflowAPI<RawSpeaker>(SPEAKERS.key, SPEAKERS.collectionId)
}
const recommendationsOptions = webflowOptions<RawRecommendations>(RECOMMENDATIONS)
export const useRecommendations = () => useQuery(recommendationsOptions)

export const useSpeakerNames = () => {
return useWebflowAPI<RawSpeakerName>(SPEAKER_NAMES.key, SPEAKER_NAMES.collectionId)
}
const recurringEventsOptions = webflowOptions<RawRecurringEvents>(RECURRING_EVENTS)
export const useRecurringEvents = () => useQuery(recurringEventsOptions)

export const useSponsors = (): { isLoading: boolean; data: RawSponsor[] } => {
const { data: sponsors, isLoading } = useWebflowAPI<RawSponsor>(
SPONSORS.key,
SPONSORS.collectionId,
)
const speakersOptions = webflowOptions<RawSpeaker>(SPEAKERS)
export const useSpeakers = () => useQuery(speakersOptions)

const speakerNamesOptions = webflowOptions<RawSpeakerName>(SPEAKER_NAMES)
export const useSpeakerNames = () => useQuery(speakerNamesOptions)

const sponsorsOptions = webflowOptions<RawSponsor>(SPONSORS)
export const useSponsors = () => {
const { data: sponsors, ...rest } = useQuery(sponsorsOptions)
const data = cleanedSponsors(sponsors)

return { isLoading, data }
}
export const useTalks = () => {
return useWebflowAPI<RawTalk>(TALKS.key, TALKS.collectionId)
return { data, ...rest }
}

export const useVenues = () => {
return useWebflowAPI<RawVenue>(VENUES.key, VENUES.collectionId)
}
const talksOptions = webflowOptions<RawTalk>(TALKS)
export const useTalks = () => useQuery(talksOptions)

export const useWorkshops = () => {
return useWebflowAPI<RawWorkshop>(WORKSHOPS.key, WORKSHOPS.collectionId)
}
const venuesOptions = webflowOptions<RawVenue>(VENUES)
export const useVenues = () => useQuery(venuesOptions)

export const useScheduledEvents = () => {
const { data: speakers, isLoading } = useSpeakers()
const { data: workshops } = useWorkshops()
const { data: recurringEvents } = useRecurringEvents()
const { data: talks } = useTalks()
const { data: scheduledEvents, ...rest } = useWebflowAPI<RawScheduledEvent>(
SCHEDULE.key,
SCHEDULE.collectionId,
!isLoading && !!speakers && !!workshops && !!recurringEvents && !!talks,
const workshopsOptions = webflowOptions<RawWorkshop>(WORKSHOPS)
export const useWorkshops = () => useQuery(workshopsOptions)

const scheduledEventsOptions = webflowOptions<RawScheduledEvent>(SCHEDULE)
const scheduledEventsQueries = [
speakersOptions,
workshopsOptions,
recurringEventsOptions,
talksOptions,
scheduledEventsOptions,
] as const
export const prefetchScheduledEvents = async () => {
await Promise.all(
scheduledEventsQueries.map(async (query) => {
return queryClient.prefetchQuery(query)
}),
)
}
export const useScheduledEvents = () => {
const queries = useQueries({
queries: scheduledEventsQueries,
})

const [speakers, workshops, recurringEvents, talks, scheduledEvents] = queries

return {
data: cleanedSchedule({
recurringEvents,
scheduledEvents,
speakers: cleanedSpeakers(speakers),
talks: cleanedTalks({ speakers, talks }),
workshops: cleanedWorkshops(workshops, cleanedSpeakers(speakers)),
recurringEvents: recurringEvents.data,
scheduledEvents: scheduledEvents.data,
speakers: cleanedSpeakers(speakers.data),
talks: cleanedTalks({ speakers: speakers.data, talks: talks.data }),
workshops: cleanedWorkshops(workshops.data, cleanedSpeakers(speakers.data)),
}),
...rest,
refetch: async () => Promise.all(queries.map((query) => query.refetch())),
isLoading: queries.map((query) => query.isLoading).some((isLoading) => isLoading),
isRefetching: queries.map((query) => query.isFetching).some((isFetching) => isFetching),
}
}

Expand All @@ -126,7 +134,7 @@ export const useScheduleScreenData = () => {
title: "Conference Day 2",
events: convertScheduleToScheduleCard(events, "Friday"),
},
] as Schedule[],
] satisfies Schedule[],
refetch,
}
}
Expand Down
28 changes: 20 additions & 8 deletions app/services/api/webflow-consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ export const SITE_ID = "5ca38f35db5d2ea94aea469d"
export const SPONSORS = {
collectionId: "640a728fc24f8e73575fe189",
key: "sponsors",
}
} as const

export const SPEAKERS = {
collectionId: "640a728fc24f8e94385fe188",
key: "speakers",
}
} as const

export const SPEAKER_NAMES = {
collectionId: "640a728fc24f8e74d05fe18a",
Expand All @@ -23,32 +23,44 @@ export const WORKSHOPS = {
export const SCHEDULE = {
collectionId: "640a728fc24f8e63325fe185",
key: "schedule",
}
} as const

export const PAST_TALKS = {
collectionId: "640a728fc24f8e76ef5fe186",
key: "pastTalks",
}
} as const

export const RECURRING_EVENTS = {
collectionId: "640a728fc24f8e85a75fe18c",
key: "recurringEvents",
}
} as const

export const TALKS = {
collectionId: "640a728fc24f8e31ee5fe18e",
key: "talks",
}
} as const

export const VENUES = {
collectionId: "640a728fc24f8e553c5fe18d",
key: "venues",
}
} as const

export const RECOMMENDATIONS = {
collectionId: "640a728fc24f8e083b5fe18f",
key: "recommendations",
}
} as const

export type CollectionConst =
| typeof SPONSORS
| typeof SPEAKERS
| typeof SPEAKER_NAMES
| typeof WORKSHOPS
| typeof SCHEDULE
| typeof PAST_TALKS
| typeof RECURRING_EVENTS
| typeof TALKS
| typeof VENUES
| typeof RECOMMENDATIONS

// [NOTE] these keys probably have to change when webflow is updated
// `/collections/${collectionId}` api will the keys
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
"mocha": "6",
"patch-package": "6.4.7",
"postinstall-prepare": "1.0.1",
"prettier": "2.6.2",
"prettier": "2.8.7",
"query-string": "^7.0.1",
"react-devtools-core": "4.24.7",
"reactotron-core-client": "^2.8.10",
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11621,10 +11621,10 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==

prettier@2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032"
integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==
prettier@2.8.7:
version "2.8.7"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450"
integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==

[email protected]:
version "5.6.0"
Expand Down