chahinebrini 2e49aad386 feat(voice+chat): voice notes DM, chat list attachment preview, DiGA milestone modal
Voice Notes (DM):
- WhatsApp-style voice recording bar (shared VoiceRecordingBar component)
- Audio bubbles: 80 fixed-2dp bars (Instagram-style thin), space-between layout,
  deterministic waveform, moving blue position dot, WA gray bar colors
- Cancel flash fix: setIsVoiceRecording delayed 350ms so trash flash is visible
- Mic button 44pt (Apple min), hitSlop on all recording controls
- startReply shows 🎤/📷 label for voice/image instead of empty

Chat list:
- lastAttachmentType from backend (getDmConversations now selects attachmentType)
- Shows '🎤 Sprachnachricht' / '📷 Foto' / '📎 Medien' as fallback per type
- User search second stage: GET /api/users/search?q= + debounced frontend section
- Push preview: audio → '🎤 Sprachnachricht', image → '📷 Foto' (was '📎 Anhang')

Blocker iOS Layer 3 (Screen Time):
- ScreentimePasscodeCard visible in locked-in state (was hidden once both layers active)
- Confirmed status loaded from backend on mount
- Numbered step instructions (iOS has no deep link to passcode dialog)
- Guard: only for unsupervised VPN+FC path (!mdmManaged && !nefilterActive)
- URL fallback: App-Prefs:SCREEN_TIME → App-Prefs:root=SCREEN_TIME → openSettings

DiGA Milestone Modal:
- Day 3/7/10 celebratory bottom sheet with soft demographic data ask
- Per-user/milestone AsyncStorage tracking, never shows if demographics filled
- Opens DemographicsAccordion in profile via ?openDemo=1 param

Lyra coach: contextual DiGA demographic nudge (optional, positive moments only)
i18n: DE/EN/FR/AR for voice_message, photo, media_sent, mic_access, diga_milestone,
  screentime steps, chat search strings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 01:59:26 +02:00

31 lines
842 B
TypeScript

/** GET /api/users/search?q=... — Nickname-Suche für neue DM-Gespräche */
export default defineEventHandler(async (event) => {
const user = await requireUser(event);
const query = getQuery(event);
const q = ((query.q as string) ?? "").trim();
if (!q || q.length < 2) return [];
const db = usePrisma();
const results = await db.profile.findMany({
where: {
id: { not: user.id },
deletedAt: null,
OR: [
{ nickname: { contains: q, mode: "insensitive" } },
{ username: { contains: q, mode: "insensitive" } },
],
},
select: { id: true, nickname: true, username: true, avatar: true },
take: 20,
orderBy: { nickname: "asc" },
});
return results.map((p) => ({
id: p.id,
nickname: p.nickname ?? p.username ?? "Anonym",
avatar: p.avatar ?? null,
}));
});