feat: Add initial Discord clone application with Convex backend services and Electron React frontend components.

This commit is contained in:
Bryan1029384756
2026-02-10 05:27:10 -06:00
parent 47f173c79b
commit 34e9790db9
29 changed files with 3254 additions and 1398 deletions

View File

@@ -2,7 +2,8 @@ import { query, mutation, internalMutation } from "./_generated/server";
import { v } from "convex/values";
import { internal } from "./_generated/api";
// Start typing indicator
const TYPING_TTL_MS = 6000;
export const startTyping = mutation({
args: {
channelId: v.id("channels"),
@@ -11,9 +12,8 @@ export const startTyping = mutation({
},
returns: v.null(),
handler: async (ctx, args) => {
const expiresAt = Date.now() + 6000; // 6 second TTL
const expiresAt = Date.now() + TYPING_TTL_MS;
// Upsert: check if already exists
const existing = await ctx.db
.query("typingIndicators")
.withIndex("by_channel", (q) => q.eq("channelId", args.channelId))
@@ -32,14 +32,11 @@ export const startTyping = mutation({
});
}
// Schedule cleanup
await ctx.scheduler.runAfter(6000, internal.typing.cleanExpired, {});
await ctx.scheduler.runAfter(TYPING_TTL_MS, internal.typing.cleanExpired, {});
return null;
},
});
// Stop typing indicator
export const stopTyping = mutation({
args: {
channelId: v.id("channels"),
@@ -61,7 +58,6 @@ export const stopTyping = mutation({
},
});
// Get typing users for a channel (reactive!)
export const getTyping = query({
args: { channelId: v.id("channels") },
returns: v.array(
@@ -79,21 +75,17 @@ export const getTyping = query({
return indicators
.filter((t) => t.expiresAt > now)
.map((t) => ({
userId: t.userId,
username: t.username,
}));
.map((t) => ({ userId: t.userId, username: t.username }));
},
});
// Internal: clean expired typing indicators
export const cleanExpired = internalMutation({
args: {},
returns: v.null(),
handler: async (ctx) => {
const now = Date.now();
const all = await ctx.db.query("typingIndicators").collect();
for (const t of all) {
const expired = await ctx.db.query("typingIndicators").collect();
for (const t of expired) {
if (t.expiresAt <= now) {
await ctx.db.delete(t._id);
}