feat: Implement core Discord clone functionality including Convex backend, Electron frontend, and UI components for chat, voice, and settings.

This commit is contained in:
Bryan1029384756
2026-02-10 04:41:10 -06:00
parent 516cfdbbd8
commit 47f173c79b
63 changed files with 4467 additions and 5292 deletions

104
convex/dms.ts Normal file
View File

@@ -0,0 +1,104 @@
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
// Find-or-create DM channel between two users
export const openDM = mutation({
args: {
userId: v.id("userProfiles"),
targetUserId: v.id("userProfiles"),
},
returns: v.object({
channelId: v.id("channels"),
created: v.boolean(),
}),
handler: async (ctx, args) => {
if (args.userId === args.targetUserId) {
throw new Error("Cannot DM yourself");
}
// Deterministic channel name
const sorted = [args.userId, args.targetUserId].sort();
const dmName = `dm-${sorted[0]}-${sorted[1]}`;
// Check if already exists
const existing = await ctx.db
.query("channels")
.withIndex("by_name", (q) => q.eq("name", dmName))
.unique();
if (existing) {
return { channelId: existing._id, created: false };
}
// Create DM channel
const channelId = await ctx.db.insert("channels", {
name: dmName,
type: "dm",
});
// Add participants
await ctx.db.insert("dmParticipants", {
channelId,
userId: args.userId,
});
await ctx.db.insert("dmParticipants", {
channelId,
userId: args.targetUserId,
});
return { channelId, created: true };
},
});
// List user's DM channels with other user info
export const listDMs = query({
args: { userId: v.id("userProfiles") },
returns: v.array(
v.object({
channel_id: v.id("channels"),
channel_name: v.string(),
other_user_id: v.string(),
other_username: v.string(),
})
),
handler: async (ctx, args) => {
// Get all DM participations for this user
const myParticipations = await ctx.db
.query("dmParticipants")
.withIndex("by_user", (q) => q.eq("userId", args.userId))
.collect();
const result: Array<{
channel_id: typeof myParticipations[0]["channelId"];
channel_name: string;
other_user_id: string;
other_username: string;
}> = [];
for (const part of myParticipations) {
const channel = await ctx.db.get(part.channelId);
if (!channel || channel.type !== "dm") continue;
// Find other participant
const otherParts = await ctx.db
.query("dmParticipants")
.withIndex("by_channel", (q) => q.eq("channelId", part.channelId))
.collect();
const otherPart = otherParts.find((p) => p.userId !== args.userId);
if (!otherPart) continue;
const otherUser = await ctx.db.get(otherPart.userId);
if (!otherUser) continue;
result.push({
channel_id: part.channelId,
channel_name: channel.name,
other_user_id: otherUser._id,
other_username: otherUser.username,
});
}
return result;
},
});