PlankPact logo

Project

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. To presja społeczna w stylu Duolingo jako motor wytrwałości.

PlankPact app screen 1PlankPact app screen 7PlankPact app screen 10

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

  1. 1Login przez Apple / Google / email (Supabase Auth).
  2. 2Onboarding: 30-sekundowy test plank z keep-awake na ekranie, wybór nicku i avatara.
  3. 3Wejście do kolejki matchingu – silnik dobiera partnera po karmie, średnim czasie planka i strefie czasowej.
  4. 47-dniowy pakt: codzienny timer, chat realtime z partnerem, status drugiej osoby na żywo.
  5. 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

FrameworkReact Native 0.81 + Expo 54
Routingexpo-router 6
LanguageTypeScript 5.9
StateZustand + React Query
FormsReact Hook Form + Zod
AnimationsReanimated 4
StorageMMKV
BackendSupabase (Postgres + RLS + Realtime)
Edge15 Deno Edge Functions + cron
AuthApple / Google / Email via Supabase Auth
PaymentsRevenueCat
PushOneSignal
MonitoringSentry
AnalyticsFirebase Analytics + AppsFlyer
i18ni18next + react-i18next

Galeria ekranów

PlankPact app screen 1PlankPact app screen 2PlankPact app screen 3PlankPact app screen 4PlankPact app screen 5PlankPact app screen 6PlankPact app screen 7PlankPact app screen 8PlankPact app screen 9PlankPact app screen 10PlankPact app screen 11PlankPact app screen 12PlankPact app screen 13PlankPact app screen 14PlankPact app screen 15PlankPact app screen 16PlankPact app screen 17PlankPact app screen 18