atagen

malice

public
Star0
Watch0

Commit 4ac384dcce6c

tarball

atagen <boss@atagen.co>2026-10-27 04:32 UTC

2 files changed+22-26
web: break the store import cycle the message sound added

store.ts imported message-sound.ts, which pulled in mute.ts, whose module body derives from store.ts's rooms. Whenever the bundle evaluated store.ts first, rooms was still in its temporal dead zone and the app died at startup with "Cannot access before initialization". The arrival watcher now lives in notifications.ts, which store.ts doesn't import.
Modified file: web/src/lib/message-sound.ts−25 deletions
@@ -1,16 +1,11 @@
11 import { get, writable } from 'svelte/store';
22 import { getAccountData, messageMediaUrl, setAccountData, uploadMedia } from './api';
3-import { effectiveRooms } from './mute';
4-import { arrivalIn, isRoomWatched } from './notification-model';
53 import {
64 DEFAULT_MESSAGE_SOUND,
75 SOUND_MAX_BYTES,
86 parseMessageSound,
9- shouldPlaySound,
107 type MessageSoundSettings
118 } from './message-sound-model';
12-import { selectedRoomId } from './store';
13-import type { RoomSummary } from './types';
149
1510 const EVENT_TYPE = 'dev.malice.message_sound';
1611 const DEFAULT_URL = '/sounds/message.wav';
@@ -19,7 +14,6 @@ const CACHE_NAME = 'malice-sounds';
1914 export const messageSound = writable<MessageSoundSettings>(DEFAULT_MESSAGE_SOUND);
2015
2116 const resolved = new Map<string, Promise<string>>();
22-let lastPlayedAt = 0;
2317
2418 export async function loadMessageSound(): Promise<void> {
2519 try {
@@ -105,22 +99,3 @@ export async function uploadMessageSound(file: File): Promise<void> {
10599 await remember(mxc, file);
106100 await saveMessageSound({ ...get(messageSound), sound: mxc });
107101 }
108-
109-export function startMessageSound(): () => void {
110- let previous = new Map<string, RoomSummary>();
111- const unsubscribe = effectiveRooms.subscribe((list) => {
112- const prior = previous;
113- previous = new Map(list.map((room) => [room.room_id, room]));
114- const now = Date.now();
115- const mode = get(messageSound).mode;
116- for (const room of list) {
117- const arrival = arrivalIn(prior.get(room.room_id), room, now);
118- if (!arrival) continue;
119- if (isRoomWatched(room.room_id, get(selectedRoomId), document.visibilityState === 'visible', document.hasFocus())) continue;
120- if (!shouldPlaySound(mode, room, arrival, lastPlayedAt, now)) continue;
121- lastPlayedAt = now;
122- void playMessageSound();
123- }
124- });
125- return unsubscribe;
126-}
Modified file: web/src/lib/notifications.ts+22 additions−1 deletions
@@ -13,7 +13,8 @@ import {
1313 type NotificationPlan
1414 } from './notification-model';
1515 import { effectiveRooms } from './mute';
16-import { startMessageSound } from './message-sound';
16+import { messageSound, playMessageSound } from './message-sound';
17+import { shouldPlaySound } from './message-sound-model';
1718 import { PUSH_CHANGED_MESSAGE } from './push-model';
1819 import { disablePush, enablePush, pushActive, syncPush } from './push';
1920 import { selectedRoomId, sessionUser, timelines } from './store';
@@ -28,6 +29,26 @@ export type NotificationPermissionState = NotificationPermission | 'unsupported'
2829 const supported = typeof Notification !== 'undefined';
2930 const preferenceKey = (account: string) => `malice.notifications.${account}`;
3031
32+function startMessageSound(): () => void {
33+ let previous = new Map<string, RoomSummary>();
34+ let lastPlayedAt = 0;
35+ const unsubscribe = effectiveRooms.subscribe((list) => {
36+ const prior = previous;
37+ previous = new Map(list.map((room) => [room.room_id, room]));
38+ const now = Date.now();
39+ const mode = get(messageSound).mode;
40+ for (const room of list) {
41+ const arrival = arrivalIn(prior.get(room.room_id), room, now);
42+ if (!arrival) continue;
43+ if (isRoomWatched(room.room_id, get(selectedRoomId), document.visibilityState === 'visible', document.hasFocus())) continue;
44+ if (!shouldPlaySound(mode, room, arrival, lastPlayedAt, now)) continue;
45+ lastPlayedAt = now;
46+ void playMessageSound();
47+ }
48+ });
49+ return unsubscribe;
50+}
51+
3152 export const notificationsEnabled = writable(false);
3253 export const notificationPermission = writable<NotificationPermissionState>(supported ? Notification.permission : 'unsupported');