Skip to content

epic(mobile-expo): cross-platform watch app with React Native and Expo #89

@Ur-imazing

Description

@Ur-imazing

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 at apps/cms/schema.graphql. Same Experience and section types as web.
  • App location: Expo app under mobile/expo (alongside mobile/ios and mobile/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) and InfoBlocks (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/expo that 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)

  1. Scaffold with npx create-expo-app (or equivalent); TypeScript; folder structure by feature.
  2. GraphQL: Reuse @forge/graphql if Metro-compatible, or dedicated codegen for Expo from apps/cms/schema.graphql.
  3. 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

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/watch source for layout logic, section ordering, responsive behavior, and interaction patterns (autoplay, accordion, carousel snap, CTA navigation).
  • The forge repo'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 export or 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 content dynamic zones, which can recursively hold other section types. Container slots also have gridSpan for 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

  1. ✅ feat(mobile-expo): scaffold Expo app under mobile/expo (feat(mobile-expo): scaffold Expo app under mobile/expo #90) — CLOSED
  2. ✅ feat(mobile-expo): GraphQL client and codegen for Strapi Experience (feat(mobile-expo): GraphQL client and codegen for Strapi Experience #91) — CLOSED
  3. ✅ feat(mobile-expo): Experience fetch and section data layer (feat(mobile-expo): Experience fetch and section data layer #92) — CLOSED
  4. ✅ 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
  5. 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
  6. 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)
  7. ✅ chore(mobile-expo): Dev/stage/prod config, README, gitignore (chore(mobile-expo): Dev/stage/prod config, README, gitignore #95) — CLOSED
  8. ✅ 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)
  9. 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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions