424 Commits

Author SHA1 Message Date
chahinebrini
80165c851c chore: NEXT_RELEASE.md — session 2026-06-01 release notes 2026-06-01 11:10:44 +02:00
chahinebrini
585cb73947 feat(coach): voice bar silence/speech detection + trash flash + timer fix
- VoiceBars: active=false → kleine Punkte (Stille), active=true → animierte
  Bars (Sprechen). Übergang fließend via Animated.timing.
- Metering via isMeteringEnabled:true + getStatusAsync() alle 200ms.
  audioLevel (0-1) aus dBFS normalisiert. Threshold >0.1 = Sprechen.
- Trash-Button: 400ms roter Flash (backgroundColor + Icon-Farbe) beim
  Klick bevor Recording verschwindet — wie Instagram.
- Timer: Date.now()-basiert statt Increment → kein Android-setInterval-Jitter.
- VoiceBars volle Breite via flex:1 + justifyContent:space-evenly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 10:54:20 +02:00
chahinebrini
7db32ca606 feat(coach): Instagram-style voice recording bar
Beim Aufnehmen ersetzt jetzt eine volle Pill-Bar die Eingabe:
  - Links: Trash-Icon (neutral, dezent)
  - Mitte: Live-Dot (brandOrange) + animierte Waveform-Bars + Timer
  - Rechts: Senden-Button (brandOrange, Pfeil-Icon)

Keine roten Farben mehr, kein separater Mic-Button beim Aufnehmen.
Mic-Button verschwindet komplett während Recording (erst wieder
sichtbar wenn aufgehört). Konsistent mit Rebreak-Farbschema.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 10:47:08 +02:00
chahinebrini
bd8d5a3072 fix(dm): Android scroll-to-bottom via scrollToOffset(999999)
scrollToEnd() unterschätzt Content-Höhe auf Android und stoppt
konsistent eine Message zu früh (verifiziert per adb-Screenshot).
scrollToOffset({offset:999999}) wird auf den echten Max-Wert geclampt
und landet immer am absoluten Ende der Liste.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 10:42:44 +02:00
chahinebrini
89391a807b fix: Arabic STT + DM scroll + info sheet FormSheet
STT (Arabic):
- Deepgram hat nova-2-general ar/tr-Support eingestellt (400 Bad Request)
- Fix: einheitlich nova-3 für alle Sprachen inkl. ar/tr
- Stale Kommentar aus 2026-05-30 entfernt

DM scroll-to-bottom:
- onLayout auf FlatList hinzugefügt → zusätzlicher scrollToEnd nach
  initialem Layout-Pass (Android-specific race condition)
- onOpenImage im FlatList-Renderer auf Lightbox verdrahtet (war () => {})

Info-Sheet:
- Modal(pageSheet) → FormSheet mit initialHeightPct={0.85}
- Nutzt jetzt unsere eigene Sheet-Komponente konsistent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 10:25:35 +02:00
chahinebrini
0533fcad71 fix(mail): classifier v1.2 — FS-token + extreme-percent + casino-name + threshold 40
- SUBJECT_PERCENT_PATTERN weight 10 → 15
- SCORE_BLOCK_MIDRANGE 50 → 40
- new FS-token pattern (+20): \b\d+\s*FS\b (free-spins abbreviation)
- new extreme-percent pattern (+20): \b[1-9]\d{2,}\s*% (>=100% signals gambling)
- casino in sender display-name (+30): targeted v1.2 reactivation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 10:17:25 +02:00
chahinebrini
f59d1800c7 feat(dm): info sheet + avatar→profile + scroll fix + image cache
- Header: Avatar-Tap navigiert direkt zum Profil des Chatpartners
- Header: Info-Icon (ℹ) rechts öffnet neues Info-Sheet
- Header: kein separater BG mehr — blendet in native Background ein
- Info-Sheet (pageSheet Modal):
    - Partner-Karte mit Avatar + "Profil anzeigen"-Link
    - Geteilte Medien als 3-Spalten-Grid (neueste zuerst)
    - Tap auf Bild → Lightbox-Modal (Vollbild mit Close-Button)
- Scroll-to-bottom: 3-stufiges Timing (rAF + 100ms + 300ms) für
  zuverlässiges Bottom-Scroll auch wenn Bilder nachgeladen werden
