main f6d8405

client.ts RawHistoryFolder

1/**
2 * Client-side helpers for authenticated Convex calls (AIM features).
3 *
4 * Every authQuery/authMutation needs `tokenHash`; this module reads it from
5 * chatState so components only pass their own args.
6 */
7import { useConvexClient, useQuery } from 'convex-svelte';
8import { api } from '../../convex/_generated/api';
9import type { FunctionReference, FunctionArgs } from 'convex/server';
10import type { Id } from '../../convex/_generated/dataModel';
11import { chatState } from '$lib/states/chat.svelte';
12import { browser } from '$app/environment';
13import { env } from '$env/dynamic/public';
14
15export const convexAvailable = browser && Boolean(env.PUBLIC_CONVEX_URL);
16
17/** Returns the current tokenHash or null when signed out. Reactive when read inside $derived. */
18export function tokenHash() {
19	return chatState.getSessionTokenHash();
20}
21
22/**
23 * Subscribe to an authenticated query. Must be called at component init
24 * (top level of <script>), like useQuery. The subscription is skipped while
25 * signed out and resumes automatically after login (data is undefined meanwhile).
26 *
27 *   const buddies = authQuery(api.aim.getMyBuddies, () => ({}));
28 *   buddies?.data
29 */
30export function authQuery<Q extends FunctionReference<'query'>>(
31	fn: Q,
32	args: () => Omit<FunctionArgs<Q>, 'tokenHash'> | 'skip' = () =>
33		({}) as Omit<FunctionArgs<Q>, 'tokenHash'>
34) {
35	if (!convexAvailable) return null;
36	return useQuery(fn, () => {
37		const hash = tokenHash();
38		const a = args();
39		if (!hash || a === 'skip') return 'skip';
40		return { tokenHash: hash, ...a } as FunctionArgs<Q>;
41	});
42}
43
44export function useAimClient() {
45	const client = convexAvailable ? useConvexClient() : null;
46
47	function requireAuth() {
48		const hash = tokenHash();
49		if (!client || !hash) throw new Error('Non connecté');
50		return { client, hash };
51	}
52
53	return {
54		client,
55		async addBuddy(buddyId: Id<'users'>, group?: string) {
56			const { client, hash } = requireAuth();
57			return client.mutation(api.aim.addBuddy, { tokenHash: hash, buddyId, group });
58		},
59		async removeBuddy(buddyId: Id<'users'>) {
60			const { client, hash } = requireAuth();
61			return client.mutation(api.aim.removeBuddy, { tokenHash: hash, buddyId });
62		},
63		async getOrCreateDirectRoom(otherUserId: Id<'users'>) {
64			const { client, hash } = requireAuth();
65			return client.mutation(api.aim.getOrCreateDirectRoom, { tokenHash: hash, otherUserId });
66		},
67		async createGroupRoom(name: string, topic?: string) {
68			const { client, hash } = requireAuth();
69			return client.mutation(api.aim.createGroupRoom, { tokenHash: hash, name, topic });
70		},
71		async setAwayMessage(awayMessage?: string) {
72			const { client, hash } = requireAuth();
73			return client.mutation(api.aim.setAwayMessage, { tokenHash: hash, awayMessage });
74		},
75		async updateProfile(patch: { profile?: string }) {
76			const { client, hash } = requireAuth();
77			return client.mutation(api.aim.updateProfile, { tokenHash: hash, ...patch });
78		},
79		async markRoomRead(roomId: Id<'chatRooms'>) {
80			const { client, hash } = requireAuth();
81			return client.mutation(api.aim.markRoomRead, { tokenHash: hash, roomId });
82		},
83		async setTyping(roomId: Id<'chatRooms'>, typing: boolean) {
84			const { client, hash } = requireAuth();
85			return client.mutation(api.aim.setTyping, { tokenHash: hash, roomId, typing });
86		}
87	};
88}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 8

↑↓ moveenter openesc close