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.





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.