- expo-image cachePolicy="memory-disk" überall: ChatBubble-Image +
  alle Images im Info-Sheet + Lightbox
- i18n: dm.view_profile / dm.shared_media / dm.no_shared_media (DE/EN/FR/AR)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 10:00:03 +02:00
chahinebrini
24044c3a0c feat(backend): mail_classification_samples row-cap (100k max)
Tabelle war auf 13GB gewachsen und hat heute den Disk voll gemacht.
Neuer täglicher Row-Cap-Job hält die Tabelle unter 100k Rows —
löscht älteste Samples wenn Cap überschritten. CTE-basierter Delete
nutzt created_at-Index, kein Full-Table-Scan.

Bestehende Jobs bleiben: Subject-Nullification (30 Tage) + Sample-Purge
(12 Monate). Row-Cap ist die harte Schranke gegen Disk-Wachstum.
100k Rows ≈ ~500MB — nachhaltig für Staging + Prod.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 09:44:55 +02:00
chahinebrini
53d8ace974 chore(native): strings fix + app.config + plist updates
- strings.xml: accessibility_service_summary = "ReBreak — Schutz"
  (war durch Linter mehrfach zurückgesetzt worden)
- app.config.ts + Info.plist: version bumps
- CHANGELOG.md + deploy-runtimes: release-tracking aktualisiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 09:32:41 +02:00
chahinebrini
372aaa43dd fix(ci): pipeline race-condition + health-check retry + maestro secrets
Hauptproblem: Webhook-Deploy (deploy.sh) und GH-Actions-Deploy
(deploy-from-artifact.sh) liefen gleichzeitig → Race auf .output-staging
und doppelter pm2-restart.

Fixes:
- deploy-from-artifact.sh: setzt .deploy-ga.lock (noclobber, mit PID)
  während Deploy läuft; stale locks werden erkannt und überschrieben
- deploy.sh: prüft .deploy-ga.lock bei Start — wenn GH-Actions aktiv,
  sauberes exit 0 statt Kollision
- Health-Check: Retry-Loop (12× × 5s = max 60s) statt einmaligem sleep 5;
  Infisical-Login + Nitro-Start braucht auf gestresstem Server bis 30s
- maestro-cloud.yml: ungültiges `if: secrets.X != ''` entfernt (secrets
  in if-conditions sind in GH-Actions immer leer); stattdessen expliziter
  secrets-check als erster Step mit klarer Fehlermeldung
- pnpm --prefer-offline in deploy-from-artifact.sh: nutzt Store-Cache
- .gitignore: .deploy-ga.lock ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 09:32:25 +02:00
chahinebrini
617312f367 fix(vpn): bypass own domains in DNS filter (rebreak.org, rebreak.app)
OAuth-Callbacks gehen an db-staging.rebreak.org — wenn der In-Flight-Cap
kurz erreicht wird, kriegt das SERVFAIL statt einer echten Antwort.
Eigene Infrastruktur-Domains explizit als Bypass deklariert: werden nie
aus der Blocklist geblockt und umgehen den In-Flight-Zähler nicht
(Forward läuft weiterhin normal, aber Block-Entscheidung wird übersprungen).

