Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c180a09
Implement server-side functions, rules, and iOS improvements for Resi…
cursoragent Aug 13, 2025
a737d35
Add Firebase analytics, remote config, and improve data management
cursoragent Aug 13, 2025
a2aa346
Implement community post visibility, image handling, and FCM token re…
cursoragent Aug 13, 2025
ed1687c
Add data export to storage, notification improvements, and sync enhan…
cursoragent Aug 13, 2025
0123023
Add age gate, URL schemes, and remote notification support
cursoragent Aug 13, 2025
c84b328
Add photo library permission and new UI tests for age gate and commun…
cursoragent Aug 13, 2025
eaf42f2
Add iOS Fastlane CI/CD and scheduled Firestore cleanup
cursoragent Aug 13, 2025
b1fd546
Configure Firebase Messaging and handle FCM token registration
cursoragent Aug 13, 2025
e8cddca
Add navigation link to response templates in recovery hub view
cursoragent Aug 13, 2025
56d9401
Add App Store privacy configuration for iOS app
cursoragent Aug 13, 2025
682c43e
Add methods to enable/disable daily check-in and recovery follow-up n…
cursoragent Aug 13, 2025
85ff49e
Add empty state views, haptics, and error handling in community and h…
cursoragent Aug 13, 2025
e855418
Add onboarding flow and improve notification settings management
cursoragent Aug 13, 2025
4845ff3
Add loading states and skeleton views for community and dashboard
cursoragent Aug 13, 2025
6e1ccc4
Add resilient card style to dashboard, community, and history views
cursoragent Aug 13, 2025
2a4bad4
Initialize ResilientMe iOS project with documentation and project str…
cursoragent Aug 13, 2025
72cce3c
Add Firebase deployment workflow for Firestore rules, indexes, and fu…
cursoragent Aug 13, 2025
3c6a080
Update Firebase deploy workflow to use GCP service account key
cursoragent Aug 13, 2025
5b92cba
Add accessibility labels and response templates for rejection scenarios
cursoragent Aug 13, 2025
9270cba
Add edit functionality for rejection history entries
cursoragent Aug 13, 2025
ddc1eeb
Add share functionality and loading skeleton for history view
cursoragent Aug 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: CI

on:
push:
branches: [ main, develop, '**' ]
pull_request:
branches: [ main, develop ]
workflow_dispatch:
inputs:
beta:
description: 'Run beta lane'
required: false
default: 'false'

jobs:
functions-tests:
runs-on: ubuntu-latest
defaults:
run:
working-directory: functions
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run build
- run: npm test

ios-build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Select Xcode
run: sudo xcode-select -s "/Applications/Xcode.app/Contents/Developer"
- name: Install xcpretty
run: gem install xcpretty
- name: Build App
run: xcodebuild -project ios/ResilientMe.xcodeproj -scheme ResilientMe -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' build | xcpretty
shell: bash
env:
NSUnbufferedIO: YES

ios-fastlane-test:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
- name: Install Bundler and Fastlane
run: |
gem install bundler fastlane
- name: Run Fastlane test
working-directory: ios
run: fastlane test

ios-fastlane-beta:
if: ${{ github.event.inputs.beta == 'true' }}
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
- name: Install Bundler and Fastlane
run: |
gem install bundler fastlane
- name: Run Fastlane beta
working-directory: ios
env:
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
run: fastlane beta
35 changes: 35 additions & 0 deletions .github/workflows/firebase-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Firebase Deploy

on:
workflow_dispatch:
inputs:
target:
description: 'Firebase target: default|staging|production'
required: false
default: 'default'

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Functions deps
working-directory: functions
run: npm ci
- name: Build Functions
working-directory: functions
run: npm run build
- name: Write GCP service account key
run: |
echo "${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_JSON }}" > $RUNNER_TEMP/gcp-key.json
echo "PROJECT_ID=$(if [ \"${{ github.event.inputs.target }}\" = \"production\" ]; then echo resilientme-prod; else echo resilientme-staging; fi)" >> $GITHUB_ENV
- name: Firebase deploy (rules, indexes, functions)
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{ runner.temp }}/gcp-key.json
run: |
npm i -g firebase-tools
firebase deploy --project $PROJECT_ID --only firestore:rules,firestore:indexes,functions
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# ResilientMe (iOS + Firebase)

Turn rejection into resilience. ResilientMe helps Gen Z log rejections in seconds, surface patterns, and bounce back with actionable recovery tools and community support.

## Highlights
- 30-sec Quick Log with types, impact slider, optional note/screenshot, and haptics
- Smart Dashboard with resilience score, weekly stats, trends, and pattern alerts
- Recovery Hub with contextual plans, quick actions, and response templates
- Daily Challenges with server-side generation and streaks
- Anonymous Community feed with reactions, moderation/reporting, and callables
- Offline-first logging with Core Data and background sync queue
- Push notifications (daily check-in, recovery follow-ups) and deep-links to tabs
- Privacy-first: anonymous by default, biometric lock, export/delete data
- CI/CD with GitHub Actions + Fastlane; Firestore/Storage rules + tests

