-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Background
Parent epic for a cross-platform (iOS + Android) watch app using React Native and Expo. Content is fetched from Strapi (this monorepo's CMS) via GraphQL; layout is driven by Experience/sections. All implementation work is tracked in sub-issues linked below.
Context:
- Product target: Watch-style app mirroring jesusfilm.org/watch and experience pages (e.g. Easter, Christmas)—streaming library, featured content, locale-specific experiences with sections.
- CMS: Strapi in
apps/cms; schema atapps/cms/schema.graphql. Same Experience and section types as web. - App location: Expo app under
mobile/expo(alongsidemobile/iosandmobile/android). - GraphQL: Apollo Client (JS/TS) for Strapi; types/operations from shared codegen (
@forge/graphql) or Expo-specific codegen from same schema—to be decided in sub-issue.
Section types (schema-aligned)
The CMS schema defines 10 active section types in the ExperienceSectionsDynamicZone union (see apps/cms/schema.graphql line 693):
| # | Component | Description |
|---|---|---|
| 1 | VideoHero | Hero banner with heading, subheading, linked Video, CTA button |
| 2 | MediaCollection | Collection of video/media items; variants: carousel, collection, grid, hero, player; supports showItemNumbers, linkToSectionKey, footer text |
| 3 | CTA | Call-to-action with heading, body, button (primary/secondary variants) |
| 4 | Text | Rich text block with heading (h1–h6), subtitle, content; variants: default, lead, small |
| 5 | Video | Embedded video player with title, subtitle, streaming URL, linked Video entity |
| 6 | BibleQuotesCarousel | Heading + scrollable quote cards (text, reference, attribution, background image, optional CTA) |
| 7 | RelatedQuestions | Heading + expandable Q&A items (question + answer pairs) |
| 8 | Card | Standalone card with title, description, media, link; variants: default, featured |
| 9 | Section | Wrapper with backgroundColor (dark/default/light/primary), blurHash, and nested content dynamic zone (recursive) |
| 10 | Container | Grid layout with slots; each slot has gridSpan and nested content dynamic zone (recursive) |
Deprecated (do not implement):
PromoBanner(superseded by VideoHero) andInfoBlocks(superseded by Text). These remain in the schema but are stubs in queries (id-only) and should be ignored.
Expected outcome
- A single cross-platform (iOS + Android) React Native app under
mobile/expothat builds and runs with Expo. - App fetches Experience(s) from Strapi via GraphQL and renders server-driven UI (section-based layout).
- Watch home and experience-by-slug with locale; visual parity goal with jesusfilm.org/watch and experience pages.
- All work tracked in sub-issues linked to this epic; repo workflow (issue first, branch, PR, conventional commits) and CI (lint, typecheck, build) applied.
Acceptance criteria
- All sub-issues created and linked (dependency order below updated with issue numbers).
- Expo app under
mobile/expo; runs on iOS and Android. - App fetches Experience(s) and renders section-based UI for all 10 active section types (VideoHero, MediaCollection, CTA, Text, Video, BibleQuotesCarousel, RelatedQuestions, Card, Section, Container).
- Nested content rendering works for Section and Container wrappers.
- Watch home and at least one experience-by-slug with locale.
- Lint/CI pass; dev/stage/prod config and README; tests as agreed.
Possible solution(s)
- Scaffold with
npx create-expo-app(or equivalent); TypeScript; folder structure by feature. - GraphQL: Reuse
@forge/graphqlif Metro-compatible, or dedicated codegen for Expo fromapps/cms/schema.graphql. - Section renderers: RN components per section type; data layer maps API response to section models. Reference the iOS native app's GraphQL query (
mobile/ios/GraphQL/Operations/GetWatchExperience.graphql) which already handles all 10 types.
References
- AGENTS.md, mobile/AGENTS.md
- apps/cms/schema.graphql —
ExperienceSectionsDynamicZoneunion (line 693) - mobile/ios/GraphQL/Operations/GetWatchExperience.graphql — iOS query covering all 10 types
- apps/web/src/lib/content.ts (web watch experience consumption)
- Sample Experience: documentId
lr6luew6oh4hurag4n8s0ddz(slug:easter) — mocks first two sections of the Easter page
Source reference (JesusFilm/core)
The original production Easter page lives in JesusFilm/core — the existing Next.js monorepo that powers jesusfilm.org. Use this as the ground-truth reference when building and testing features.
| What | Link | Notes |
|---|---|---|
| Core repo | github.com/JesusFilm/core | Production monorepo (Next.js, Apollo, i18n) |
| Watch app | apps/watch | Next.js app serving /watch routes |
| Easter page | apps/watch/pages/easter.html | Page component, data fetching, section rendering |
| Live page | jesusfilm.org/watch/easter.html/english.html | Production reference — compare visual output against this |
How to use for testing:
- Compare each section renderer's output (VideoHero, MediaCollection, Text, Video, BibleQuotesCarousel, RelatedQuestions, CTA, Card) against the live Easter page.
- Check
apps/watchsource for layout logic, section ordering, responsive behavior, and interaction patterns (autoplay, accordion, carousel snap, CTA navigation). - The
forgerepo's Strapi CMS replicates the same content structure — section types and fields should map 1:1.
Foundations (Expo/RN)
- Stack: TypeScript, React Native, Expo SDK (latest stable).
- Structure: Organize by feature; single responsibility.
- Testing: Unit tests for data layer and critical components; optional e2e later.
- Environments: Dev / stage / prod config (e.g. GraphQL URL).
- CI: Lint, typecheck, build (e.g.
expo exportor EAS); follow repo workflow.
Server-driven UI and content model
- Data flow: App → Apollo Client → Strapi GraphQL → Experience (sections). Response drives which sections and data to render.
- Section types (10 active): VideoHero, MediaCollection, CTA, Text, Video, BibleQuotesCarousel, RelatedQuestions, Card, Section (wrapper), Container (grid layout).
- Nested content: Section and Container types contain their own
contentdynamic zones, which can recursively hold other section types. Container slots also havegridSpanfor layout. - Routing: Watch home (homepage experience) and experience-by-slug (e.g.
easter,christmas) with locale.
Progress summary
Completed (6 of 9 sub-issues)
| # | Issue | Status | PR |
|---|---|---|---|
| 1 | #90 — Scaffold Expo app under mobile/expo |
✅ Closed | Merged |
| 2 | #91 — GraphQL client and codegen for Strapi Experience | ✅ Closed | Merged |
| 3 | #92 — Experience fetch and section data layer | ✅ Closed | Merged |
| 4 | #304 — Expand data layer for all 10 CMS section types | ✅ Closed | Merged |
| 7 | #95 — Dev/stage/prod config, README, gitignore | ✅ Closed | Merged |
| 8 | #96 — CI (lint, typecheck, build) for Expo app | ✅ Done | #318 (CI passing) |
In progress / Next up (3 of 9 sub-issues)
| # | Issue | Status | Blocked by |
|---|---|---|---|
| 5 | #93 — Section renderers for all 10 section types | 🔜 In progress | #304 ✅ (started with #305) |
| 6 | #94 — Watch home and experience-by-slug + locale | ⏳ Blocked | #93 |
| 9 | #97 — Unit tests for data layer and critical components | ⏳ Blocked | #304 ✅, #93 |
Current focus: #93 → in progress with #305 (SectionDispatcher scaffold)
Dependency order
- ✅ feat(mobile-expo): scaffold Expo app under mobile/expo (feat(mobile-expo): scaffold Expo app under mobile/expo #90) — CLOSED
- ✅ feat(mobile-expo): GraphQL client and codegen for Strapi Experience (feat(mobile-expo): GraphQL client and codegen for Strapi Experience #91) — CLOSED
- ✅ feat(mobile-expo): Experience fetch and section data layer (feat(mobile-expo): Experience fetch and section data layer #92) — CLOSED
- ✅ feat(mobile-expo): expand data layer for all 10 CMS section types (feat(mobile-expo): expand data layer for all 10 CMS section types #304 — depends on feat(mobile-expo): Experience fetch and section data layer #92) — CLOSED
- feat(mobile-expo): section renderers for all 10 section types (feat(mobile-expo): section renderers for all 10 CMS section types #93 — depends on feat(mobile-expo): expand data layer for all 10 CMS section types #304) — IN PROGRESS
- feat(mobile-expo): Watch home and experience-by-slug + locale (feat(mobile-expo): Watch home and experience-by-slug + locale #94 — depends on feat(mobile-expo): section renderers for all 10 CMS section types #93)
- ✅ chore(mobile-expo): Dev/stage/prod config, README, gitignore (chore(mobile-expo): Dev/stage/prod config, README, gitignore #95) — CLOSED
- ✅ chore(mobile-expo): CI (lint, typecheck, build) for Expo app (chore(mobile-expo): CI (lint, typecheck, build) for Expo app #96 — depends on feat(mobile-expo): scaffold Expo app under mobile/expo #90) — DONE (chore(mobile-expo): CI lint, typecheck, and build for Expo app #318)
- chore(mobile-expo): Unit tests for data layer and critical components (chore(mobile-expo): Unit tests for data layer and critical components #97 — depends on feat(mobile-expo): expand data layer for all 10 CMS section types #304, feat(mobile-expo): section renderers for all 10 CMS section types #93)
Out of scope
- Duplicating native app logic (Swift/Kotlin); changing Strapi schema; web-specific or native-only tooling.
- Implementing deprecated PromoBanner or InfoBlocks section types.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status