Gilt für iOS (PacketTunnelProvider) und Android (DnsFilter) gleichzeitig.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 05:12:50 +02:00
chahinebrini
b1d382bada testflight 2026-06-01 04:44:19 +02:00
chahinebrini
e227eb0407 chore: trigger deploy after screentime migration fix 2026-06-01 04:38:53 +02:00
chahinebrini
c39dfcbf8b fix(migration): screentime_passcode in korrektes Prisma-Migration-Format
Loose .sql Datei → 20260601_add_screentime_passcode/migration.sql
(prisma migrate deploy erwartet Verzeichnis-Format)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:34:34 +02:00
chahinebrini
d65ba84eb1 feat(binder): MDMClient, EnrollView improvements + supervise flow_backup
- MDMClient: error handling verbessert
- SuperviseRunner: robustere EOF-nach-Success Erkennung
- EnrollView: Enrollment-Status-Polling, Retry-Logik
- SuperviseView: UX-Verbesserungen
- ConfigureView: minor cleanup
- flow_backup.go: backup flow für supervise-magic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:30:28 +02:00
chahinebrini
db0aa6d24e feat(native): Protection Onboarding v2 + Devices + ProtectionSlide
- ProtectionOnboardingSheet: Android a11y 2-step flow mit tamper-arm nach Return
- ProtectionDetailsSheet: cleanup, iOS/Android split, locked-state logic
- ProtectionSlide: neuer Onboarding-Slide für Protection-Intro
- _layout.tsx: reconcileVpn on app-foreground (Android VPN self-heal)
- devices.tsx: Two-device approval flow
- useProtectionState: applyCooldownDisableIfElapsed, forceDisable on cooldown-end
- iOS module Info.plist: bundle version bumps
- app.config.ts: minor config updates
- tmp/.deploy-runtimes: build-time metrics aktualisiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:30:20 +02:00
chahinebrini
adf0d33f1b feat(android): Protection Module v2 — VPN self-heal, boot-receiver, multilang
- RebreakProtectionModule: reconcileVpn, Samsung overlay guide, openPowerDialog,
  armTamperLock preconditions (VPN+a11y required), openAccessibilitySettings fallback chain
- RebreakVpnService: onRevoke auto-recover, blocklist self-heal on empty start,
  ACTION_RESTART für hard-reload nach blocklist-Änderung
- VpnBootReceiver (neu): startet VPN nach Geräte-Neustart wenn filter_enabled=true
- Strings: DE/EN/FR/AR a11y-Guide, Overlay-Permission, Hint-Steps
- RebreakProtectionModule.ts: reconcileVpn, armTamperLock, disarmTamperLock,
  openPowerDialog, openAccessibilitySettings, dismissAccessibilityHint exports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:30:07 +02:00
chahinebrini
ab4b9c48e5 feat(ios): Screen Time Passcode als Layer 3 (setup flow)
User generiert 4-stelligen Code in der App, setzt ihn manuell als
Screen Time Passcode → ReBreak speichert ihn auf dem Backend.
Damit kann niemand Screen Time deaktivieren → deny-removal bleibt
aktiv → App nicht deinstallierbar ohne den Passcode.

Backend:
- Profile.screentimePasscode Feld (Migration add_screentime_passcode)
- POST /api/protection/screentime-passcode — Code speichern
- GET /api/protection/screentime-passcode — Code abrufen (nach Cooldown)

iOS UI (blocker.tsx):
- ScreentimePasscodeCard erscheint wenn Layer 1 + 2 aktiv (iOS only)
- Code-Generierung → Einmal-Anzeige → Deep-Link zu Settings → Screen Time
- Bestätigung speichert Code auf Backend, Card zeigt Confirmed-State

Locales: DE/EN/FR/AR screentime_* Keys

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:19:43 +02:00
chahinebrini
59766f8530 fix(android): block Force Stop + App-Info auf Samsung OneUI
Samsung nutzt generische Klassen (SubSettings, FrameLayout) statt
InstalledAppDetails für die App-Info-Seite — classGuard schlug fehl,
Tamper-Lock griff nicht.

Fixes:
- classGuard für Uninstall-Pfad entfernt: Text-Kombination "rebreak"
  + Deinstall-Keyword reicht (App-Liste zeigt Buttons nie inline)
- Force-Stop-Bestätigungsdialog explizit erkannt via "stopp erzwingen"
  + "abbrechen" + "ok" (Dialog nennt App-Namen nie)
- Throttle-Reset bei TYPE_WINDOW_STATE_CHANGED: eliminiert 400ms-Fenster
  zwischen Activity-Wechsel und erstem CONTENT_CHANGED-Check
- ApplicationDetail (ohne 's') zu Pattern-Listen: Samsung OEM-Variante
- accessibility_service_summary korrigiert: "ReBreak — Schutz" statt
  "Sichert den Schutz gegen Abschalten ab" (HIGH_CONFIDENCE_KEYWORD muss matchen)

Getestet auf Samsung Galaxy A50 (One UI): App-Info-Seite wird sofort
per BACK-Action geblockt, Lyra-Toast erscheint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:11:01 +02:00
chahinebrini
2e056c7257 feat(devices): Apple-style two-device approval flow + email fallback
iCloud-Sign-In Pattern: wenn ein neues Gerät versucht sich anzumelden
und das Plan-Limit erreicht ist, kann der User auf einem bereits
angemeldeten Gerät bestätigen — Code wird auf BEIDEN Geräten gezeigt
für visuellen Vergleich (verhindert Code-Forwarding-Attacken).

