add more ux and ui patches with extra login helper
This commit is contained in:
@@ -40,6 +40,7 @@ class ChatStore {
|
||||
private initPromise: Promise<void> | null = null;
|
||||
private lastSeenAt = 0;
|
||||
private typingTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
private audioCtx: AudioContext | null = null;
|
||||
|
||||
typingNames = $derived.by(() => {
|
||||
const names: string[] = [];
|
||||
@@ -156,8 +157,9 @@ class ChatStore {
|
||||
}
|
||||
// Stop showing their typing indicator once the message lands.
|
||||
this.clearTyping(record.authorId);
|
||||
if (record.authorId !== this.actorId && !this.open) {
|
||||
this.unread++;
|
||||
if (record.authorId !== this.actorId) {
|
||||
if (!this.open) this.unread++;
|
||||
this.playDunk();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +186,39 @@ class ChatStore {
|
||||
if (this.typing[actorId]) this.removeTyping(actorId);
|
||||
}
|
||||
|
||||
// Short, low-volume "dunk" blip played when a message from someone else is
|
||||
// broadcast in. Synthesised with the Web Audio API so there's no asset to
|
||||
// ship. Browsers require a user gesture before audio can start, but chat
|
||||
// messages only arrive after the user has interacted, so the context
|
||||
// resumes fine. Failures are silently ignored.
|
||||
private playDunk() {
|
||||
if (typeof window === 'undefined' || typeof AudioContext === 'undefined') return;
|
||||
try {
|
||||
const Ctor = window.AudioContext || (window as any).webkitAudioContext;
|
||||
if (!Ctor) return;
|
||||
if (!this.audioCtx) this.audioCtx = new Ctor();
|
||||
const ctx = this.audioCtx;
|
||||
if (ctx.state === 'suspended') ctx.resume();
|
||||
|
||||
const now = ctx.currentTime;
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
osc.type = 'sine';
|
||||
// Quick downward pitch sweep gives the soft "dun-nk" thud.
|
||||
osc.frequency.setValueAtTime(190, now);
|
||||
osc.frequency.exponentialRampToValueAtTime(70, now + 0.18);
|
||||
// Low volume with a fast attack/decay envelope.
|
||||
gain.gain.setValueAtTime(0.0001, now);
|
||||
gain.gain.exponentialRampToValueAtTime(0.12, now + 0.01);
|
||||
gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.22);
|
||||
osc.connect(gain).connect(ctx.destination);
|
||||
osc.start(now);
|
||||
osc.stop(now + 0.24);
|
||||
} catch {
|
||||
/* audio is best-effort */
|
||||
}
|
||||
}
|
||||
|
||||
private removeTyping(actorId: string) {
|
||||
if (!this.typing[actorId]) return;
|
||||
const next = { ...this.typing };
|
||||
|
||||
Reference in New Issue
Block a user