feat: Add initial Discord clone application with Convex backend services and Electron React frontend components.
This commit is contained in:
@@ -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);
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user