Backend:
- New table device_approval_requests + supabase_realtime + RLS
- POST   /api/devices/approvals               — create (new device)
- GET    /api/devices/approvals               — list pending (existing devices)
- GET    /api/devices/approvals/:id           — status poll (new device)
- POST   /api/devices/approvals/:id/approve   — approve + atomic evict
- POST   /api/devices/approvals/:id/reject    — reject
- POST   /api/devices/approvals/:id/email     — trigger email fallback
- POST   /api/devices/approvals/email/:token  — magic-link approve (no auth)
- Email-Template via Resend (lyra-neutral, security-formal)
- 10min TTL, 6-digit numeric codes (crypto-random)

Frontend (rebreak-native):
- DeviceApprovalIncomingSheet — existing devices: code + device-picker + Allow/Reject
- DeviceApprovalPendingSheet  — new device: code + spinner + 'Send via email'
- useDeviceApprovalRealtime   — postgres_changes subscription
- DeviceLimitReachedSheet     — neues CTA 'Auf anderem Gerät bestätigen'
- i18n DE/EN/FR/AR

Migration läuft automatisch via prisma migrate deploy bei push.
2026-06-01 02:36:28 +02:00
chahinebrini
efca157969 fix(backend): device-mgmt cleanup + stats rejected fallback + realtime refresh
- devices: cleanupStaleDevices() purges phantoms >14d not bound; called from
  GET /api/devices + register limit-check. Deterministic sort
  (lastSeenAt, createdAt, id) so iPad+iPhone see identical order.
  Merge-cutoff tightened 30d -> 7d.
- stats: rejected aggregates from notifications(type='domain_rejected')
  via Math.max — admin reject cascade-deletes submission row.
- blocker.tsx: useDomainSubmissionRealtime triggers blockerStats.refresh()
  directly (not stale-check only) so info-sheet shows fresh rejected count.
2026-06-01 02:23:27 +02:00
chahinebrini
578abfe3bb chore(release): v0.3.13 build 46 / vc36 — DM scroll fix + chat timestamps weekday/days/weeks/months 2026-05-31 07:33:06 +02:00
chahinebrini
2715d2620b chore(release): ios buildNumber 44 → 45 (TestFlight + MDM deployed) 2026-05-31 03:42:23 +02:00
chahinebrini
acf14aaf11 chore(release): rebreak-native v0.3.13 build 44 (versionCode 35)
- Bump android versionCode 33 → 35 (build.gradle was out-of-sync with app.config.ts)
- Archive NEXT_RELEASE.md → CHANGELOG.md
- Released to Google Play Internal Track

Release notes (Build 44):
- DM-Chat: scrollt jetzt zuverlässig zur neuesten Nachricht
- Lyra-Sprachnachrichten: arabisch/türkisch antworten in richtiger Sprache (STT fix)
- DM Push-Notifications: Fehler-Logging hinzugefügt
2026-05-31 02:03:00 +02:00
chahinebrini
49e558902b fix(dm): always scroll to bottom on new messages and content-size changes
The smart isNearBottomRef gating was too restrictive — own sent messages,
image-loads, and incoming partner messages were sometimes not scrolled to.
Adopt the room-chat pattern: always scroll on messages.length change and
onContentSizeChange. Drop isNearBottomRef + firstContentSizeChangeRef +
onScroll handler.
2026-05-31 01:37:44 +02:00
chahinebrini
55e3cdfb26 fix(transcribe): pass language=ar/tr to nova-2-general so Lyra answers in correct language
Without explicit language param, nova-2-general falls back to multilingual
auto-detect and often misdetects arabic audio as english (phonetic transcript
'salam alaikum' instead of 'السلام عليكم'). detectLang() then sees only
latin chars and answers in english.

