feat: Implement core Discord features including members list, direct messages, user presence, authentication, and chat UI.
Some checks failed
Build and Release / build-and-release (push) Has been cancelled

This commit is contained in:
Bryan1029384756
2026-02-11 04:36:40 -06:00
parent a29858fd32
commit cb4361da1a
32 changed files with 2051 additions and 144 deletions

View File

@@ -17,6 +17,7 @@ import type * as gifs from "../gifs.js";
import type * as invites from "../invites.js";
import type * as members from "../members.js";
import type * as messages from "../messages.js";
import type * as presence from "../presence.js";
import type * as reactions from "../reactions.js";
import type * as roles from "../roles.js";
import type * as typing from "../typing.js";
@@ -39,6 +40,7 @@ declare const fullApi: ApiFromModules<{
invites: typeof invites;
members: typeof members;
messages: typeof messages;
presence: typeof presence;
reactions: typeof reactions;
roles: typeof roles;
typing: typeof typing;
@@ -72,4 +74,67 @@ export declare const internal: FilterApi<
FunctionReference<any, "internal">
>;
export declare const components: {};
export declare const components: {
presence: {
public: {
disconnect: FunctionReference<
"mutation",
"internal",
{ sessionToken: string },
null
>;
heartbeat: FunctionReference<
"mutation",
"internal",
{
interval?: number;
roomId: string;
sessionId: string;
userId: string;
},
{ roomToken: string; sessionToken: string }
>;
list: FunctionReference<
"query",
"internal",
{ limit?: number; roomToken: string },
Array<{
data?: any;
lastDisconnected: number;
online: boolean;
userId: string;
}>
>;
listRoom: FunctionReference<
"query",
"internal",
{ limit?: number; onlineOnly?: boolean; roomId: string },
Array<{ lastDisconnected: number; online: boolean; userId: string }>
>;
listUser: FunctionReference<
"query",
"internal",
{ limit?: number; onlineOnly?: boolean; userId: string },
Array<{ lastDisconnected: number; online: boolean; roomId: string }>
>;
removeRoom: FunctionReference<
"mutation",
"internal",
{ roomId: string },
null
>;
removeRoomUser: FunctionReference<
"mutation",
"internal",
{ roomId: string; userId: string },
null
>;
updateRoomUser: FunctionReference<
"mutation",
"internal",
{ data?: any; roomId: string; userId: string },
null
>;
};
};
};

View File

@@ -198,6 +198,7 @@ export const getPublicKeys = query({
username: v.string(),
public_identity_key: v.string(),
status: v.optional(v.string()),
displayName: v.optional(v.string()),
avatarUrl: v.optional(v.union(v.string(), v.null())),
aboutMe: v.optional(v.string()),
customStatus: v.optional(v.string()),
@@ -215,7 +216,8 @@ export const getPublicKeys = query({
id: u._id,
username: u.username,
public_identity_key: u.publicIdentityKey,
status: u.status || "online",
status: u.status || "offline",
displayName: u.displayName,
avatarUrl,
aboutMe: u.aboutMe,
customStatus: u.customStatus,
@@ -229,6 +231,7 @@ export const getPublicKeys = query({
export const updateProfile = mutation({
args: {
userId: v.id("userProfiles"),
displayName: v.optional(v.string()),
aboutMe: v.optional(v.string()),
avatarStorageId: v.optional(v.id("_storage")),
customStatus: v.optional(v.string()),
@@ -236,6 +239,7 @@ export const updateProfile = mutation({
returns: v.null(),
handler: async (ctx, args) => {
const patch: Record<string, unknown> = {};
if (args.displayName !== undefined) patch.displayName = args.displayName;
if (args.aboutMe !== undefined) patch.aboutMe = args.aboutMe;
if (args.avatarStorageId !== undefined) patch.avatarStorageId = args.avatarStorageId;
if (args.customStatus !== undefined) patch.customStatus = args.customStatus;

6
convex/convex.config.ts Normal file
View File

@@ -0,0 +1,6 @@
import { defineApp } from "convex/server";
import presence from "@convex-dev/presence/convex.config.js";
const app = defineApp();
app.use(presence);
export default app;

View File

@@ -79,7 +79,7 @@ export const listDMs = query({
channel_name: channel.name,
other_user_id: otherUser._id as string,
other_username: otherUser.username,
other_user_status: otherUser.status || "online",
other_user_status: otherUser.status || "offline",
};
})
);

View File

@@ -50,7 +50,7 @@ export const getChannelMembers = query({
members.push({
id: user._id,
username: user.username,
status: user.status || "online",
status: user.status || "offline",
roles: roles.sort((a, b) => b.position - a.position),
avatarUrl,
aboutMe: user.aboutMe,

32
convex/presence.ts Normal file
View File

@@ -0,0 +1,32 @@
import { mutation, query } from "./_generated/server";
import { components } from "./_generated/api";
import { v } from "convex/values";
import { Presence } from "@convex-dev/presence";
const presence = new Presence(components.presence);
export const heartbeat = mutation({
args: {
roomId: v.string(),
userId: v.string(),
sessionId: v.string(),
interval: v.number(),
},
handler: async (ctx, { roomId, userId, sessionId, interval }) => {
return await presence.heartbeat(ctx, roomId, userId, sessionId, interval);
},
});
export const list = query({
args: { roomToken: v.string() },
handler: async (ctx, { roomToken }) => {
return await presence.list(ctx, roomToken);
},
});
export const disconnect = mutation({
args: { sessionToken: v.string() },
handler: async (ctx, { sessionToken }) => {
return await presence.disconnect(ctx, sessionToken);
},
});

View File

@@ -12,6 +12,7 @@ export default defineSchema({
encryptedPrivateKeys: v.string(),
isAdmin: v.boolean(),
status: v.optional(v.string()),
displayName: v.optional(v.string()),
avatarStorageId: v.optional(v.id("_storage")),
aboutMe: v.optional(v.string()),
customStatus: v.optional(v.string()),