v0.4.0 f6d8405

mutations.ts RawHistoryFolder

1import { mutation } from './_generated/server';
2import { authMutation } from './auth';
3import { v } from 'convex/values';
4
5// Authenticated mutation: Send a message
6export const sendMessage = authMutation({
7	args: {
8		chatRoomId: v.id('chatRooms'),
9		content: v.string(),
10		type: v.optional(v.string()),
11		styleData: v.optional(v.string()),
12		hasFormatting: v.optional(v.boolean())
13	},
14	handler: async (ctx, args) => {
15		// ctx.user is provided by authMutation
16		const user = ctx.user;
17
18		// Verify room exists
19		const room = await ctx.db.get(args.chatRoomId);
20		if (!room) {
21			throw new Error('Chat room not found');
22		}
23		if (room.type === 'direct' && !(room.memberIds ?? []).includes(user._id)) {
24			throw new Error('Not a member of this conversation');
25		}
26
27		const messageId = await ctx.db.insert('messages', {
28			chatRoomId: args.chatRoomId,
29			senderId: user._id,
30			content: args.content,
31			type: args.type ?? 'chat',
32			timestamp: Date.now(),
33			styleData: args.styleData,
34			hasFormatting: args.hasFormatting ?? false
35		});
36
37		const message = await ctx.db.get(messageId);
38
39		// Return enriched message
40		return {
41			id: message!._id,
42			chatRoomId: message!.chatRoomId,
43			senderId: message!.senderId,
44			content: message!.content,
45			type: message!.type,
46			timestamp: message!.timestamp,
47			styleData: message!.styleData,
48			hasFormatting: message!.hasFormatting,
49			sender: {
50				id: user._id,
51				nickname: user.nickname,
52				status: user.status,
53				avatarUrl: user.avatarUrl
54			}
55		};
56	}
57});
58
59// Authenticated mutation: Update current user's status
60export const updateMyStatus = authMutation({
61	args: {
62		status: v.string()
63	},
64	handler: async (ctx, args) => {
65		const validStatuses = ['online', 'away', 'busy', 'offline', 'idle'];
66		if (!validStatuses.includes(args.status)) {
67			throw new Error('Invalid status');
68		}
69
70		await ctx.db.patch(ctx.user._id, {
71			status: args.status,
72			lastSeen: Date.now()
73		});
74
75		const updated = await ctx.db.get(ctx.user._id);
76		return {
77			id: updated!._id,
78			nickname: updated!.nickname,
79			status: updated!.status,
80			avatarUrl: updated!.avatarUrl,
81			lastSeen: updated!.lastSeen
82		};
83	}
84});
85
86// Public mutation: Create or get default room (can be called during init)
87export const getOrCreateDefaultRoom = mutation({
88	args: {},
89	handler: async (ctx) => {
90		// Look for existing General room
91		const rooms = await ctx.db.query('chatRooms').collect();
92		const generalRoom = rooms.find((r) => r.name === 'General' && r.type === 'group');
93
94		if (generalRoom) {
95			return {
96				id: generalRoom._id,
97				name: generalRoom.name,
98				type: generalRoom.type,
99				createdAt: generalRoom.createdAt
100			};
101		}
102
103		// Create default room
104		const roomId = await ctx.db.insert('chatRooms', {
105			name: 'General',
106			type: 'group',
107			createdAt: Date.now()
108		});
109
110		const room = await ctx.db.get(roomId);
111		return {
112			id: room!._id,
113			name: room!.name,
114			type: room!.type,
115			createdAt: room!.createdAt
116		};
117	}
118});

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