Confirmed via Deepgram docs: nova-2-general accepts language=ar and language=tr
(only nova-3 rejects them with HTTP 400).
2026-05-31 01:37:11 +02:00
chahinebrini
e0b4d9f530 fix(dm): add error handling and logging to DM push notifications 2026-05-31 01:32:34 +02:00
chahinebrini
3a4e1ecfba feat(coach): switch Lyra to Gemini 2.5 Flash Lite (Groq+OpenRouter quotas dead)
- Primary: gemini-2.5-flash-lite (~789ms TTFR, ~10x cheaper than Haiku, no reasoning overhead)
- Fallback 1: gemini-2.5-flash (smarter when Lite overloaded)
- Fallback 2: gpt-4o-mini (anchor on different provider)
- message.post.ts: candidates chain replaced
- sos-stream.get.ts: gemini-flash-lite default + auto-fallback to gpt-4o-mini if key missing
- nitro.config.ts: geminiApiKey runtimeConfig
- start-staging.sh: GEMINI_API_KEY export + NITRO_GEMINI_API_KEY

OpenRouter credits = 0, Groq TPD exhausted - users get 503 currently.
2026-05-31 01:07:10 +02:00
chahinebrini
487af4ede1 fix(coach): duplicate lastUserMsg declaration in message.post 2026-05-31 00:15:28 +02:00
chahinebrini
685782b538 fix(coach): dynamische Sprache (Text-Detection + App-Locale-Fallback)
LLM-Prompt (message.post + sos-stream):
- LANG_INSTRUCTIONS Map raus, ersetzt durch dynamische Instruktion
  'Reply in {detectedFromUser} ... fallback: {appLang}'
- Lyra matcht jetzt die Sprache der letzten User-Message (per
  detectLang Unicode-Detection); App-Locale ist nur noch Fallback
- Instruktion doppelt eingehängt (Anfang + Ende des System-Prompts)
  gegen recency bias bei langen deutschen Prompts

TTS (speak dispatcher + speak-cartesia + speak-elevenlabs):
- Kein 'de'-Default mehr für language. detectLang(text, locale) leitet
  Sprache primär aus dem Antwort-Text ab (Arabic/Cyrillic/CJK/Turkish-
  Letters), Locale als Fallback
- Cartesia + ElevenLabs: language/language_code nur senden wenn
  ableitbar, sonst Provider auto-detect statt erzwungenem 'de'
- speak-cartesia: sonic-2 → sonic-3 (Multi-Lang, war beim Dispatcher-
  Fix gestern vergessen worden)
- Google: en-US neutraler Fallback statt de-DE-Bias

Neu: server/utils/detect-lang.ts
2026-05-31 00:12:40 +02:00
chahinebrini
b956b3b1fc chore(ui): branded splash.png statt weißer icon.png-Box + Release-Notes
- app.config.ts splash: icon.png → splash.png (full-bleed Navy-Gradient, cover)
- NEXT_RELEASE.md: Blur-Menüs + neuer Splash ergänzt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:52:58 +02:00
chahinebrini
89775838bc fix(chat): gedrückte Bubble scharf über Blur + Emoji-Ring entfernt
- MessageActionMenu: scharfe Preview-Kopie der gedrückten Bubble am Anker
  (bleibt über dem Blur sichtbar, WhatsApp-Stil) statt mitgeblurrt
- Reaktions-Leiste: kein Ring/Hintergrund mehr, aktives Emoji nur leicht größer
- Reaction-Pills: plain Emoji + Count ohne Hintergrund/Border

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:44:41 +02:00
chahinebrini
f83a13ba60 chore(release): deploy.sh all-platform (MDM/TF/Android) + NEXT_RELEASE-Workflow
- deploy.sh: Android-Signing-Auto-Restore aus build-config/android-signing/
  (gitignored), getestet für MDM + TF + Android
- .gitignore: *.keystore + build-config/android-signing/
- NEXT_RELEASE.md wiederhergestellt mit Notes fürs WA-Chat-Popup + Comment-Likes
  (deploy.sh archiviert es in CHANGELOG + leert es danach)
- buildNumber 27→41, versionCode 18→31 (Release-State)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:34:57 +02:00
chahinebrini
2591b2a89c feat(chat): WhatsApp-Style Reaktions-/Aktions-Popup (DM) + Reaction-Pills
- MessageActionMenu: an der Bubble verankert (measureInWindow) statt zentriert,
  Blur-Backdrop, Emoji-Leiste oben (fremd) + Aktions-Liste unten (fremd:
  Antworten/Kopieren, eigen: Kopieren/Löschen)
