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
    No fields configured for issues without a type.

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions