Files
DiscordClone/convex/dms.ts
Bryan1029384756 ff269ee154 nickname
2026-02-18 10:16:12 -06:00

99 lines
2.9 KiB
TypeScript

import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
import { getPublicStorageUrl } from "./storageUrl";
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");
}
const sorted = [args.userId, args.targetUserId].sort();
const dmName = `dm-${sorted[0]}-${sorted[1]}`;
const existing = await ctx.db
.query("channels")
.withIndex("by_name", (q) => q.eq("name", dmName))
.unique();
if (existing) {
return { channelId: existing._id, created: false };
}
const channelId = await ctx.db.insert("channels", {
name: dmName,
type: "dm",
});
await Promise.all([
ctx.db.insert("dmParticipants", { channelId, userId: args.userId }),
ctx.db.insert("dmParticipants", { channelId, userId: args.targetUserId }),
]);
return { channelId, created: true };
},
});
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(),
other_displayName: v.union(v.string(), v.null()),
other_user_status: v.optional(v.string()),
other_user_avatar_url: v.optional(v.union(v.string(), v.null())),
})
),
handler: async (ctx, args) => {
const myParticipations = await ctx.db
.query("dmParticipants")
.withIndex("by_user", (q) => q.eq("userId", args.userId))
.collect();
const results = await Promise.all(
myParticipations.map(async (part) => {
const channel = await ctx.db.get(part.channelId);
if (!channel || channel.type !== "dm") return null;
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 otherUser = await ctx.db.get(otherPart.userId);
if (!otherUser) return null;
const avatarUrl = otherUser.avatarStorageId
? await getPublicStorageUrl(ctx, otherUser.avatarStorageId)
: null;
return {
channel_id: part.channelId,
channel_name: channel.name,
other_user_id: otherUser._id as string,
other_username: otherUser.username,
other_displayName: otherUser.displayName || null,
other_user_status: otherUser.status || "offline",
other_user_avatar_url: avatarUrl,
};
})
);
return results.filter((r): r is NonNullable<typeof r> => r !== null);
},
});