chahinebrini 8670b45351 fix(magic): inline mobileconfig template as TS constant
serverAssets approach didn't bundle the template into the Nitro
output (no .output-staging/server/chunks/raw/ dir, no asset-storage
mount in nitro.mjs). Logs confirm: '[Magic] Profile template missing
in serverAssets'.

Drop serverAssets entirely. Inline the template (~2KB) as a TS
constant in backend/server/utils/magic-profile-template.ts. Build-
robust, no FS/storage dependency at runtime. Canonical source of
truth remains ops/mdm/rebreak-mac-dns-filter.mobileconfig — keep in
sync manually until/unless we add a codegen step.
2026-06-03 09:57:27 +02:00

73 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useRef, useState, useCallback } from 'react';
import { supabase } from '../lib/supabase';
import type { RealtimeChannel } from '@supabase/supabase-js';
/**
* Typing-Indicator für eine DM-Konversation via Supabase-Broadcast (ephemer,
* KEIN DB-Write — Tipp-Status muss nicht persistiert werden).
*
* Beide Peers joinen denselben deterministischen Channel (sortiertes ID-Paar),
* damit `send()` von A bei B ankommt. `self:false` filtert die eigenen Events.
*
* - `sendTyping()` → throttled-Broadcast „ich tippe" (max 1×/1.5s)
* - `sendStopTyping()` → sofortiger „Stop" (beim Senden / Leeren des Inputs)
* - `partnerTyping` → true solange Partner-Events reinkommen (Auto-Clear 4s)
*/
export function useDmTyping(myUserId: string | undefined, partnerId: string | undefined) {
const [partnerTyping, setPartnerTyping] = useState(false);
const channelRef = useRef<RealtimeChannel | null>(null);
const clearTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const lastSent = useRef(0);
useEffect(() => {
if (!myUserId || !partnerId) return;
const pair = [myUserId, partnerId].sort().join('_');
const channel = supabase.channel(`dm-typing:${pair}`, {
config: { broadcast: { self: false } },
});
channel
.on('broadcast', { event: 'typing' }, (msg: any) => {
if (msg?.payload?.userId !== partnerId) return;
setPartnerTyping(true);
if (clearTimer.current) clearTimeout(clearTimer.current);
clearTimer.current = setTimeout(() => setPartnerTyping(false), 4000);
})
.on('broadcast', { event: 'stop_typing' }, (msg: any) => {
if (msg?.payload?.userId !== partnerId) return;
if (clearTimer.current) clearTimeout(clearTimer.current);
setPartnerTyping(false);
})
.subscribe();
channelRef.current = channel;
return () => {
if (clearTimer.current) clearTimeout(clearTimer.current);
supabase.removeChannel(channel);
channelRef.current = null;
setPartnerTyping(false);
};
}, [myUserId, partnerId]);
const sendTyping = useCallback(() => {
const now = Date.now();
if (now - lastSent.current < 1500) return; // Throttle
lastSent.current = now;
channelRef.current?.send({
type: 'broadcast',
event: 'typing',
payload: { userId: myUserId },
});
}, [myUserId]);
const sendStopTyping = useCallback(() => {
lastSent.current = 0;
channelRef.current?.send({
type: 'broadcast',
event: 'stop_typing',
payload: { userId: myUserId },
});
}, [myUserId]);
return { partnerTyping, sendTyping, sendStopTyping };
}