Closer/README.md

350 lines
15 KiB
Markdown

# Closer
> *Private daily questions for couples who want honest answers before shared conversations.*
**Product goal:** private, mutual-reveal relationship questions with real encryption and calmer UX.
Closer is a **couples relationship app** built for partners who want a steadier way to talk, reflect, and make time for each other. It turns relationship check-ins into small, intentional rituals — one daily question, curated conversation packs, private answers, partner reveal flows, gentle reminders, and shared date planning.
The app is **not** a social network, **not** a therapy replacement, and **not** a productivity tracker. There are no public feeds, no likes, no followers, and no infinite scroll. The core loop is simple: **answer honestly, choose what to reveal, and keep a record of the conversations that mattered.**
Closer is built as native apps on **Android (Kotlin / Jetpack Compose)** and **iOS (SwiftUI)**, both backed by the same Firebase stack and shared data model.
---
## Pricing Philosophy
> *No shady trials. No cancellation friction. One subscription per couple, not per person.*
Subscription apps for couples have a trust problem — confusing trial wording, hard-to-cancel flows, and partners getting double-billed. Closer treats pricing the same way it treats relationships: **clear, straightforward, and built on honesty.**
| Principle | How Closer Handles It |
| --- | --- |
| Free tier | Generous by default. Daily questions, basic packs, recent history, spin wheel — all free, no time limit. |
| One sub per couple | One premium purchase unlocks it for both partners. No second billing. |
| Trial wording | No auto-renewal trials that silently convert. If there's a free period, you'll know exactly when and how it ends. |
| Cancellation | Clear, actionable instructions in-app and in docs. No hidden unsubscribe paths. |
| Restore | Works with one tap. If it breaks, support fixes it — no escalation maze. |
---
## Screenshots
<p>
<img src="docs/screenshots/01-onboarding.png" alt="Closer onboarding screen" width="180" />
<img src="docs/screenshots/03-home.png" alt="Closer home dashboard" width="180" />
<img src="docs/screenshots/04-daily-question.png" alt="Closer daily question answer screen" width="180" />
<img src="docs/screenshots/05-question-packs.png" alt="Closer question pack library" width="180" />
</p>
<p>
<img src="docs/screenshots/06-answer-history.png" alt="Closer answer history" width="180" />
<img src="docs/screenshots/08-play.png" alt="Closer play hub" width="180" />
<img src="docs/screenshots/09-date-builder.png" alt="Closer date planning screen" width="180" />
<img src="docs/screenshots/07-settings.png" alt="Closer settings screen" width="180" />
</p>
<p>
<img src="docs/screenshots/02-login.png" alt="Closer login screen" width="180" />
</p>
---
## What Closer Does
Closer gives couples a private, shared space for guided connection. Every feature should support
the product goal: **private, mutual-reveal relationship questions with real encryption and calmer UX**.
- **Daily question** — one prompt a day, with written, scale, multiple-choice, and this-or-that answer modes.
- **Private-first answers** — each partner answers independently before deciding whether to reveal or discuss.
- **Question packs** — 22+ curated categories spanning 6,000+ bundled prompts (communication, conflict, trust, intimacy, parenting, marriage, money, stress, date night, and more).
- **Answer history** — review past questions and answers, delete controls, and partner reveal support.
- **Discussion threads** — question-specific conversation threads and reactions for follow-up.
- **Partner pairing** — 6-character invite code with copy and share, and partner-aware home states.
- **Spin the wheel** — category-based random questions for date nights, road trips, and low-pressure moments.
- **Date tools** — swipe-to-match date ideas, date planning preferences, and a shared bucket list.
- **Settings & privacy** — account, notifications, appearance, subscription, relationship, privacy, and account deletion flows.
- **Subscriptions** — free and premium tiers powered by RevenueCat (Google Play Billing on Android, StoreKit on iOS).
---
## Product Shape
Closer is optimized for **short, meaningful sessions** rather than endless engagement.
| Area | Behavior |
| --- | --- |
| Home | Surfaces the next best action — daily question, partner activity, saved answers |
| Questions | Local-bundled content so the app stays fast and usable without waiting on the network |
| Partner Sync | Firebase Auth + Firestore isolate user, couple, invite, thread, and entitlement data per couple |
| Reminders | FCM + local notification preferences and quiet-hour controls |
| Premium | Unlocks deeper packs, full history, Desire Sync, select Connection Challenges, Memory Lane, and analytics — **one subscription for both partners** |
---
## Platform Status
| Platform | Stack | Status |
| --- | --- | --- |
| **Android** | Kotlin · Jetpack Compose · Material 3 · Hilt · Room · DataStore | **Active development** — feature-complete MVP |
| **iOS** | SwiftUI · MVVM · async/await · Firebase iOS SDK | **In progress** — full scaffold + screen parity landed on `dev` branch |
| **Backend** | Firebase Auth · Firestore · Cloud Functions (TypeScript) · FCM · App Check | **Shared source of truth** for both platforms |
| **Billing** | RevenueCat · Google Play Billing · StoreKit | Server-verified entitlements via Cloud Function webhook |
The Android app is the **reference implementation** — the iOS port is built to mirror it screen-for-screen and consume the same backend.
---
## MVP Feature Scope
### Free Tier — No Time Limit, No Hidden Cap
All of this is free, forever. No credits, no daily limits that magically shrink after a week.
- Anonymous onboarding → email or Google sign-up
- 6-character invite code pairing (copy or share via any app)
- Daily question with full answer modes (text, scale, multiple choice, this-or-that)
- Private answer reveal flow once both partners have answered
- Question packs — 6,000+ bundled prompts across 22 categories (most packs included free)
- Spin wheel — category-based random questions for date nights
- Recent answer history (last 30 days)
- Push reminders with quiet-hour support
- Account deletion and data export
### Premium Tier (Per-Couple, Not Per-Person)
One purchase unlocks premium for both partners. No separate subscriptions.
- **Unlimited daily questions** with premium-only question packs
- **Full answer history** — search, filter, export the complete timeline
- **Saved spin wheel session history** — revisit what landed
- **Desire Sync** — preferences alignment exercise for tough conversations
- **Select Connection Challenges** — premium multi-day programs; free challenges available to all users
- **Memory Lane** — time capsules with locked prompts that open on future dates
- **Advanced analytics and relationship insights**
- **Priority support**
### Out of Scope (Future)
- AI-assisted question suggestions
- Native group/relationship types beyond dyadic couples
- Wearable (Wear OS / watchOS) companions
- Live video or voice sessions
---
## Tech Stack
### Android
| Layer | Stack |
| --- | --- |
| Language | Kotlin 2.x |
| UI | Jetpack Compose, Material 3, Navigation Compose |
| Architecture | Clean architecture — `core/` · `data/` · `domain/` · `ui/` |
| State | ViewModel · Kotlin Coroutines · Kotlin Flow |
| Dependency Injection | Hilt |
| Local Data | Room · DataStore Preferences · bundled SQLite seed |
| SDK | min 26 · target 35 · compile 35 |
### iOS
| Layer | Stack |
| --- | --- |
| Language | Swift 5.9 |
| UI | SwiftUI · NavigationStack · TabView |
| Architecture | MVVM · `AppState` ObservableObject · `EnvironmentObject` |
| Concurrency | async/await |
| Dependency Management | Swift Package Manager · XcodeGen (`project.yml`) |
| SDK | iOS 17+ |
### Backend (Shared)
| Layer | Stack |
| --- | --- |
| Auth | Firebase Authentication (anonymous, email/password, Google) |
| Database | Cloud Firestore |
| Server Logic | Firebase Cloud Functions (TypeScript) |
| Push | Firebase Cloud Messaging (FCM) |
| Security | Firebase App Check · Play Integrity (Android) · DeviceCheck (iOS) |
| Billing | RevenueCat (server-verified entitlements) |
| Analytics | Firebase Analytics · Crashlytics |
---
## Repository Layout
```text
.
├── app/ # Native Android app (Kotlin)
│ └── src/main/java/app/closer
│ ├── core/ # Firebase, analytics, billing, navigation, notifications, security
│ ├── data/ # Room, Firestore data sources, repositories, seed parsing
│ ├── domain/ # Models and repository contracts
│ └── ui/ # Compose screens and feature ViewModels
├── iphone/ # Native iOS app (SwiftUI)
│ ├── ARCHITECTURE_AUDIT.md # iOS port blueprint (49 screens, schema, models)
│ ├── project.yml # XcodeGen project spec
│ ├── Package.swift # SPM dependency manifest
│ └── Closer/
│ ├── Models/ # Firestore + domain codable types
│ ├── Core/ # Auth, Billing, Notifications
│ ├── Services/ # FirestoreService (callable wrappers)
│ ├── Theme/ # CloserTheme (colors, typography, spacing)
│ ├── Components/ # Shared SwiftUI components
│ ├── Navigation/ # Root ContentView + TabView routing
│ ├── Onboarding/ # Onboarding, login, signup, profile creation
│ ├── Pairing/ # Invite code, partner confirm, recovery
│ ├── Home/ # Home dashboard, streak, partner mirror
│ ├── Questions/ # Daily question, answer reveal, history, packs
│ ├── Play/ # Play hub + games (ThisOrThat, HowWell, DesireSync)
│ ├── Wheel/ # Spin wheel with animated 8-slice picker
│ ├── Dates/ # Date swipe, matches, builder, bucket list
│ └── Settings/ # Settings, paywall, help, data export
├── functions/ # Firebase Cloud Functions (TypeScript)
│ └── src/
│ ├── auth/ # Auth flow + invite lifecycle
│ ├── billing/ # RevenueCat webhook + entitlement sync
│ ├── couples/ # Pairing, leave, daily question triggers
│ ├── questions/ # onAnswerWritten, onMessageWritten, threads
│ └── server/ # Internal Express webhook service (not client-facing)
├── server/ # Optional Express webhook/health service
├── seed/ # Question pack JSON and local DB generation
├── docs/ # QA notes, release prep, roadmap, screenshots
└── firestore.rules # Firestore security rules
```
---
## Getting Started
### Prerequisites
- **Android:** Android Studio · Android SDK · JDK 17
- **iOS:** Xcode 16 · macOS · [XcodeGen](https://github.com/yonaskolb/XcodeGen) (`brew install xcodegen`)
- **Firebase:** Project with Auth, Firestore, Cloud Messaging, Crashlytics, Analytics, App Check
- **Android config:** `app/google-services.json`
- **iOS config:** `iphone/Closer/GoogleService-Info.plist`
- **Billing:** RevenueCat project with Android and iOS API keys
- **Node 20** for Firebase Functions tooling
### Local config
```bash
# Android
cp local.properties.example local.properties
# iOS
cp iphone/Closer/GoogleService-Info.plist.example iphone/Closer/GoogleService-Info.plist
```
Add local-only values such as:
```properties
sdk.dir=/path/to/Android/Sdk
RC_API_KEY_ANDROID=your_revenuecat_android_key
RC_API_KEY_IOS=your_revenuecat_ios_key
```
### Android
```bash
./gradlew :app:assembleDebug
./gradlew :app:installDebug
```
Useful verification command:
```bash
./gradlew :app:compileDebugKotlin
```
### iOS
```bash
cd iphone
xcodegen generate
xed Closer.xcodeproj
```
Build from the command line:
```bash
xcodebuild -project iphone/Closer.xcodeproj \
-scheme Closer \
-destination 'platform=iOS Simulator,name=iPhone 15' \
build
```
### Firebase Functions
```bash
cd functions
npm install
npm run build
npm run serve
```
### Optional Server
```bash
cd server
npm install
npm run dev
```
---
## Data Model
Closer combines local-first question content with cloud sync for shared relationship state:
- **Room (Android) / local bundled JSON (iOS)** stores seeded question categories, questions, and date preferences — the app stays usable offline.
- **DataStore (Android) / UserDefaults (iOS)** stores local settings and lightweight preferences.
- **Firestore** stores users, couples, invites, daily questions, answer history, date matches, bucket-list state, time capsules, gentle reminders, and entitlements.
- **Cloud Functions** handle pairing lifecycle, reminder workflows, billing entitlement sync, RevenueCat webhooks, partner activity triggers (FCM), and security checks.
- **FCM** delivers reminders and partner activity notifications to both platforms.
---
## Privacy & Safety Principles
- Couple data is **scoped by couple ID** and protected by Firestore security rules — no other couple can read another couple's data.
- Answers are **private first**, then revealed intentionally by the partner.
- Account deletion and privacy screens are first-class surfaces in the app.
- **App Check** (Play Integrity on Android, DeviceCheck on iOS) blocks abusive backend access.
- Subscription state is verified **server-side** via Cloud Functions rather than trusting the client.
- All private answer and capsule content is **end-to-end encrypted** with couple-owned keys (Android uses Tink; iOS uses Apple CryptoKit in a follow-up batch).
---
## Current Status
This is a **private MVP / internal testing codebase**.
### Complete
- Android MVP — onboarding, auth (anonymous/email/Google), pairing, home, daily questions, question packs, answer history, spin wheel, date tools, settings, billing, notifications, Firebase integration, E2EE crypto layer
- iOS port scaffold — all 49 screens mapped to SwiftUI views, Firebase/RevenueCat integration, full screen parity landed on `dev` branch
- Backend — 17 Cloud Functions (callable, triggers, scheduled, webhook) covering full relationship lifecycle
- Seeded question content — 6,000+ prompts across 22 categories
### In Progress
- iOS Xcode project generation and build verification
- iOS E2EE layer (CryptoKit interop with Android Tink)
- Final QA pass on both platforms
### Release Prep
- `docs/release/internal-testing-checklist.md`
- `docs/release/store-assets.md`
- `docs/qa/private-mvp-checklist.md`
- `docs/qa/ui-review.md`
---
## Project History
See `HISTORY.md` for the full changelog and release notes.
See `PROJECT.md` for detailed scope, feature matrix, and architectural decisions.
---
## License
Private project. All rights reserved.