Android-Onboarding (Platform.OS dispatch in ProtectionSlide):
- Neue Phasen für Android: preexplain_vpn → preexplain_a11y → a11y_pending
- AppState-Listener: nach Settings-Rückkehr auto-poll isAccessibilityEnabled
→ wenn live, armTamperLock + finish (kein Fokus-Klick nötig)
- onboardingAssets: 8 neue Mappings (android_vpn + android_a11y × 4 Locales)
- Screenshots: vpn-permission + a11y-rebreak-row pro Locale
- Locale-Keys: protection_url_android, protection_lock_android, cta_open_a11y,
cta_check_a11y, dialog_button_vpn_ok, dialog_button_a11y_toggle, tap_marker_hint_*
Lyra-Post i18n Phase 1 (Scaffold, feature-flag OFF by default):
- schema.prisma: CommunityPost.i18nKey String? (nullable)
- migration 20260517_add_lyra_post_i18n_key: ALTER TABLE ADD COLUMN i18n_key
(NICHT auto-deployed — `prisma migrate deploy` als separater Step)
- server/lib/lyraPostCatalog.ts: 15 Templates skelettiert + pickRandomTemplate
- cron/lyra-post: USE_TEMPLATE_CATALOG=true Branch → speichert i18nKey;
default false → LLM-Path unverändert (zero-risk-deployment)
- community.createPost: optionaler i18nKey-Parameter
- posts.get: i18nKey in API-Response
- PostCard: 3-Zeilen-Branch — i18nKey ? t('lyra_posts.'+id) : content
- stores/community: i18nKey?: string|null im Interface
- de.json: lyra_posts-Block mit 15 IDs + DE-Texten
Single-Banner-Verhalten auf Android verifiziert:
lockedIn=urlFilter && appDeletionLock funktioniert weiter — auf Android
alias appDeletionLock ← tamperLock; onboarding arms tamperLock, also
nach onboarding-done direkt ProtectionLockedCard sichtbar.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
61 lines
3.1 KiB
TypeScript
61 lines
3.1 KiB
TypeScript
/**
|
|
* Lyra Community Post Template Catalog
|
|
*
|
|
* Each entry has a stable ID that maps to a locale key in:
|
|
* apps/rebreak-native/locales/{de,en,fr,ar}.json → lyra_posts.<id>
|
|
*
|
|
* The `topic` field mirrors the existing TOPICS enum in lyra-post.ts and is
|
|
* used for throttle-logic and potential future category filtering.
|
|
*
|
|
* Texts live in locale files — this file is locale-agnostic.
|
|
* EN/FR/AR texts are curated by lyra-persona agent (pending).
|
|
*
|
|
* Target: ~60 templates (15 per topic). Currently: 15 scaffolded.
|
|
*/
|
|
|
|
export type LyraPostTopic = 'motivation' | 'tipp' | 'zitat' | 'witzig' | 'news' | 'feature';
|
|
|
|
export interface LyraPostTemplate {
|
|
id: string;
|
|
topic: LyraPostTopic;
|
|
}
|
|
|
|
export const LYRA_POST_CATALOG: LyraPostTemplate[] = [
|
|
// ── motivation (5) ──────────────────────────────────────────────────────
|
|
{ id: 'motivation_quiet_01', topic: 'motivation' },
|
|
{ id: 'motivation_quiet_02', topic: 'motivation' },
|
|
{ id: 'motivation_quiet_03', topic: 'motivation' },
|
|
{ id: 'motivation_distance_01', topic: 'motivation' },
|
|
{ id: 'motivation_distance_02', topic: 'motivation' },
|
|
|
|
// ── tipp (4) ─────────────────────────────────────────────────────────────
|
|
{ id: 'tipp_breath_01', topic: 'tipp' },
|
|
{ id: 'tipp_urge_surf_01', topic: 'tipp' },
|
|
{ id: 'tipp_habit_replace_01', topic: 'tipp' },
|
|
{ id: 'tipp_sos_reminder_01', topic: 'tipp' },
|
|
|
|
// ── zitat (2) ────────────────────────────────────────────────────────────
|
|
{ id: 'zitat_stoic_01', topic: 'zitat' },
|
|
{ id: 'zitat_psychology_01', topic: 'zitat' },
|
|
|
|
// ── witzig (2) ───────────────────────────────────────────────────────────
|
|
{ id: 'witzig_impulse_01', topic: 'witzig' },
|
|
{ id: 'witzig_distraction_01', topic: 'witzig' },
|
|
|
|
// ── news (1) ─────────────────────────────────────────────────────────────
|
|
{ id: 'news_push_tactics_01', topic: 'news' },
|
|
|
|
// ── feature (1) ──────────────────────────────────────────────────────────
|
|
{ id: 'feature_sos_01', topic: 'feature' },
|
|
];
|
|
|
|
/**
|
|
* Returns a random template from the catalog.
|
|
* Optional: pass `excludeIds` (e.g. recently used) to avoid repeats.
|
|
*/
|
|
export function pickRandomTemplate(excludeIds: string[] = []): LyraPostTemplate {
|
|
const pool = LYRA_POST_CATALOG.filter((t) => !excludeIds.includes(t.id));
|
|
const source = pool.length > 0 ? pool : LYRA_POST_CATALOG;
|
|
return source[Math.floor(Math.random() * source.length)];
|
|
}
|