Commit Graph
49 Commits
Author SHA1 Message Date
adminandClaude Opus 5.5 2bd4b2d973 feat(mobile): Badge fuer ungelesene Nachrichten an der Sprechblase
found.md (iOS-Feedback): Push kam an, aber die Sprechblase zeigte nichts.
MessagesHeaderButton zeigt jetzt conversations.unreadCount im selben Badge
wie die Glocke (gemeinsame UnreadBadge-Komponente), aktualisiert alle 30 s;
Lesen eines Chats invalidiert den Zaehler bereits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 15:01:51 +02:00
adminandClaude Opus 5.5 333ba16573 fix(mobile): iOS-Tastatur verdeckt Eingabefelder nicht mehr
Profil bearbeiten (Geburtstag/Adoptionstag), Gesundheit, neuer Beitrag,
neuer Meilenstein, Tier anlegen/Onboarding und Einladen hatten keinen
Tastatur-Ausgleich: das fokussierte Feld lag unter der Tastatur.
automaticallyAdjustKeyboardInsets setzt den unteren Inset und scrollt
das aktive Feld in den sichtbaren Bereich (RN Fabric ScrollView),
keyboardShouldPersistTaps="handled" laesst Feldwechsel ohne Zuklappen zu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:54:35 +02:00
adminandClaude Opus 5.5 c74bbffccb feat: Datumseingabe TT.MM.JJJJ (App) + Erinnerung an Folgeimpfung
Datum (found.md, iOS-Feedback): neue DateField-Komponente mit Ziffern-
tastatur und automatisch eingefügten Punkten (TT.MM.JJJJ) für Geburtstag,
Adoptionstag, Gewicht, Tierarzt und Impfungen. Parser akzeptiert weiter
"-" und "/" sowie einstellige Tage/Monate. Wheel-Picker bleibt in
PENDING-NATIVE-BUILD.md (natives Modul).