- ChatBubble: Long-Press → measure + Menu, Reaction-Pills unter Bubble,
  Tombstone "Nachricht gelöscht"; ersetzt @expo-action-sheet
- dm.tsx: optimistisches Reaction-Toggle + Delete-Confirm + Realtime-Refetch
  (Reaction-Changes + Partner-Soft-Delete)
- useChatRealtime: DM-Hook lauscht zusätzlich auf reactions + message-UPDATE
- PostCommentsSheet: optimistisches Herz + Realtime-Subscription + größeres Icon
- i18n (de/en/fr/ar): chat.delete/message_deleted/delete_confirm_* + public_domain

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:18:51 +02:00
chahinebrini
69f01c5a0c feat(chat): DM-Reaktionen + Soft-Delete Backend + comment_likes realtime
- db/chat: getDmHistory liefert reactions + deletedAt; toggleDmReaction
  (WA-Toggle) + softDeleteDmMessage (nur eigene)
- Endpoints: /api/chat/reaction (Emoji-Toggle, 7er-Allowlist) +
  /api/chat/delete-message (Soft-Delete für alle)
- dm/[userId].get: aggregierte reactions + deleted im Response, Inhalt bei
  Tombstone geblankt
- Migration: comment_likes zur supabase_realtime-Publication (fixt
  Post-Kommentar-Like-Realtime, eskaliert von rebreak-native-ui)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:18:32 +02:00
chahinebrini
28887cfc49 feat(chat): schema + migration — Multi-Emoji-Reaktionen + Soft-Delete
- ChatMessageReaction + DirectMessageReaction (PK user+message, emoji-Spalte,
  1 Reaktion/User/Message = WhatsApp-Verhalten)
- deleted_at (Tombstone) auf chat_messages + direct_messages
- Realtime-Publication für beide Reaction-Tabellen
NICHT gepusht: Push triggert Auto-Migration auf Staging-DB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 10:56:39 +02:00
chahinebrini
4135e388ff ui(deploy): reorder progress bar so %, time stay visible on narrow terms
Old layout truncated the critical right-side (1m23s/~3m) on long labels.
New layout puts %, bar, and elapsed/eta on the LEFT, label+subtitle on
the RIGHT where truncation does no harm. Bar shrunk 20→15 chars.
2026-05-30 10:25:24 +02:00
chahinebrini
8f871611f1 feat(deploy): round ETA display (~8m statt ~8m00s)
Baseline aus tmp/.deploy-runtimes wird weiterhin per Run aktualisiert
(genauer Sekunden-Wert im Cache, nur Display gerundet).
2026-05-30 10:21:51 +02:00
chahinebrini
adc506291a fix(deploy): ERR-trap survives set -u (FUNCNAME may be unbound at top-level) 2026-05-30 10:16:11 +02:00
chahinebrini
6255006cad feat(deploy): clean Ctrl+C handler kills background xcodebuild
Without this, Ctrl+C would kill deploy.sh but leave xcodebuild
running as orphan, eating CPU and locking DerivedData.
2026-05-30 10:14:01 +02:00
chahinebrini
24a52a5bae fix(deploy): run_quiet works on bash 3.2 — drop subshell, toggle set -e around wait
- Background subshell '( cmd ) &' + 'wait' interacted badly with
  set -euo pipefail on macOS bash 3.2, killing the script silently
  before the progress-bar's error branch could run
- New approach: just 'cmd &' (no subshell), bracket the whole bg+wait
  region with 'set +e' / 'set -e', then check rc explicitly
- Also adds ERR-trap with call stack + RUN_QUIET_DEBUG=1 fallback
  (streams output directly via tee, useful for debugging build failures)
2026-05-30 10:12:10 +02:00
chahinebrini
9f8e99d287 fix(deploy): preserve rc under set -e (run_quiet swallowed errors silently)
'wait $pid' triggered 'set -e' before 'local rc=$?' could capture exit code,
so xcodebuild failures vanished without any log dump. Use 'wait $pid && rc=0 || rc=$?'
idiom to keep the script alive and let the error-dump branch run.
2026-05-30 10:05:10 +02:00
chahinebrini
061bd2d799 fix(deploy): allow flags before subcommand (./deploy.sh --skip-pods)
Treat $1 as subcommand only if it doesn't start with '-', else default to 'all'.
2026-05-30 10:03:11 +02:00
chahinebrini
77407f9d63 feat(deploy): --skip-pods flag + quiet clean-ios
- clean-ios.sh: new --skip-pods (or SKIP_PODS=1) skips prebuild+pod install
  (use when Pods are already fresh — saves ~30s)
