expo-av has no SDK 56 compatible release, and its EXEventEmitter.h
interface was removed from expo-modules-core, so the iOS release
archive fails compiling Pods/EXAV. Move video playback to
expo-video's useVideoPlayer/VideoView, and audio playback and
recording to expo-audio's createAudioPlayer/AudioModule.AudioRecorder,
preserving existing controls, mute/loop/resize behavior, and the
recording audio session configuration.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Install expo-observe (56.0.29, matching the SDK 56 line) and configure
its endpoint under expo.extra.eas.observe in app.json, alongside the
existing EAS projectId. Leave dispatchInDebug and sampleRate at their
defaults.
Log an app_started custom event once per cold start from the root
layout's existing "run once" startup effect, and install a global JS
error handler that logs xprem_js_crash and then always delegates to
whatever handler was previously registered, so existing Crashlytics
reporting is unaffected.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bump expo, react, react-native, and all expo-* / react-native-* peers to
the SDK 56 line via expo install --fix, adding expo-splash-screen and
expo-system-ui for config plugins the SDK split out of core. Bump
@bacons/apple-targets, react-native-view-shot, and other native modules
to versions compatible with React Native 0.85 and the new architecture.
Remove @react-navigation/* direct dependencies and route their hooks
through expo-router, which is no longer compatible with react-navigation
as of SDK 56. Fix RN 0.85's removed StyleSheet.absoluteFillObject (use
absoluteFill) and react-native-view-shot 5.x's ViewShotRef type across
call sites. Downgrade eslint-plugin-react-hooks v7's new React Compiler
era rules to warnings pending a dedicated refactor, and repair Jest
fallout from the jest-expo/reanimated/worklets/expo-media-library bumps.
Regenerate the committed ios/ and android/ native projects with
`expo prebuild --clean` (required for @bacons/apple-targets 5.x) against
the production release channel, and document that requirement.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the inline ShareableCard in SharePreviewModal with a designed
QuestionShareCard: Sauci logo + wordmark header, pack badge, and a
branded sauci.app footer pill on the captured story image. The card
deliberately never carries the couple join code - it is a private
pairing credential, not a referral link (a regression test enforces
this). Text-share fallbacks now carry a full https://sauci.app link,
and all three share paths fire a new question_shared analytics event
with the destination.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The sender can require photo or audio proof when sending any dare,
catalogue or custom. The recipient captures and uploads the proof as a
new couple-scoped 'dare_proof' media kind and attaches it on submit;
the server enforces ownership, kind, couple, and MIME-type match.
The proof renders inline on the dare card for the sender to review
before confirming completion.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unpaired users could only reach a waiting-room dead end and could not answer
questions until a partner joined. Responses.couple_id is now nullable so a
solo user answers immediately and banks "sealed answers" (couple_id IS NULL).
Pairing claims both members' sealed answers and computes matches for every
question both have now answered. Invite copy and the unpaired push nudge
escalate with the sealed answer count.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both share modals opened the instagram-stories:// scheme without ever
placing the captured PNG on the pasteboard, so Instagram opened an empty
story. Replace the manual scheme launch with react-native-share
shareSingle(INSTAGRAM_STORIES), which writes the image to UIPasteboard
under com.instagram.sharedSticker.backgroundImage on iOS and uses the
ADD_TO_STORY intent on Android. Gate the flow on the required Facebook
App ID (EXPO_PUBLIC_FACEBOOK_APP_ID) and Instagram being installed, with
fallback to the system share sheet, and remove the dead base64 reads.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lets new users skip the onboarding avatar step and instead defers the ask
to a lightweight, dismissible prompt shown once after the couple's first
match. Also personalizes the home screen's first pack recommendation using
the usage_reason already collected during onboarding, which now round-trips
through GET /v1/me.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the notify-me quiz stub with a full loop: quiz sessions per
couple, answer-and-guess flow, dual-direction scoring, and a score-only
share card (no question or answer content) captured via ViewShot with
Sauci branding.
API: migration 0019 (quiz_questions, quiz_sessions, quiz_answers, 12
seeded questions), new quiz domain with session start / current /
answers / result routes, couple isolation, route + integration +
scoring unit tests.
Mobile: src/features/quiz feature (intro, per-question self+guess flow,
waiting-for-partner nudge, results with breakdown), quizStore with
sign-out clearing, QuizShareModal following SharePreviewModal, discovery
QuizTile, analytics events, and tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a shareable https://sauci.app/join/{code} link as an alternative to
manually typing an 8-character invite code:
- apps/web: /join/[code] page that validates the code shape client-side,
attempts the app.sauci:// scheme link, and falls back to a large code
display with copy button plus App Store/Play Store links; PostHog events
for page view, code copy, and store-button clicks.
- apps/web: apple-app-site-association and assetlinks.json under
/.well-known so iOS Universal Links and Android App Links can verify
sauci.app for /join/*. The Android release cert SHA-256 fingerprint is a
placeholder pending the real value.
- apps/mobile: associated domains (iOS) and an intent filter (Android) for
https://sauci.app/join/*, alongside the existing app.sauci scheme.
- apps/mobile: parse incoming join links, pre-fill and highlight the code on
the pairing screen, stash the code for signed-out/mid-onboarding users and
apply it once they reach pairing, and offer a once-per-session clipboard
hand-off that is never auto-applied.
- apps/mobile: share message now leads with the join link, keeping the raw
code visible as a fallback.
- Analytics: invite_link_opened, pairing_code_prefilled (mobile);
join_page_viewed, join_page_code_copied, join_page_store_button_clicked
(web).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Block broad image and video read permissions in Expo config and the checked-in Android manifest. Keep picker access scoped to user-selected media and gallery saves write-only. Add regression coverage and document the release-manifest invariant.
Add a conditional Home cue for partner-answered questions and route both the card and catch-up reminders into the pending answer flow. Track prompt engagement and cover hidden, visible, CTA, and notification behavior.
Measure untouched mobile source, add regression coverage for native media paths and account operations, and enforce ratcheting API and mobile thresholds in required CI checks.
Share bounded database pools across API repositories, consolidate sync and chat queries, and make mobile refreshes cache-aware and foreground-aware.
Add focused regression coverage, client freshness documentation, and repository agent role configuration.
Combine the Android API 36 release, dares loop, RevenueCat documentation correction, and daily engagement features. Resolve migration ordering and preserve both dares expiry and streak reminder operations.
Use reported user timezones for daily response limits, expose couple streak state and reminders, and improve swipe and live-drawing synchronization with focused tests and documentation.
Completes the half-built dares feature: send a dare, respond, report done,
confirm, and get a push notification on every transition.
Backend (apps/api):
- New `dares` domain with couple-shared premium entitlement, a free weekly
send allowance, and server-enforced actor/state rules on every transition.
- `submitted` status so the recipient reports completion and the sender
confirms, rather than the sender guessing.
- Notifications ride the existing operations outbox via a trigger on
`sent_dares`. Push copy is generic; store compliance forbids dare text in a
preview and a test asserts no payload leaks it.
- Worker expiry sweep now covers `pending` as well as `active`/`submitted`;
an invitation nobody opened previously stayed open forever.
Fixes two latent defects:
- Deleting any dare that had been sent aborted with a check violation, because
`dare_id` is ON DELETE SET NULL while `sent_dares_dare_or_custom_check`
required it. Dare text is now snapshotted at send time, which also keeps a
couple's history stable across catalogue edits.
- The free send allowance was raceable: concurrent requests all read the same
count and every one succeeded. The count and insert are now serialized on
the sender's profile row.
Also ports `content_status` review metadata to the API catalogue, which had no
store-safety gating at all, and mirrors the review decision already enforced on
the legacy catalogue.
Mobile: dares screen (active/send/history), send sheet with duration presets,
home tile badging incoming dares, paywall on both proactive and 402 paths.
Verified: lint, typecheck, 379 unit tests, build, 71 integration tests against
local PostgreSQL.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Upgrade the mobile runtime and tracked Android project to compile and target API 36. Align Expo modules, React Native, Node, Gradle, and Android build tooling while preserving the custom widget sources.\n\nAdapt file-system, notification, animation, test, and Metro compatibility for the SDK 54 runtime.
Replace the Supabase application data plane with the Node/PostgreSQL API, migrate mobile, admin, web, and MCP callers, add controlled cutover tooling, and define hardened Dokploy infrastructure.
Remove client-side adult-content preferences and badges so the centrally reviewed catalogue is the single visibility boundary.\n\nUse neutral relationship-focused copy across onboarding, discovery, paywall, and login surfaces, and retain biometric controls under App Lock for both native platforms.
- Add Fastlane setup for uploading AAB to Google Play
- Chain upload into release:android npm script
- Fix onboarding re-renders from authStore subscriptions
- Reduce unnecessary authStore state updates in fetchUser
- Bump Android versionCode to 45
Add automatic push notifications for behind partners (escalating: 24h then
every 3 days) and daily unpaired user reminders. Includes edge functions,
migrations, notification preference toggles, and UI improvements to pairing
and swipe screens.
Adds Sunday weekly recap push notifications summarizing couple match
activity. Includes edge function, migration, notification preference
toggle, and mobile notification handling.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract the slider track, levels, card, and modal into\nfocused subcomponents with shared styles/constants.\nPreserve the existing IntensitySlider API.
Move swipe screen logic into hooks/services and extract
state-specific UI components to keep files small while
preserving existing behavior.
Reference: US-002
- New discovery components (ContentTile, ContentRow, CircularProgress)
- Refactor home screen with compact header and content tiles
- Add hide_nsfw profile setting for content filtering
- Add color column to categories table
- Track pack progress (answered/total questions)
- Update related stores and tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>