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. To 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.
Flow użytkownika
- 1Login przez Apple / Google / email (Supabase Auth).
- 2Onboarding: 30-sekundowy test plank z keep-awake na ekranie, wybór nicku i avatara.
- 3Wejście do kolejki matchingu – silnik dobiera partnera po karmie, średnim czasie planka i strefie czasowej.
- 47-dniowy pakt: codzienny timer, chat realtime z partnerem, status drugiej osoby na żywo.
- 5Po pakcie: aktualizacja karmy (snapshot + event sourcing), opcjonalny rematch, historia w profilu.
Co robi aplikacja
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, ale 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
Czat z partnerem oparty o Supabase Realtime (postgres_changes na chat_messages). Optimistic insert daje natychmiastowe UI bez loading state.
Karma + event sourcing
karma_snapshot przechowuje aktualny score, karma_events daje audytowalną historię i ranking percentylowy. Łatwy rollback przy korektach.
Streak i rematch
Po zamknięciu paktu opcjonalny rematch z tym samym partnerem albo wejście w nową kolejkę. Historia paktów żyje w profilu.
Wyzwania i rozwiązania
Problem
Dwóch userów z różnych stref czasowych ma „ten sam dzień paktu” o innych porach. UTC midnight łamie jednych, lokalny midnight drugich.
Rozwiązanie
Per-user deadlines: 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).
Problem
Matching musi działać też przy małej puli aktywnych użytkowników – ścisłe kryteria oznaczają długie czekanie, luźne psują jakość pary.
Rozwiązanie
Progressive widening: 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.
Problem
Stan partnera musi być widoczny natychmiast – inaczej presja społeczna gubi efekt.
Rozwiązanie
Subskrypcja Realtime na pact_days w plank-hub aktualizuje status drugiej osoby w momencie, gdy zakończy plank. Bez polling, bez odświeżania.
Stack technologiczny
Galeria ekranów


















