PlankPact

Projekt

PlankPact

PlankPact: 7-dniowy pakt plankowy z anonimowym partnerem

React Native 0.81 + Expo 54 + Supabase

Aplikacja habit-building, w której algorytm łączy dwie anonimowe osoby w 7-dniowe wyzwanie plankowe. Codziennie oboje muszą zrobić plank – jeśli ktoś zerwie, pakt kończy się dla obu. Presja społeczna w stylu Duolingo jako motor wytrwałości.

PlankPact screenshot 1PlankPact screenshot 2PlankPact screenshot 3PlankPact screenshot 4PlankPact screenshot 5

Geneza projektu

Projekt rozwijany dla Syntagma Marketing LTD jako kompletna aplikacja habit-building na iOS i Android. Mój zakres obejmuje całość warstwy klienckiej (React Native + Expo), backend na Supabase (Postgres z RLS, Realtime, 15 Edge Functions w Deno, 30 migracji SQL) oraz integracje – RevenueCat dla IAP, OneSignal dla pushy, Sentry, Firebase Analytics, AppsFlyer.

Kluczowe funkcjonalności

Anonimowy matching

Edge Function dobiera partnera po karmie, średnim czasie planka i strefie czasowej. Bez znajomych, bez znanych twarzy – tylko ktoś, kto ma podobny poziom i jest gotów dziś.

Plank z keep-awake

Natywny timer planka z expo-keep-awake aktywowanym tylko na czas ćwiczenia. Ekran nie gaśnie podczas trzymania, a bateria nie cierpi poza sesją.

Fair między strefami

Każdy uczestnik ma własny UTC deadline pre-kalkulowany przy matchingu (23:59:59 w jego IANA timezone). Backend porównuje proste timestampy – żadnych runtime DST niespodzianek.

Chat realtime i karma

Czat z partnerem oparty o Supabase Realtime z optimistic insertami. Karma trzymana w event-sourced tabeli daje audytowalną historię i ranking percentylowy.

Decyzje techniczne

Per-user deadlines zamiast globalnego midnight

pact_days trzyma user_a_deadline i user_b_deadline jako UTC timestampy, pre-kalkulowane w match-users (binary search w 15-min krokach w IANA timezone). Cron close-pact-day robi tylko lt('user_a_deadline', now) – żadnej runtime gimnastyki DST.

Progressive widening w matchingu

Pierwszy próg to tz diff ≤2h i ścisła karma, po 15 min ≤3h, po 30+ min rozluźnienie do 10h. Kompromis quality ↔ wait time bez ręcznych decyzji, działa też przy małej puli aktywnych użytkowników.

Karma jako snapshot + event sourcing

karma_snapshot przechowuje aktualny score dla szybkich odczytów, karma_events daje pełną historię zdarzeń i prosty rollback przy korektach. Ranking percentylowy liczy się ze snapshotów.

Wyzwania i rozwiązania

Problem

Dwóch użytkowników z różnych stref czasowych ma „ten sam dzień paktu" o innych porach. UTC midnight łamie jednych, lokalny midnight drugich – a stan partnera musi być widoczny natychmiast, bo inaczej presja społeczna gubi efekt.

Rozwiązanie

Per-user UTC deadlines pre-kalkulowane przy matchingu oraz subskrypcja Realtime na pact_days w plank-hub, która aktualizuje status drugiej osoby w momencie, gdy zakończy plank. Bez polling, bez odświeżania, fair niezależnie od strefy.

Stack technologiczny

FrameworkReact Native 0.81 + Expo 54
LanguageTypeScript 5.9
Routingexpo-router 6
StateZustand + React Query
BackendSupabase (Postgres + RLS + Realtime)
Edge15 Deno Edge Functions + cron
AuthApple / Google / Email (Supabase Auth)
PaymentsRevenueCat
PushOneSignal
MonitoringSentry + Firebase Analytics + AppsFlyer