Impf-Erinnerung (found.md, iOS-Feedback): täglicher Cron
/api/cron/vaccine-reminders erinnert einmalig 30 Tage vor
Vaccine.nextDueDate per Mitteilung (VACCINE_DUE) + Push mit Impfname und
Datum. Schema: NotificationType.VACCINE_DUE, Notification.vaccineId
(Cascade), Vaccine.reminderSentAt (Claim gegen Doppelversand, wird bei
Fehler wieder freigegeben). Web + App zeigen die Mitteilung an, Tippen
öffnet die Gesundheitsseite. App: Schnellauswahl +1/+3 Jahre für den
Folgetermin plus Hinweis auf die Erinnerung.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:42:20 +02:00
adminandClaude Opus 5.5 5606a81dd2 feat(mobile): Fortschrittsbalken beim Video-Story-Upload
Statt nur Spinner: „Upload wird vorbereitet …“, dann Balken + Prozent aus
expo-file-system File.upload onProgress (nativ seit 57.0.7 im 1.0.6-Build,
daher OTA). Vorschau ist während des Uploads nicht mehr antippbar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 14:44:35 +02:00
adminandClaude Opus 5.5 69e2c1364e feat(mobile): Video-Storys in der App (Upload + Wiedergabe)
Merged feature/mobile-video-stories (Gitea #37) auf den seit 02.10.
umgebauten Story-Viewer: Galerie-Picker nimmt Videos bis 30 s, Upload
direkt zu Mux, Viewer spielt READY-Videos ab (Fortschritt aus der echten
Wiedergabe, Weiter bei playToEnd) und zeigt PROCESSING/ERROR. Offene
Kommentar-/Zuschauer-Sheets pausieren das Video wie den Foto-Timer.
Nur JS — expo-video ist seit dem 1.0.6-Build nativ enthalten, also OTA.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 14:25:24 +02:00
adminandClaude Opus 5.5 307b76e8fd feat: Gründungsmitglieder (erste 250 Tiere) und Kurator-Begrüßung
- Pet.isFoundingMember: in pets.create per bewachter SQL-Anweisung für die
  ersten 250 Tiere gesetzt, nie entzogen; Abzeichen (Spross) in Feed und
  Profil, Web + App, unabhängig vom Empfehlungsprogramm
- Kurator "PawFeed Team": folgt jedem neuen Tier und schickt eine
  Begrüßungs-Direktnachricht (DE/EN nach Sprache beim Anlegen), im
  Hintergrund — blockiert das Onboarding nicht
- CuratorSettings + Admin-Seite /p/[secret]/curator: Schalter, Texte mit
  Pflicht-Platzhalter {name} und Vorschau, Zähler der Gründungsmitglieder
- prisma/backfill_founding_members.sql, prisma/enable_rls_curator.sql

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:26:43 +02:00
adminandClaude Opus 5.5 2c0680cef9 feat: 7-Tage-Ansicht der eigenen Storys für den Ersteller (Web + App)
- stories.listActive: der Owner bekommt seine Storys der letzten 7 Tage mit
  isExpired, alle anderen weiterhin nur die aktiven 24 Stunden
- stories.hasActiveStory: optional includeOwnRecent → grauer Ring
  (expiredOnly) nur in der Story-Leiste, nicht an Profilbildern im Feed
- Viewer: Hinweis "Abgelaufen – nur für dich sichtbar", Kommentare bei
  abgelaufenen Storys nur lesbar, Pfote nur als Zähler; Löschen und
  Zuschauerliste bleiben

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:30:22 +02:00
adminandClaude Opus 5.5 81d9c6da30 feat: eigene Story löschen (Web + App)
- stories.delete: nur Owner; Aufrufe, Story (Pfoten/Kommentare per Cascade),
  danach Bild in Supabase Storage bzw. Mux-Asset best-effort
- Web: Papierkorb neben der Zuschauerzahl + Rückfrage, Timer pausiert,
  gelöscht wird die beim Antippen gemerkte Story
- App: gleicher Papierkorb mit nativer Rückfrage, Timer pausiert
- found.md: Nutzer-Feedback "Story löschbar" erledigt, 7-Tage-Wunsch mit
  Befund als offener Punkt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:17:16 +02:00
adminandClaude Opus 5.5 ccebfa46a6 feat: Story-Kommentare, Pfoten und Zuschauerliste in der App
- App-Viewer: Pfote, Kommentar-Sheet (schreiben, eigene löschen) und für die
  eigene Story die Zuschauerliste; Timer pausiert, solange ein Sheet offen ist
- stories.listActive: optionales viewerPetId → reactedByViewer, damit die
  Pfote gefüllt startet (abwärtskompatibel für alte App-Builds)
- Web-StoryPawButton nutzt reactedByViewer ebenfalls und wird pro Story
  neu initialisiert

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:59:02 +02:00
adminandClaude Opus 5.5 1bf0f9282a fix: HEIF/HEIC-Fotos (Apple) für Beiträge und Storys annehmen
- App: post-new.tsx sortierte HEIC/HEIF still aus, obwohl resizeForUpload()
  ohnehin in JPEG umwandelt — jetzt erlaubt, plus Hinweis bei übersprungenen
  Fotos
- Web: neues src/lib/heic-to-jpeg.ts wandelt HEIC vor dem Upload im Browser
  in JPEG um (Safari kann HEIC nativ dekodieren); eingebaut in Beiträge,
  Storys, Meilensteine und Tierbild, accept um HEIC ergänzt. Browser ohne
  HEIC-Dekodierung bekommen eine klare Meldung statt "falsches Format".
- Server-Whitelist (JPEG/PNG/WebP) bleibt unverändert

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:26:24 +02:00
adminandClaude Opus 5.5 32ba01f7cf refactor: altes Einladungs-Gate (InviteCode/pf_invite) durch Empfehlungsprogramm ersetzen
- Proxy-Sperre für /sign-up, INVITE_REQUIRED, set-invite/consume-invite,
  invites-Router, Admin-Invite-Seite und -Prozeduren, Startcodes in
  pets.create entfernt — Registrierung ist offen
- /join?code=… leitet auf /r/<code> um, /invite auf /einladen; alte geteilte
  Links laufen nicht ins Leere
- InviteCode-Tabelle bleibt nur als Historie (Kontolöschung räumt sie weiter auf)
- Datenschutzerklärung 4b (Empfehlungsprogramm, Herkunftsangabe, pf_ref-Cookie)
  und Nutzungsbedingungen 3b (Programmbedingungen, Bestandsschutz, Missbrauch)
- CLAUDE.md: Abschnitt "Referral program" statt "Invite system"

Build, Typecheck und alle Tests grün.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:35:22 +02:00
adminandClaude Opus 5.5 78e16c9210 feat(mobile): Empfehlungsprogramm in der App
- Bildschirm "Freunde einladen" (/invite, aus den Einstellungen erreichbar):
  eigener Code mit nativem Teilen-Dialog, Rangleiter mit Fortschritt,
  eingeladene Tiere, nachträgliche Code-Eingabe im Einlösefenster
- Onboarding-Schritt "Wie hast du von PawFeed erfahren?" mit Code-Eingabe,
  einmal pro Owner (referrals.onboardingState)
- Rang-Abzeichen in PetIdentityBadges (Feed und Profil), Profilring in
  Rangfarbe, "Eingeladen von …" im Tierprofil
- Benachrichtigungen REFERRAL_JOINED / REFERRAL_RANK_UP
- Keine neuen nativen Module (React Native Share) — per OTA auslieferbar

Typecheck grün; nicht auf einem Gerät getestet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:30:11 +02:00
adminandClaude Opus 5.5 de237f19a1 fix(mobile): content hidden under Android gesture bar on stack screens
Android is edge-to-edge, and stack screens (no tab bar below) had no
bottom inset on their scroll containers — the first post on a pet profile
sat in the gesture area and couldn't be tapped (found.md). New
useBottomSafePadding hook adds insets.bottom to the contentContainerStyle
of pet profile, followers, following, health, milestones, edit pet,
settings, search, messages list and delete account.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 12:41:42 +02:00
adminandClaude Opus 5.5 4213d7029e feat(mobile): block pets and delete account in-app (App Store requirements)
- Block (Guideline 1.2, UGC): useBlockPet hook (native confirm Alert,
  blocks.create). PostCard "..." on other pets' posts now offers
  Report / Block; pet profile gets a header "..." with Report pet / Block
  (hidden for any of the owner's own pets), navigating back after a block.
  Feed, comments and DMs already enforce blocks server-side; explore and
  search don't yet (server follow-up).
- Delete account (Guideline 5.1.1(v)): new delete-account screen reached
  from a quiet link below Log out in Settings. Mirrors web's
  DeleteAccountDialog (warning list + type a pet name) and calls the same
  POST /api/account/delete with the Clerk bearer token, then clears the
  query cache and signs out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 11:19:17 +02:00
adminandClaude Opus 5.5 69e095ddcb fix(mobile/ios): back button showed "< (tabs)" label
iOS labels the back button with the previous screen's title, which was
the untitled (tabs) route group. Use headerBackButtonDisplayMode
"minimal" (chevron only) for all stack screens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 09:07:49 +02:00
adminandClaude Opus 5.5 1e7d76aa79 feat(mobile/ios): iOS simulator build profile and native Sign in with Apple
- eas.json: new `preview-simulator` profile (extends preview, ios.simulator)
  for EAS cloud simulator builds without an Apple Developer account
- app.json: iOS deployment target 17.0 via expo-build-properties
  (@clerk/expo's ClerkKit SPM package requires iOS 17 — pod install failed
  on the default 16.4), iPhone-only (supportsTablet: false),
  usesAppleSignIn, ITSAppUsesNonExemptEncryption: false
- AppleSignInButton: native Apple sheet via Clerk's useSignInWithApple
  (App Store Guideline 4.8, since Google login is offered); same
  legal-consent handling as GoogleSignInButton; iOS-only
- Not yet testable end-to-end: needs Apple Developer account, the
  Sign in with Apple capability on the App ID and Apple enabled in Clerk

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 09:07:49 +02:00
adminandClaude Sonnet 5 17a17bd392 fix(mobile): add sso-callback route to fix Unmatched Route on Android Google login
Multiple users reported "Unmatched Route pawfeed://sso-callback" after
Google sign-in. Root cause: Android's custom tab delivers the OAuth
redirect intent to the app twice — once caught correctly by
expo-web-browser's openAuthSessionAsync() session, but also as a plain
deep link seen by expo-router's own Linking listener. With no matching
route, expo-router briefly showed its built-in Unmatched Route error
screen instead of letting the real sign-in flow (GoogleSignInButton ->
setActive -> router.replace) take over.

Adds app/sso-callback.tsx as a themed loading spinner so expo-router has
a valid screen for that window; no auth logic needed there since
useProtectedRoute() in app/_layout.tsx already redirects based on
Clerk's session state. JS-only change, OTA-deployable, no native rebuild
needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 17:24:35 +02:00
adminandClaude Sonnet 5 2b285de09e feat(mobile): add video story upload and playback (Gitea #37)
Extends the gallery picker to accept videos, uploads them via Mux direct
upload (same flow web already uses), and adds PROCESSING/READY/ERROR
handling to the story viewer with real playback-driven progress instead
of a fixed timer.

Fixes a video-viewer bug found during device testing: play() was called
before the player reached readyToPlay, leaving duration at 0 and firing
playToEnd immediately — the viewer closed within ~1s instead of playing.
Now waits for statusChange to readyToPlay before calling play().

Also fixes the story-creation "Story teilen" button sitting flush against
gesture navigation on devices without a bottom safe-area inset applied.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 14:23:03 +02:00
adminandClaude Sonnet 5 be5a333de1 feat(mobile): move Settings out of Meine Tiere into a header-icon screen
Closes Gitea #36. Plan: ~/.claude/plans/mobile-settings-tab-extraction.md.

Extracts the 5 footer sections pets.tsx had grown (Darstellung, Sprache,
Tier-Vorschlaege opt-out, Rechtliches+Version/OTA, Abmelden) into their
own files under src/components/settings/, and a new app/(app)/settings.tsx
screen reached via a gear icon in every tab's header, next to Messages -
not a 5th bottom tab. Chosen over a tab (see the two mockups in the plan
folder) because Settings is a low-frequency destination for a typical
user; the header icon still reaches it in one tap from anywhere without
spending permanent bottom-nav space on it.

settings.tsx lives outside (tabs)/, registered as a plain Stack.Screen
(app/(app)/_layout.tsx) matching search.tsx's pattern - normal back-arrow
push, no presentation:modal, no tab bar visible while it's open.
SettingsHeaderButton is wired into (tabs)/_layout.tsx's global headerRight
default plus the 3 tabs (index/explore/pets) that override it.

pets.tsx is pet-list-only again. FooterErrorBoundary now wraps the whole
Settings screen instead of only the logout button, closing a gap a prior
code review found (AppearanceSection/LegalSection were rendering
unprotected).

Code-reviewed before commit (WORKFLOW.md step 6) - fixed two of three
findings: extracted the section/sectionTitle style duplicated across
AppearanceSection/LanguageSection/PetSuggestionsSection into a shared
sectionTitleStyle.ts, and reduced every header icon's hitSlop 12->8 (rows
can now hold 3 icons with gap:16; 12+12=24 overlapped the gap, 8+8=16
exactly meets it - see the comment on headerButtons in (tabs)/_layout.tsx).
Left the pets.* i18n namespace as-is on the moved strings (documented in
settings.tsx's doc comment) - pets.language* is shared with the pre-login
LanguageSwitcher, so a partial rename would misdescribe those call sites.

Typecheck + lint clean across all touched/new files, i18n key parity
verified (de/en).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 12:39:34 +02:00
adminandClaude Sonnet 5 8b1aba1d9f feat(mobile): show OTA update id next to app version
Meine Tiere tab footer now shows 'Version X.X.X - OTA <short-id>' (or
'Build (kein OTA)' when running the embedded/non-OTA bundle) via
expo-updates' Updates.isEmbeddedLaunch/updateId. Directly answers the
support question 'did the update actually arrive on this device?' by
comparing the shown short id against the 'Android update ID' an
'eas update' publish prints - the exact ambiguity that cost a multi-day
debugging session earlier (see .claude/handoffs/2026-09-12-ota-update-
mystery-expo-dev-client-root-cause.md).

Code-reviewed before commit (per WORKFLOW.md step 6): fixed two of four
findings - wrapped the expo-updates reads in a try/catch (LegalSection
sits outside FooterErrorBoundary, so an unguarded throw here would have
taken down the whole footer) and moved the hardcoded 'OTA ' prefix into
i18n (pets.otaId). The web-shim-always-unknown finding is a documented
dead branch (this app ships no web build - eas update only ever runs
--platform android). No test coverage added - no RN test infrastructure
exists in mobile/ yet (pre-existing gap, tracked in WORKFLOW.md).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 11:05:26 +02:00
adminandClaude Sonnet 5 be2ab20273 fix(mobile): dark-mode white-on-white search input text
Same root cause as post-new.tsx/story-new.tsx: search.tsx imported the
static light-only colors/typography instead of useTheme(). Bigger
refactor than the other two since TabButton/PetRow/Loading/EmptyState
are separate top-level components, not closures inside the screen -
they now receive styles/colors as props from the theme-aware
makeStyles(colors) built in SearchScreen, rather than importing the
static tokens themselves.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 09:44:12 +02:00
adminandClaude Sonnet 5 9185e54402 fix(mobile): dark-mode white-on-white caption text in New Post
post-new.tsx (and story-new.tsx, same latent bug) still imported the
static, light-only colors/typography from theme/tokens.ts instead of
useTheme() - container background stayed permanently light while the
shared TextField correctly pulled dark-mode text color from the theme,
producing white text on a white background in the caption input.

Migrated both to the useTheme() + makeStyles(colors) pattern already
used by milestone-new.tsx. search.tsx has the same underlying issue but
needs a larger refactor (several sub-components reference the static
tokens directly) - left for a separate pass, noted in found.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 22:25:08 +02:00
adminandClaude Sonnet 5 99575ddc82 fix(mobile): Google SSO, image resize, Sentry, dark-mode splash (1.0.3-1.0.6)
Catches up several native builds' worth of work that was already tested
live and shipped to the Play Store (per mobile/found.md TODO Neu4-Neu6)
but never made it into a git commit:

- Google SSO via expo-auth-session/expo-web-browser (GoogleSignInButton,
  useWarmUpBrowser) on sign-in/sign-up.
- Upload-time image downscaling (resize-image.ts) to fix slow first
  content loads — capped at 1600px, re-encoded JPEG @0.8.
- @sentry/react-native (GlitchTip-compatible) crash reporting, plus a
  10s auth-loading timeout screen.
- Dark-mode splash screen background, breed/birthday editing in
  PetCreateForm, and the OptionPicker Modal rewrite that fixed the
  Android nested-scroll dropdown bug across Explore/onboarding/edit.
- expo-dev-client removed from the shared plugin list (was silently
  blocking OTA updates in production), app.json bumped to 1.0.6/
  versionCode 7, several expo-* packages patched via `expo install --fix`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 21:54:08 +02:00
adminandClaude Sonnet 5 e9bad092f2 feat: pet-suggestion notifications, story music overlay, auth UX fixes
Three pieces of work from this session:

- Pet-suggestion notifications: a weekly cron (species/breed affinity
  heuristic) suggests pets to follow, with an owner opt-out and web/mobile
  notification UI wiring.
- Story music overlay: admin-curated royalty-free track catalog
  (/p/[secret]/music), attachable to stories on web (StoryForm/StoryViewer),
  with mute-original support for video stories. Mobile UI is deferred to
  the next native build (needs an audio-playback module) — tracked in
  mobile/PENDING-NATIVE-BUILD.md.
- Auth UX fixes: sign-in/sign-up now use KeyboardAvoidingView + ScrollView
  and a shared PasswordField with a show/hide toggle (mobile). Web's
  ActivePetContext gained clearGactivePetId(), wired to sign-out via a new
  SignOutCleanup component, fixing a false "you do not have permission to
  access this pet" error after switching accounts on the same browser.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 21:02:49 +02:00
admin bad24e6463 fix(mobile): sign-up link overflow, pre-login language switcher; set up EAS Update (1.0.2)
- Sign-up's Terms/Privacy links overflowed the screen width — they
  reused onboarding's long descriptive sentences ("Alle Details in
  unseren Nutzungsbedingungen") in a compact side-by-side row with no
  wrap. Switched to the existing short pets.legalTerms/legalPrivacy
  labels and added flexWrap as a safety net.
- Neither sign-in nor sign-up let you pick a language before logging
  in — extracted pets.tsx's language toggle into a shared
  LanguageSwitcher component and added it to both auth screens.
- Set up expo-updates (Gitea #35) via `eas update:configure`:
  runtimeVersion policy "appVersion", per-profile update channels in
  eas.json (development/preview/production). Future JS-only fixes can
  now ship via `eas update` without a new Play Store review — this
  build itself still needs a full native build since expo-updates is
  a native module. Manually removed a duplicate RECORD_AUDIO
  permission entry that update:configure's app.json rewrite introduced.
- Bumped to 1.0.2 (versionCode 3).
2026-09-08 06:34:50 +02:00
admin df4d5ccec2 fix(mobile): video-loop, breed/birthday edit, avatar zoom, milestone dark mode
- Videos kept looping forever after scrolling off-screen (expo-video has
  no visibility awareness). New useVisiblePostIds hook tracks FlatList
  viewability and pauses VideoPlayer once a post scrolls away, wired
  into both the feed and the pet-profile post list.
- Pet edit screen couldn't change breed or set birthday/adoption date,
  even though the backend (pets.update) already supported all three —
  web already used them. Extracted BreedPicker out of PetCreateForm
  into a shared component, added it plus TT-MM-JJJJ date fields (same
  validation pattern as the health forms) to edit.tsx. Its BreedPicker
  nests a ScrollView, so edit.tsx's outer ScrollView was swapped for a
  FlatList to avoid the same nested-scroll gesture conflict fixed
  earlier for onboarding/pet.tsx.
- Pet profile's own avatar was a plain, non-tappable Image. Tapping it
  now opens the existing /image-viewer (same pinch-to-zoom viewer post
  images already use) when the pet has an avatar set.
- milestones.tsx and milestone-new.tsx still imported the static
  light-only colors/typography tokens instead of useTheme() — switched
  to the established makeStyles(colors) pattern.
2026-09-07 21:33:23 +02:00
admin f700000e87 fix(mobile): dark mode + nested-scroll fix in pet creation, invalidate pets.list after create
- PetCreateForm.tsx and onboarding/pet.tsx used the static light-only
  colors/typography tokens instead of useTheme() — pet creation stayed
  light even in dark mode.
- onboarding/pet.tsx wrapped its steps in a plain ScrollView; the breed
  OptionPicker nests its own vertical ScrollView, and two same-axis
  ScrollViews swallow the inner one's scroll gesture on Android even
  with nestedScrollEnabled. Switched the outer container to a FlatList
  (empty data + ListHeaderComponent), matching explore.tsx's already
  working fix for the same component.
- pets.create's mutation never invalidated trpc.pets.list, so
  ActivePetContext kept seeing zero pets after the first pet was
  created — OnboardingRedirect then bounced the user straight back to
  /onboarding/pet after the avatar step, with only an app restart
  breaking the loop. Added the missing invalidateQueries call.
2026-09-07 21:16:31 +02:00
admin c94df5fc6b fix(mobile): sign-up stuck on "missing_requirements" — no way to create an account
New sign-ups got stuck after entering the email verification code:
Clerk's status stayed on "missing_requirements" (surfaced via the
generic auth.signUpIncompleteError as "Sign-up incomplete") instead of
"complete", with no way to proceed. Checked this Clerk instance's
public /v1/environment config directly: first_name and last_name are
both required, and sign_up.legal_consent_enabled is true — web's
prebuilt <SignUp> component discovers and renders these automatically,
but this hand-built native form only ever collected email + password.

Added first/last name fields and a legal-consent checkbox (linking to
the actual Terms/Privacy pages), passed as firstName/lastName/
legalAccepted in the same signUp.password() call (Clerk's Futures API
accepts all three there — no separate .update() step needed). Submit
is disabled until all fields are filled and consent is checked.

This blocked 100% of new mobile sign-ups until fixed.
2026-09-07 21:03:54 +02:00
admin 4758ce8d87 feat: add Child Safety Standards page for Google Play compliance
New public page at /kinderschutz — required by Google Play's Child
Safety Standards policy for apps with user-generated content/social
features (https://support.google.com/googleplay/android-developer/answer/14747720).
Registered as public + maintenance-exempt in proxy.ts, matching the
existing /impressum, /datenschutz, /nutzungsbedingungen pattern.

Content covers only mechanisms that actually exist in this codebase:
in-app reporting (ReportSheet + admin moderation review), a named
responsible contact (same identity as /impressum), and the German/EU
legal framework (StGB §184b/c, DSA, JuSchG) for law-enforcement
cooperation on confirmed CSAE content — no invented automated-
detection claims.

Cross-linked from the other legal pages' footers, the web /pets
footer, and the mobile app's My Pets legal-links section (new
pets.legalChildSafety i18n key, DE/EN).
2026-09-07 18:57:49 +02:00
admin 768d0dda23 fix(mobile): dark mode for Messages, safe-area padding for input bars
Messages (conversation list + chat detail) used the static (light-only)
color tokens instead of useTheme() — same bug class as the earlier
auth-screen/comments/RepostCard fixes this session.

Also fixed: the comment and chat input rows sat flush against the
bottom edge with a flat 12px padding, no bottom safe-area inset. On
devices with Android's gesture nav bar this put the send button right
where the system back/home gesture also listens, making it error-prone
to tap ("in einem Schutzbereich sehr schwer anklickbar"). Both input
rows now pad by max(12, insets.bottom) instead of a flat 12px.
2026-09-07 05:42:52 +02:00
admin 04a193b8dd fix(mobile): fix EAS build failure — expo doctor schema/version errors
Build #4 failed at the expo doctor pre-build check EAS runs, with two
real issues:

- app.json's newArchEnabled key is no longer a valid Expo config field
  in SDK 57 (New Architecture is the only architecture now, so the
  opt-in toggle was removed) — the schema validator rejected it as an
  unknown property.
- expo-router was pinned to 5.1.11, an old-style version number
  predating Expo's SDK-aligned versioning — way behind the ~57.0.19
  expected for this SDK. expo-linking/expo-secure-store were a patch
  version behind too. Fixed via `npx expo install --fix` rather than
  guessing version numbers.

The expo-router bump changed tabBarIcon's expected color prop type
from `string` to RN's `ColorValue`; updated NotificationsTabIcon's
signature to match.

`npx expo-doctor` now reports 18/18 checks passed.
2026-09-06 21:47:18 +02:00
admin 6f83f47186 fix(mobile): fix Feed crash — hook called after conditional early return
FeedScreen's feedItems useMemo (interleaveTrending) sat after the
loading/error early returns instead of before them, violating the
Rules of Hooks: the loading-state render calls fewer hooks than the
loaded-state render, so React throws "Rendered fewer hooks than
expected" the moment the feed finishes loading — crashing the app on
every real device right after login. Moved the useMemo (and the plain
posts/trendingPosts derivations it depends on) above both early
returns so every render calls the same hooks in the same order.

Also add graphify-out to the root .easignore.
2026-09-06 21:39:28 +02:00
admin c04b69a11f fix(mobile): dark mode on auth screens + RepostCard, expand .easignore
Sign-in/sign-up and RepostCard used the static (light-only) color
tokens instead of useTheme() — in dark mode the login screen kept a
light background with light-theme-colored text baked in, and
RepostCard's text color was hardcoded independent of its (already
theme-aware) surrounding background, both landing on illegible
low-contrast text. Same bug class as earlier dark-mode fixes this
session, converted the same way (useTheme() + makeStyles(colors)).

Also add a root-level .easignore (EAS Build's archive root may resolve
above mobile/ for monorepo purposes) excluding backend/tooling
directories the mobile app never needs at build or runtime (.claude,
.impeccable, .planning, docker, prisma — the last is only a type-only
dependency via mobile's tRPC router import, erased before Metro
bundles anything).
2026-09-06 21:10:49 +02:00
admin 3d196b1528 fix(mobile): notification post link and actor-name link, comments dark mode
Notification taps for post-related types (REACTION/COMMENT/MENTION/
PAW_BACK) landed on a bare "no comments yet" screen with no sign of the
actual post. The comments screen now fetches the post via the existing
posts.getById and shows it (image/caption/header) via the shared
PostCard above the comment list.

The actor's name in each notification row is now its own tappable text
run that links to their profile, separate from tapping the rest of the
row (which still opens the notification's target) — ported from web's
notifications page, which does the same split between a Link and plain
text.

Also fixed: the comments screen used the static (light-only) color
tokens throughout, so embedding the theme-aware PostCard produced
near-invisible light text on its own light background in dark mode.
Converted the screen to useTheme() like the rest of the dark-mode-
converted screens.
2026-09-06 19:56:48 +02:00
admin 73ea97ac16 fix(mobile): notification separators/navigation, own-post delete option
Notifications: add a visible hairline separator between rows and make
each row tappable — it marks read and navigates to the relevant post
(comments screen) or pet profile, porting web's getHref()/getPostId()
routing logic.

Feed: the "..." menu on a post now opens a delete confirmation instead
of the report sheet when the post belongs to the currently active pet;
report sheet stays unchanged for posts from other pets. Reuses the
pre-existing posts.delete mutation, no backend changes needed.
2026-09-06 19:39:52 +02:00
admin 6b01046ded feat(mobile): mix trending posts into the Feed, update app icon assets
- explore.getTrending posts are now interleaved into the chronological
  Feed at semi-random intervals (src/lib/interleave-trending.ts),
  marked with a small "Trending" badge on PostCard — a lightweight
  stand-in for web's separate Explore "Trending" tab, which mobile
  wasn't going to get as its own screen. No backend change: reuses the
  existing explore.getTrending procedure as-is.
- update app icon / splash / favicon / Android adaptive-icon assets
  (user-provided)
- README: correct the now-stale Dark Mode "not yet converted" list
  (Explore/Notifications/pet profile/health were converted in the
  found.md batch) and document the report/date-format/pinch-zoom/
  legal-links fixes
2026-09-06 19:12:39 +02:00
admin 4a35fce20b fix(mobile): found.md batch — reports, dark mode, health form fixes
- new report feature: a "..." button on every post opens a reason-picker
  sheet (reports.create), matching web's ReportSheet reasons/copy
- pet profile, edit, followers/following, and the whole health screen
  (incl. consent gate) converted to the makeStyles(colors) dark-mode
  pattern
- fixed the invisible back button on every pushed (app)/_layout.tsx
  screen: headerTintColor was never set, so the native-stack default
  back chevron kept its platform tint instead of the theme color —
  black-on-black in dark mode looked like a missing button entirely
- health forms (weight/vet visit/vaccine) now take dates as DD-MM-YYYY
  instead of the backend's raw YYYY-MM-DD, converting internally
  (src/lib/date-format.ts); invalid dates now show an error instead of
  the mutation silently failing with no onError handler at all — this,
  not persistence, was the actual "Gesundheitsdaten werden nicht
  gespeichert" bug
- pinch-to-zoom on the full-screen image viewer via a plain
  PanResponder (no new native dependency)
- Pets tab: language switcher now uses flag emoji (🇩🇪/🇬🇧/🌐), and a
  new footer section links Impressum/Datenschutz/Nutzungsbedingungen
  plus the installed app version

All live-verified against the deployed backend except pinch-zoom
(two-finger gestures aren't simulatable over adb).
2026-09-06 18:55:12 +02:00
admin 462f295caa feat(mobile): add German/English i18n across the whole app
- i18next + react-i18next + expo-localization: device-locale detection,
  an MMKV-persisted DE/EN/Automatisch override (mirrors web's cookie-only
  locale, no backend field needed), and a language switcher on the Pets
  tab next to the theme toggle
- every screen and shared component converted from hardcoded German
  strings to translation keys; de.json/en.json kept in exact key-parity
  (218 keys each, verified programmatically)
- backend: src/trpc/init.ts now accepts an x-locale header as a fallback
  locale source (cookie still wins, web unaffected) since mobile has no
  cookie jar to set NEXT_LOCALE in; mobile's tRPC client sends the
  current language on every request
- fixes a latent bug: species/breed pickers were hardcoded to locale
  "de" while every other pet query resolved names via ctx.locale's
  default (English, since mobile never sent a locale cookie) — both now
  use the same current-language value
- expo-localization is a new native module (config plugin) and needs a
  native rebuild to actually link; deviceLocale() falls back to English
  instead of crashing when it isn't linked yet, so the app stays usable
  before that rebuild happens

Needs a NAS deploy (src/trpc/init.ts is shared backend code) before the
x-locale fallback and the species/breed locale fix take effect for mobile.
2026-09-06 17:31:41 +02:00
admin 0c6421c8de fix(mobile): dark mode on Explore/Notifications, breed dropdown overlay
- Explore and Notifications tabs now read colors from ThemeContext
  instead of the static light-only tokens
- OptionPicker's option list is position:absolute so it overlays
  following content instead of pushing it down, with zIndex on the
  component root (and FlatList's ListHeaderComponentStyle on Explore)
  so it actually paints above sibling content instead of underneath it
- check off found.md's resolved items, record the three newly reported ones
2026-09-06 16:30:02 +02:00
admin 0408be2063 feat(mobile): add onboarding flow, dark mode, and bugfixes
- 5-step onboarding (guidelines, upload consent, spider opt-in, pet
  creation, avatar) that gates first launch until a pet exists
- Add/delete pet flows on the Pets tab, with a shared OptionPicker
  (ScrollView-based, not FlatList, to avoid nested-VirtualizedList
  warnings inside onboarding's outer ScrollView)
- App-wide light/dark/auto theme via ThemeContext, synced with the
  existing owner.getThemePreference/setThemePreference backend
  procedures; core navigation chrome, Feed, Pets tab, and shared
  primitives (Button, TextField, Card, PetAvatar, PostCard, StoryTray)
  converted to the makeStyles(colors) pattern
- found.md fixes: safe-area insets on image/story viewers so controls
  clear the status bar, auto-mark-all-read on the notifications tab,
  and an Explore species/breed filter
2026-09-06 16:14:44 +02:00
adminandClaude Sonnet 5 330cfdeac2 feat(mobile): add sign-up screen
sign-in.tsx only ever covered login (per its own comment "sign-up screens
are follow-up work"). Adds the other half: email/password sign-up using
Clerk's Futures API (signUp.password() -> verifications.sendEmailCode()/
verifyEmailCode() -> finalize()), mirroring sign-in.tsx's two-step
credentials/verify shape. No invite-code step, since INVITE_REQUIRED is
globally off on the backend since launch.

Verified live on the Android emulator: real Clerk validation surfaces
correctly (breached-password rejection), and a valid submission correctly
sends a verification email and transitions to the code-entry step.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 14:49:13 +02:00
adminandClaude Sonnet 5 a3f19ff70f feat(mobile): add health tracking, milestones, messaging, and push registration
Health: consent gate (Art. 9 DSGVO) plus weight/vet-visit/vaccine log
sections, gated behind health.getConsentStatus like the web app.
Milestones: view a pet's milestone posts and create the 5 user-initiated
types with an optional photo. Messaging: chat detail/compose screen
(messages/[conversationId].tsx) polling while focused, reachable from the
conversations list and from a pet profile's new "Nachricht" entry point.
Push: usePushNotifications requests permission and registers the Expo
push token with owner.registerPushToken; real delivery still needs an EAS
project id (`eas init`), tracked in the README.

All four verified live on a real Android emulator: health CRUD (add/list/
delete a weight log), Health/Milestones/Nachricht entry points gated
correctly by own-pet vs. other-pet, and full chat history loading via an
existing conversation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 09:50:07 +02:00
adminandClaude Sonnet 5 eda6d3b801 feat(mobile): add stories, video playback, and search
Story tray/creation/viewer with PawRing seen/unseen indicator, expo-video
playback for VIDEO posts (replacing the static placeholder), and a
pets/people/hashtags search screen reachable from the Explore tab header.
Extracts FollowButton from the pet-profile screen since search results
need the same follow/unfollow toggle.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 08:53:07 +02:00
admin 085d2abb53 feat(mobile): add post creation and comments
Post creation: gallery/camera picker (expo-image-picker) uploads each
image straight to Supabase Storage via a presigned PUT
(posts.getPresignedUrl + expo-file-system), then posts.create with the
resulting storage keys — reachable via a new "+" header button on the
Feed tab. No client-side blurhash (browser Canvas API, no RN
equivalent) or AI-disclosure/mention support yet, matching the rest of
mobile's plain-text captions.

Comments: a dedicated screen (posts/[postId]/comments) for reading,
posting, and deleting one's own comments, reachable by tapping the
comment icon on a PostCard. Ported from web's PostDetailDialog comment
panel, scoped down to just that panel since mobile's image full-view
already has its own route.
2026-09-05 15:35:32 +02:00
admin c1067b3245 feat(mobile): add header wordmark and full-screen image viewer
Header now shows the PawFeed paw+text logo on the Feed tab (matching
web's Sidebar wordmark). Tapping a post image opens a full-screen
viewer as a route-based fullScreenModal, with a blurred edge-to-edge
copy of the same photo behind the sharp foreground image instead of a
flat black backdrop, for a more dynamic Photos-app-style lightbox.
2026-09-05 14:53:13 +02:00
admin a737a17976 fix(mobile): feed separation, adaptive launcher icon; feat: full pet profile
Fixes from user testing on a real device:
- Feed: replaced an invisible spacing-only separator with an actual
  hairline divider between posts.
- Android launcher icon showed Expo's default placeholder (a blue arrow)
  because the app icon and the Android adaptive-icon layers
  (foreground/background/monochrome) are two separate asset sets — only
  icon.png/favicon.png had been replaced. Added scripts/derive-adaptive-
  icon.js to crop the paw glyph out of icon.png (detected by its marigold
  color) and generate all three adaptive-icon layers plus the splash icon;
  this is an explicit placeholder per user's own call, not final art.

New: full pet profile (app/(app)/pets/[petId]/), matching web's
src/app/(app)/pets/[petId]/page.tsx:
- View: identity, species, bio/adoption story, follower/following counts
  (tappable → list screens), the pet's own posts below.
- Follow/unfollow with real initial state (follows.isFollowing) and
  optimistic toggle — unlike feed reactions, this endpoint actually
  exists, so no need to fake an initial state.
- Edit screen (name/nickname/bio/adoption story via pets.update) for the
  owner's own pets.
- Tapping a pet (feed header, Explore card, Pets-tab row) now opens this
  profile instead of nowhere; the Pets tab's active-pet switch is now a
  separate checkmark control instead of overloading the whole row tap.

PostCard's post-shape prop is now structural rather than tied to
feed.getFeed's output type, since posts.byPetId selects `pet` via the
narrower petIdentitySelect (no isVerified/owner) — both need to satisfy
the same component.

Verified end-to-end on-device: feed separator visible, own-pet profile
shows a working Bearbeiten button whose form is correctly prefilled (a
stray edit was made to Bianca's nickname field by mistake during testing
and was discarded via back-navigation without saving — never sent to the
server), someone else's pet profile shows a working Follow/Unfollow
toggle, followers list navigates correctly, and the home-screen launcher
icon now shows the paw instead of the default Expo icon.
2026-09-05 13:47:20 +02:00
admin c35d4cda46 feat(mobile): Phase 1 — real Feed/Explore/Notifications/Pets tabs
Moves the mobile app from a connectivity scaffold to a real Phase 1 app:

- Migrate off Expo Go onto a local custom dev client (expo-dev-client,
  built via expo prebuild + expo run:android against the local Android
  SDK — no EAS account needed). Needed because react-native-mmkv requires
  native code Expo Go doesn't ship.
- Persisted, MMKV-backed TanStack Query cache (PersistQueryClientProvider)
  plus expo-image, so a relaunch renders the last-known feed/pets/
  notifications instantly instead of a blank screen while refetching.
- ActivePetContext: every pet-scoped procedure requires an explicit petId
  server-side (no server-side "current pet"), ported from web's
  ActivePetContext, MMKV-backed instead of localStorage.
- Bottom Tabs (Feed/Explore/Notifications/Pets) mirroring the web app's
  actual mobile nav, with Messages as a header button rather than a tab
  (matches web). Feed, PostCard, PawButton reactions, pet discovery grid,
  notifications list, and a conversations list are all wired to the real
  backend and verified against the live database.
- Design tokens/components extended with PostCard/PawButton/PetAvatar/
  PetIdentityBadges, all styled from the existing src/theme/tokens.ts.
- Fixed a react/react-dom version mismatch surfaced along the way, and
  handle Clerk's needs_client_trust (Device Trust) sign-in status with an
  email-code step.

Verified end-to-end in the Android emulator against pawfeed.org: feed
scrolls and paginates with real posts/images, paw reactions toggle
optimistically and persist, pet switching updates every screen, and
notifications/explore show live data.
2026-09-05 13:19:01 +02:00
admin dc344f1188 feat(mobile): match PawFeed web design system, fix Clerk device trust
- Add design tokens (colors, radii, typography) mirroring DESIGN.md's
  "Spotlight Porch" system, plus Button/TextField/Card components built
  on them. Restyle the sign-in and placeholder home screens to match.
- Load Inter via @expo-google-fonts/inter, gate rendering on fonts +
  Clerk being ready instead of a blank screen.
- Pin react-dom to match react's version — Metro/Hermes hard-require
  exact equality, and react-dom wasn't pinned anywhere before.
- Handle Clerk's `needs_client_trust` status (Device Trust, tied to the
  dashboard's Native Applications API): send/verify an email code
  before finalizing sign-in.

Verified end-to-end in the Android emulator against the live backend:
sign-in, device-trust code verification, and the home screen showing
real stats.getPublicStats data.
2026-09-05 11:51:17 +02:00
admin 9eb31c3323 feat(mobile): add Expo/React Native scaffold for PawFeed Android app
Connectivity + auth scaffold only: Expo Router, Clerk session auth via
expo-secure-store, and a type-only tRPC AppRouter import from the web
app's router — no backend changes needed. See mobile/README.md.
2026-09-05 11:01:49 +02:00