feat: Implement core Discord clone functionality including Convex backend services for authentication, channels, messages, roles, and voice state, alongside new Electron frontend components for chat, voice, server settings, and user interface.
All checks were successful
Build and Release / build-and-release (push) Successful in 14m19s

This commit is contained in:
Bryan1029384756
2026-02-12 04:52:28 -06:00
parent e790db7029
commit 7a5b789ece
30 changed files with 1339 additions and 162 deletions

View File

@@ -1,6 +1,7 @@
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
import { getPublicStorageUrl } from "./storageUrl";
import { getRolesForUser } from "./roles";
async function removeUserVoiceStates(ctx: any, userId: any) {
const existing = await ctx.db
@@ -31,6 +32,7 @@ export const join = mutation({
isMuted: args.isMuted,
isDeafened: args.isDeafened,
isScreenSharing: false,
isServerMuted: false,
});
return null;
@@ -74,6 +76,35 @@ export const updateState = mutation({
},
});
export const serverMute = mutation({
args: {
actorUserId: v.id("userProfiles"),
targetUserId: v.id("userProfiles"),
isServerMuted: v.boolean(),
},
returns: v.null(),
handler: async (ctx, args) => {
const roles = await getRolesForUser(ctx, args.actorUserId);
const canMute = roles.some(
(role) => (role.permissions as Record<string, boolean>)?.["mute_members"]
);
if (!canMute) {
throw new Error("You don't have permission to server mute members");
}
const existing = await ctx.db
.query("voiceStates")
.withIndex("by_user", (q: any) => q.eq("userId", args.targetUserId))
.first();
if (!existing) throw new Error("Target user is not in a voice channel");
await ctx.db.patch(existing._id, { isServerMuted: args.isServerMuted });
return null;
},
});
export const getAll = query({
args: {},
returns: v.any(),
@@ -86,6 +117,7 @@ export const getAll = query({
isMuted: boolean;
isDeafened: boolean;
isScreenSharing: boolean;
isServerMuted: boolean;
avatarUrl: string | null;
}>> = {};
@@ -102,6 +134,7 @@ export const getAll = query({
isMuted: s.isMuted,
isDeafened: s.isDeafened,
isScreenSharing: s.isScreenSharing,
isServerMuted: s.isServerMuted,
avatarUrl,
});
}
@@ -109,3 +142,90 @@ export const getAll = query({
return grouped;
},
});
export const afkMove = mutation({
args: {
userId: v.id("userProfiles"),
afkChannelId: v.id("channels"),
},
returns: v.null(),
handler: async (ctx, args) => {
// Validate afkChannelId matches server settings
const settings = await ctx.db.query("serverSettings").first();
if (!settings || settings.afkChannelId !== args.afkChannelId) {
throw new Error("Invalid AFK channel");
}
// Get current voice state
const currentState = await ctx.db
.query("voiceStates")
.withIndex("by_user", (q: any) => q.eq("userId", args.userId))
.first();
// No-op if not in voice or already in AFK channel
if (!currentState || currentState.channelId === args.afkChannelId) return null;
// Move to AFK channel: delete old state, insert new one muted
await ctx.db.delete(currentState._id);
await ctx.db.insert("voiceStates", {
channelId: args.afkChannelId,
userId: args.userId,
username: currentState.username,
isMuted: true,
isDeafened: currentState.isDeafened,
isScreenSharing: false,
isServerMuted: currentState.isServerMuted,
});
return null;
},
});
export const moveUser = mutation({
args: {
actorUserId: v.id("userProfiles"),
targetUserId: v.id("userProfiles"),
targetChannelId: v.id("channels"),
},
returns: v.null(),
handler: async (ctx, args) => {
// Check actor has move_members permission
const roles = await getRolesForUser(ctx, args.actorUserId);
const canMove = roles.some(
(role) => (role.permissions as Record<string, boolean>)?.["move_members"]
);
if (!canMove) {
throw new Error("You don't have permission to move members");
}
// Validate target channel exists and is voice
const targetChannel = await ctx.db.get(args.targetChannelId);
if (!targetChannel) throw new Error("Target channel not found");
if (targetChannel.type !== "voice") throw new Error("Target channel is not a voice channel");
// Get target user's current voice state
const currentState = await ctx.db
.query("voiceStates")
.withIndex("by_user", (q: any) => q.eq("userId", args.targetUserId))
.first();
if (!currentState) throw new Error("Target user is not in a voice channel");
// No-op if already in the target channel
if (currentState.channelId === args.targetChannelId) return null;
// Delete old voice state and insert new one preserving mute/deaf/screenshare
await ctx.db.delete(currentState._id);
await ctx.db.insert("voiceStates", {
channelId: args.targetChannelId,
userId: args.targetUserId,
username: currentState.username,
isMuted: currentState.isMuted,
isDeafened: currentState.isDeafened,
isScreenSharing: currentState.isScreenSharing,
isServerMuted: currentState.isServerMuted,
});
return null;
},
});