- clean-ios.sh: new --quiet (or REBREAK_QUIET=1) suppresses end-of-run
  'Nächste Schritte' tips (cluttered deploy output)
- deploy.sh: new --skip-pods flag, auto-passes --quiet to clean-ios.sh
2026-05-30 10:01:41 +02:00
chahinebrini
b15ee42a85 fix(deploy): subtitle fallback + realistic runtime seeds
- subtitle now falls back to last non-empty log line when no Compile/Build
  action matched yet (so user sees activity during xcodebuild setup phase
  instead of empty bar at 0%)
- realistic seeds: 22min → 8min for xcarchive (typical RN archive)
2026-05-30 09:55:53 +02:00
chahinebrini
e6e1bab35a fix(deploy): single-line progress bar (no fragile cursor moves)
- previous 2-line render with \033[1A broke when xcodebuild crashed
  early — left mangled output and lost stderr
- new render_progress: ONE line, truncated to $COLUMNS, no cursor moves
- indeterminate mode (no baseline) shows ping-pong bar instead of spinner
- removes 2-line reserve + clear logic in run_quiet
2026-05-30 09:51:49 +02:00
chahinebrini
32d270ccad feat(deploy): brew-style time-based progress bar with runtime-cache
- new render_progress() draws ████████░░░░░░░░ 42% (1m23s / ~3m18s) bar
- runtime_lookup/save persist step durations in tmp/.deploy-runtimes
  (gitignored — auto-learns from successful runs)
- first run = spinner mode (no baseline yet), subsequent runs show real %
- still shows live xcodebuild action (Compiling X.swift) as subtitle
- format_duration helper: 45s / 1m23s readable output
2026-05-30 09:47:48 +02:00
chahinebrini
f48df2a968 chore(deploy): require ASC API-Key, drop app-specific-password fallback, brew-style spinner with live build action
- removes APPLE_APP_SPECIFIC_PASSWORD legacy branches (it never worked for xcodebuild -exportArchive anyway, only altool-upload)
- ASC API-Key now hard-required via require_asc_api_key preflight (fails fast with clear msg + path hint)
- run_quiet: spinner now tails the build log and shows current action (Compiling X.swift, Linking, CodeSign, etc.) as live subtitle — feels like brew/homebrew progress
- .env.deploy.local.example: drop unused fallback section
2026-05-30 09:46:38 +02:00
chahinebrini
b029c00413 chore(deploy): persist iOS auth via .env.deploy.local + ASC API-Key
- deploy.sh auto-sources apps/rebreak-native/.env.deploy.local (gitignored)
  and ~/.config/rebreak/deploy.env as fallback
- new helper xcodebuild_auth_args() injects -allowProvisioningUpdates +
  -authenticationKeyPath/ID/IssuerID into archive + both exportArchive calls
- ASC API-Key (free, .p8 from appstoreconnect.apple.com) is now the
  required path for exportArchive — app-specific-password no longer works
  for export since Xcode 14 (still used as altool-upload fallback)
- .env.deploy.local.example template added with one-time setup steps
- .gitignore: add *.p8 (.env*.local already covered)
2026-05-30 09:39:46 +02:00
chahinebrini
b31066a04c feat(chat): native action sheet + Insta-style heart for DM messages
- ChatBubble: useActionSheet replaces custom Modal (native iOS popup, Android bottom sheet)
- DM mode (isDM prop): hides like-count, shows Insta-style heart badge under bubble when liked
- Group chat unchanged
- Cleanup: remove unused Modal/Platform imports, sheet styles, actionsOpen state
- deploy.sh: auto-detect ANDROID_HOME + auto-create local.properties for local Gradle
- NEXT_RELEASE.md: DM reactions release note
- Includes other staged work across binder-mac, marketing, ops/mdm, ios/
2026-05-30 09:14:32 +02:00