atagen

malice

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git
web/src/lib/mute.tsTypescript61 lines2.1 KB
1
import { derived, get } from 'svelte/store';
2
import { runCommand, setAccountData } from './api';
3
import {
4
  muteSpacePlan,
5
  reconcilePlan,
6
  spaceMutedRoomIds,
7
  unmuteSpacePlan,
8
  withSpaceMutes,
9
  type Plan,
10
  type RoomMode
11
} from './mute-model';
12
import { MUTED_SPACES_EVENT, mutedSpaces, mutedSpacesLoaded } from './muted-spaces';
13
import { rooms } from './store';
14
15
export const spaceMutedRooms = derived([rooms, mutedSpaces], ([$rooms, $muted]) => spaceMutedRoomIds($rooms, $muted.spaces));
16
17
export const effectiveRooms = derived([rooms, spaceMutedRooms], ([$rooms, $muted]) => withSpaceMutes($rooms, $muted));
18
19
let queue: Promise<void> = Promise.resolve();
20
21
export function setRoomMode(roomId: string, mode: RoomMode | null): Promise<void> {
22
  return runCommand({ op: 'set_room_notification_mode', room_id: roomId, mode }).then(() => undefined);
23
}
24
25
async function apply(plan: Plan): Promise<void> {
26
  await setAccountData(MUTED_SPACES_EVENT, { ...plan.state });
27
  mutedSpaces.set(plan.state);
28
  const results = await Promise.allSettled(plan.changes.map((c) => setRoomMode(c.roomId, c.mode)));
29
  const failed = results.find((r): r is PromiseRejectedResult => r.status === 'rejected');
30
  if (failed) throw failed.reason;
31
}
32
33
function enqueue(job: () => Promise<void>): Promise<void> {
34
  const run = queue.then(job);
35
  queue = run.catch(() => undefined);
36
  return run;
37
}
38
39
export function setSpaceMuted(spaceId: string, muted: boolean): Promise<void> {
40
  return enqueue(async () => {
41
    const state = get(mutedSpaces);
42
    const all = get(rooms);
43
    await apply(muted ? muteSpacePlan(state, spaceId, all) : unmuteSpacePlan(state, spaceId, all));
44
  });
45
}
46
47
export function startMuteSync(): () => void {
48
  const reconcile = () => {
49
    if (!mutedSpacesLoaded() || get(mutedSpaces).spaces.length === 0) return;
50
    void enqueue(async () => {
51
      const plan = reconcilePlan(get(mutedSpaces), get(rooms));
52
      if (plan.changes.length > 0) await apply(plan);
53
    }).catch(() => undefined);
54
  };
55
  const stopRooms = rooms.subscribe(reconcile);
56
  const stopMuted = mutedSpaces.subscribe(reconcile);
57
  return () => {
58
    stopRooms();
59
    stopMuted();
60
  };
61
}