Files
cinny/src/app/hooks/useTauriJumpList.ts
T
jared aab7e5ae20 feat(desktop): Tier A desktop features — web side (P5-46/36/44/43/49/47/55/57)
Web half of the desktop feature wave. A shared bridge (`hooks/useTauri.ts`:
invokeTauri/isTauri/useTauriEvent) backs per-feature hooks that no-op in the
browser and drive the native Tauri commands (compiled in cinny-desktop):

- P5-46 useTauriCallPower — hold system awake while a call is active.
- P5-36 useTauriJumpList — Windows jump list of recent rooms → matrix: deep links.
- P5-44 useTauriThumbbar — taskbar Mute/Deafen/End; events toggle mic/sound/hangup.
- P5-43 useTauriSmtc — SMTC call state + button events.
- P5-49 useTauriNetwork — react to native network-change → mx.retryImmediately().
- P5-47 window chrome — opt-in `customWindowChromeAtom` + TDS `TitleBar`; DesktopChrome
  wrapper in App.tsx (zero layout impact when off) + a desktop-only settings toggle.
- P5-55 composer toolbar drag-reorder (settings order[] + pragmatic-drag-and-drop).
- P5-57 DraftIndicator — subtle "draft saved" cue in the composer.

Client-scoped hooks mount via TauriDesktopFeatures in ClientNonUIFeatures; window
chrome mounts at App level. Gates: tsc/eslint/prettier clean, build OK, 556 tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 09:07:03 -04:00

59 lines
2.2 KiB
TypeScript

import { useEffect } from 'react';
import { useAtomValue } from 'jotai';
import { Room } from 'matrix-js-sdk';
import { allRoomsAtom } from '../state/room-list/roomList';
import { useMatrixClient } from './useMatrixClient';
import { isTauri, invokeTauri } from './useTauri';
/** Cap the Jump List to a small, glanceable set of rooms. */
const MAX_ITEMS = 8;
/** Wait for room activity to settle before re-publishing the (native) list. */
const DEBOUNCE_MS = 1500;
type JumpItem = { title: string; uri: string };
/**
* Build the `matrix:` deep link the desktop deep-link handler understands (see
* `useDeepLinkNavigate`): `matrix:r/<alias>` for a canonical alias, otherwise
* `matrix:roomid/<id>`. The sigil is dropped and the remainder is percent-encoded
* because the handler decodes each segment with `decodeURIComponent`.
*/
const roomToUri = (room: Room): string => {
const alias = room.getCanonicalAlias();
if (alias && alias.startsWith('#')) {
return `matrix:r/${encodeURIComponent(alias.slice(1))}`;
}
return `matrix:roomid/${encodeURIComponent(room.roomId.slice(1))}`;
};
/**
* P5-36 — publish a Windows taskbar Jump List of the most recently-active rooms.
* Rooms come from `allRoomsAtom` (the joined-room list), sorted by
* `getLastActiveTimestamp` (mirroring the sort used elsewhere, e.g. the forward
* dialog), with spaces excluded. The list is pushed to the native
* `set_jump_list` command, debounced so bursts of activity don't thrash the
* shell. No-op outside Tauri.
*/
export function useTauriJumpList(): void {
const mx = useMatrixClient();
const allRooms = useAtomValue(allRoomsAtom);
useEffect(() => {
if (!isTauri()) return undefined;
const timeout = setTimeout(() => {
const items: JumpItem[] = allRooms
.map((roomId) => mx.getRoom(roomId))
.filter((room): room is Room => room !== null && !room.isSpaceRoom())
.sort((a, b) => (b.getLastActiveTimestamp() ?? 0) - (a.getLastActiveTimestamp() ?? 0))
.slice(0, MAX_ITEMS)
.map((room) => ({ title: room.name || room.roomId, uri: roomToUri(room) }));
invokeTauri('set_jump_list', { items });
}, DEBOUNCE_MS);
return () => clearTimeout(timeout);
}, [mx, allRooms]);
}