feat: Introduce comprehensive user settings, voice, chat, and screen sharing features with new components, contexts, icons, and Convex backend integrations.
All checks were successful
Build and Release / build-and-release (push) Successful in 13m55s
All checks were successful
Build and Release / build-and-release (push) Successful in 13m55s
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { query, mutation } from "./_generated/server";
|
||||
import { query, mutation, internalMutation } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
import { internal } from "./_generated/api";
|
||||
import { getPublicStorageUrl } from "./storageUrl";
|
||||
import { getRolesForUser } from "./roles";
|
||||
|
||||
@@ -33,8 +34,12 @@ export const join = mutation({
|
||||
isDeafened: args.isDeafened,
|
||||
isScreenSharing: false,
|
||||
isServerMuted: false,
|
||||
lastHeartbeat: Date.now(),
|
||||
});
|
||||
|
||||
// Schedule stale cleanup to run in 90 seconds
|
||||
await ctx.scheduler.runAfter(90000, internal.voiceState.cleanStaleStates, {});
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
@@ -217,6 +222,7 @@ export const afkMove = mutation({
|
||||
isDeafened: currentState.isDeafened,
|
||||
isScreenSharing: false,
|
||||
isServerMuted: currentState.isServerMuted,
|
||||
lastHeartbeat: Date.now(),
|
||||
});
|
||||
|
||||
// Clear viewers watching the moved user's stream (screen sharing stops on AFK move)
|
||||
@@ -260,6 +266,56 @@ export const disconnectUser = mutation({
|
||||
},
|
||||
});
|
||||
|
||||
export const heartbeat = mutation({
|
||||
args: {
|
||||
userId: v.id("userProfiles"),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const existing = await ctx.db
|
||||
.query("voiceStates")
|
||||
.withIndex("by_user", (q: any) => q.eq("userId", args.userId))
|
||||
.first();
|
||||
|
||||
if (existing) {
|
||||
await ctx.db.patch(existing._id, { lastHeartbeat: Date.now() });
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
export const cleanStaleStates = internalMutation({
|
||||
args: {},
|
||||
returns: v.null(),
|
||||
handler: async (ctx) => {
|
||||
const states = await ctx.db.query("voiceStates").collect();
|
||||
const staleThreshold = Date.now() - 90_000; // 90 seconds
|
||||
let hasActiveStates = false;
|
||||
|
||||
for (const s of states) {
|
||||
if (s.lastHeartbeat && s.lastHeartbeat < staleThreshold) {
|
||||
// Clear viewers watching this user's stream
|
||||
for (const other of states) {
|
||||
if (other.watchingStream === s.userId && other._id !== s._id) {
|
||||
await ctx.db.patch(other._id, { watchingStream: undefined });
|
||||
}
|
||||
}
|
||||
await ctx.db.delete(s._id);
|
||||
} else {
|
||||
hasActiveStates = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Re-schedule if there are still active voice states
|
||||
if (hasActiveStates) {
|
||||
await ctx.scheduler.runAfter(90000, internal.voiceState.cleanStaleStates, {});
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
export const moveUser = mutation({
|
||||
args: {
|
||||
actorUserId: v.id("userProfiles"),
|
||||
@@ -303,6 +359,7 @@ export const moveUser = mutation({
|
||||
isDeafened: currentState.isDeafened,
|
||||
isScreenSharing: currentState.isScreenSharing,
|
||||
isServerMuted: currentState.isServerMuted,
|
||||
lastHeartbeat: Date.now(),
|
||||
});
|
||||
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user