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

@@ -1,7 +1,6 @@
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"),
@@ -16,11 +15,9 @@ export const openDM = mutation({
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))
@@ -30,27 +27,20 @@ export const openDM = mutation({
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,
});
await Promise.all([
ctx.db.insert("dmParticipants", { channelId, userId: args.userId }),
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(
@@ -62,43 +52,36 @@ export const listDMs = query({
})
),
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;
}> = [];
const results = await Promise.all(
myParticipations.map(async (part) => {
const channel = await ctx.db.get(part.channelId);
if (!channel || channel.type !== "dm") return null;
for (const part of myParticipations) {
const channel = await ctx.db.get(part.channelId);
if (!channel || channel.type !== "dm") continue;
const otherParts = await ctx.db
.query("dmParticipants")
.withIndex("by_channel", (q) => q.eq("channelId", part.channelId))
.collect();
// 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) return null;
const otherPart = otherParts.find((p) => p.userId !== args.userId);
if (!otherPart) continue;
const otherUser = await ctx.db.get(otherPart.userId);
if (!otherUser) return null;
const otherUser = await ctx.db.get(otherPart.userId);
if (!otherUser) continue;
return {
channel_id: part.channelId,
channel_name: channel.name,
other_user_id: otherUser._id as string,
other_username: otherUser.username,
};
})
);
result.push({
channel_id: part.channelId,
channel_name: channel.name,
other_user_id: otherUser._id,
other_username: otherUser.username,
});
}
return result;
return results.filter((r): r is NonNullable<typeof r> => r !== null);
},
});