## Repo structure
```
functions/ # Firebase Cloud Functions (TypeScript)
ios/ResilientMe/ # iOS SwiftUI app
ios/fastlane/ # Fastlane lanes for test/beta
.github/workflows/ # CI (functions tests, iOS build, fastlane)
firestore.rules # Firestore security rules
firestore.indexes.json # Firestore indexes
storage.rules # Storage rules
ios/ResilientMe/PrivacyInfo.xcprivacy # Privacy Manifest
ios/AppStorePrivacy.json # App Store privacy summary (reference)
```

## Architecture (quick)
- Client (SwiftUI): Views → Managers (state/logic) → Services (integration) → Models
- Local: Core Data (offline queue), plus transient in-memory state
- Cloud: Firestore (users/*, community, aggregates), Functions (callables, triggers, schedulers), Storage (images), FCM; GA4 analytics; Remote Config flags

Details: see `docs/ARCHITECTURE.md` and `docs/API.md`.

## Getting started (local)
- iOS: open `ios/ResilientMe.xcodeproj` (SwiftUI, iOS 15+). Build and run on simulator.
- Firebase emulators (optional): `cd functions && npm i && npm run serve`
- Functions build/test: `cd functions && npm i && npm run build && npm test`

Note: You can run CI-only paths without local builds. See CI and release below.

## Security
- Firestore rules enforce least privilege. `users/{uid}/**` only by owner. `community` is read-only for clients. Server-only collections (`userReactions`, `communityReports`, `rateLimits`) deny all client access.
- Storage rules: `rejection_images/{uid}/**` only by owner.
- Functions: input sanitization (content), rate limiting (per user/window), moderation/report flow.

More: `docs/SECURITY_PRIVACY.md`.

## Analytics & Remote Config
- GA4 events wired (screen views, core actions). See `docs/ANALYTICS.md`.
- Remote Config: `communityEnabled`, `challengeDifficulty`. See `docs/CONFIG.md`.

## CI/CD
- GitHub Actions: functions tests, iOS simulator build, fastlane test; optional manual TestFlight (`beta=true`).
- Fastlane lanes (in `ios/fastlane`): `test` and `beta`.

See `docs/RELEASE.md` for secrets and steps.

## Development standards
- Code style: SwiftUI + managers/services, meaningful naming, accessible UI (44pt targets, labels), skeletons and empty states.
- Design tokens: Dark theme, brand colors/typography.
- Docs: see `/docs/*` for deep dives.

## Documentation map
- Architecture: `docs/ARCHITECTURE.md`
- Cloud APIs: `docs/API.md`
- Security & Privacy: `docs/SECURITY_PRIVACY.md`
- Analytics taxonomy: `docs/ANALYTICS.md`
- Config & flags: `docs/CONFIG.md`
- Testing: `docs/TESTING.md`
- Release & App Store: `docs/RELEASE.md`
- UX & design system: `docs/UX.md`
- Troubleshooting: `docs/TROUBLESHOOTING.md`

## License
Proprietary. All rights reserved.
13 changes: 13 additions & 0 deletions docs/ANALYTICS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Analytics (GA4)

## Events
- `screen_view` (auto) with screen name
- `rejection_log` { type }
- `challenge_complete`
- `challenge_skip`
- `community_post`
- `reaction_add` { reaction }

## Notes
- Debug with GA4 DebugView.
- Consider adding user properties (e.g., anonymous_mode) later.
21 changes: 21 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Cloud API

## Callables
- `createCommunityPost({ type: 'dating'|'job'|'social'|'other', content: string })` → `{ id }`
- Sanitizes input, rate-limits, sets `status: 'visible'`
- `reactToPost({ postId, reaction: '💪'|'😔'|'🎉'|'🫂' })` → `{ ok: true }`
- Dedupe per user via `userReactions` and atomic increment
- `reportPost({ postId })` → `{ ok: true }`
- Increments `reports`, hides at threshold
- `requestDataExport()` → `{ url }`
- Bundles user data to Storage, returns 1h signed URL; rate-limited
- `requestAccountDeletion()` → `{ ok: true }`
- Deletes user subcollections, images, user doc, Auth user
- `backfillCommunityStatus()` [admin]
- Sets `status: 'visible'` on recent posts lacking it

## Triggers & schedules
- Firestore: `onRejectionCreate` aggregates, server patterns, optional FCM
- Firestore: `onRejectionDelete` removes Storage image
- Pub/Sub: `generateDailyChallenges` daily at 05:00 UTC
- Pub/Sub: `cleanupStaleDocs` every 24h
36 changes: 36 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Architecture

## Overview
- SwiftUI client with Managers (state/logic), Services (integration), and Models
- Firebase backend: Firestore, Functions, Storage, Auth, Remote Config, FCM, GA4
- Offline-first with Core Data and a background sync queue

## Client layers
- Views (SwiftUI): `Views/*`
- Managers: `Managers/*` (e.g., RejectionManager, CommunityManager, ChallengeManager)
- Services: `Services/*` (FirestoreSyncService, FirebaseConfigurator, ImageUploadService)
- Models: `Models/*`
- Design: `Extensions/DesignSystem.swift`, `Assets`

## Data model (core)
- RejectionEntry: id, type, emotionalImpact, note, timestamp, imageUrl?
- Aggregates: users/{uid}/aggregates/{dayKey}, users/{uid}/aggregates/patterns
- Community: `community/{postId}` with `type`, `content`, `createdAt`, `reactions`, `status`

## Cloud functions
- Rejections: onCreate aggregates + server-side pattern detection + push
- Community: callable `createCommunityPost`, `reactToPost` (dedupe), `reportPost` (hide when reports >= 3)
- Challenges: scheduled `generateDailyChallenges`
- Data lifecycle: callables `requestDataExport` (signed URL), `requestAccountDeletion`
- Maintenance: `cleanupStaleDocs` (rateLimits/userReactions), `backfillCommunityStatus`

## Security
- Firestore: user-owned subtree; community read-only; server-only collections deny client access
- Storage: images path per user; rules allow only owner

## Notifications
- Local: daily check-in, recovery follow-ups; categories and deep-link routing
- Remote: FCM token saved per user; server sends on rejections (if token)

## Analytics & Config
- Screen_view + action events; Remote Config flags (`communityEnabled`, `challengeDifficulty`)
8 changes: 8 additions & 0 deletions docs/CONFIG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Remote Config

## Flags
- `communityEnabled` (bool): shows/hides Community tab
- `challengeDifficulty` (string): reserved for tuning challenge generator

## Client
- `RemoteConfigManager` loads flags on app start and gates `ContentView` tabs.
19 changes: 19 additions & 0 deletions docs/RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Release & CI/CD

## GitHub Actions
- Functions tests, iOS build, Fastlane test
- Manual beta trigger: Actions → Run workflow → set `beta=true`

## Secrets
- `APP_STORE_CONNECT_API_KEY_ID`
- `APP_STORE_CONNECT_ISSUER_ID`
- `APP_STORE_CONNECT_API_KEY` (base64 .p8)
- (Optional) `FIREBASE_TOKEN` for deploys

## Firebase deploys
- Rules/indexes: `firebase deploy --only firestore:rules,firestore:indexes`
- Functions: `cd functions && npm i && npm run build && npm run deploy`

## TestFlight
- Trigger Fastlane beta after secrets added. Upload occurs via App Store Connect API key.
- Add testers and notes in App Store Connect.
17 changes: 17 additions & 0 deletions docs/SECURITY_PRIVACY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Security & Privacy

## Firestore rules
- `users/{userId}/{document=**}`: allow if `request.auth.uid == userId`
- `community/{postId}`: allow read; deny all client writes (Functions only)
- `userReactions`, `communityReports`, `rateLimits`: deny client read/write

## Storage rules
- `rejection_images/{userId}/{allPaths=**}`: allow if `request.auth.uid == userId`

## Functions
- Sanitizes text input for community posts
- Rate-limits callables (per user/window) via `rateLimits`
- Deletes Storage images on entry/account deletion

## Privacy Manifest
- `ios/ResilientMe/PrivacyInfo.xcprivacy`: no tracking; data types include User ID, Device ID, Product Interaction, Email Address; purposes are App Functionality, Analytics, Account Management
12 changes: 12 additions & 0 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Testing

## Functions
- Install deps: `cd functions && npm i`
- Build: `npm run build`
- Run tests: `npm test`
- Rules tests cover users/community/server-only collections
- Storage tests cover user image paths

## iOS UI Tests
- `ResilientMeUITests` includes Quick Log flow and Community tab presence
- CI runs fastlane test on simulator (see GitHub Actions)
19 changes: 19 additions & 0 deletions docs/UX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# UX & Design System

## Tokens
- Dark theme; primary blue, energy orange; Inter typography as in design tokens
- Card style: 16pt radius, systemGray6 backgrounds, consistent paddings

## Patterns
- 30-sec Quick Log: minimal required fields, big touch targets, haptics + success hint
- Skeletons on data fetch; empty states with actionable CTAs
- Optimistic UI on reactions; lightweight error banners on network failures
- Notification toggles in Settings (daily check-in, follow-ups)
- Onboarding: privacy-first (anonymous by default), notification CTA

## Accessibility
- 44pt minimum targets; VoiceOver labels/values on list items; avoid reliance on color only; Dynamic Type friendly layouts

## Microcopy
- Empathetic, Gen Z voice; short, encouraging phrases (e.g., “Logged. That’s strength.”)
- Avoid clinical/corporate tone; be direct and supportive
3 changes: 2 additions & 1 deletion firestore.indexes.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
"collectionGroup": "community",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "status", "order": "ASCENDING" },
{ "fieldPath": "createdAt", "order": "DESCENDING" }
]
},
{
"collectionGroup": "rejections",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "createdAt", "order": "DESCENDING" }
{ "fieldPath": "timestamp", "order": "DESCENDING" }
]
}
],
Expand Down
Loading
Loading