nickname
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { query, mutation, internalQuery, internalMutation } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
import { getPublicStorageUrl } from "./storageUrl";
|
||||
import { getRolesForUser } from "./roles";
|
||||
|
||||
async function sha256Hex(input: string): Promise<string> {
|
||||
const buffer = await crypto.subtle.digest(
|
||||
@@ -165,6 +166,7 @@ export const createUserWithProfile = mutation({
|
||||
manage_channels: true,
|
||||
manage_roles: true,
|
||||
manage_messages: true,
|
||||
manage_nicknames: true,
|
||||
create_invite: true,
|
||||
embed_links: true,
|
||||
attach_files: true,
|
||||
@@ -337,6 +339,34 @@ export const getUserForRecovery = internalQuery({
|
||||
},
|
||||
});
|
||||
|
||||
// Set nickname (displayName) for a user
|
||||
export const setNickname = mutation({
|
||||
args: {
|
||||
actorUserId: v.id("userProfiles"),
|
||||
targetUserId: v.id("userProfiles"),
|
||||
displayName: v.string(),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
// Self-changes are always allowed
|
||||
if (args.actorUserId !== args.targetUserId) {
|
||||
const roles = await getRolesForUser(ctx, args.actorUserId);
|
||||
const canManage = roles.some(
|
||||
(role) => (role.permissions as Record<string, boolean>)?.["manage_nicknames"]
|
||||
);
|
||||
if (!canManage) {
|
||||
throw new Error("You don't have permission to change other users' nicknames");
|
||||
}
|
||||
}
|
||||
|
||||
const trimmed = args.displayName.trim();
|
||||
await ctx.db.patch(args.targetUserId, {
|
||||
displayName: trimmed || undefined,
|
||||
});
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
// Internal: update credentials after password reset
|
||||
export const updateCredentials = internalMutation({
|
||||
args: {
|
||||
|
||||
@@ -50,6 +50,7 @@ export const listDMs = query({
|
||||
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())),
|
||||
})
|
||||
@@ -85,6 +86,7 @@ export const listDMs = query({
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -51,6 +51,7 @@ export const getChannelMembers = query({
|
||||
members.push({
|
||||
id: user._id,
|
||||
username: user.username,
|
||||
displayName: user.displayName || null,
|
||||
status: user.status || "offline",
|
||||
roles: roles.sort((a, b) => b.position - a.position),
|
||||
avatarUrl,
|
||||
@@ -77,6 +78,7 @@ export const listAll = query({
|
||||
results.push({
|
||||
id: user._id,
|
||||
username: user.username,
|
||||
displayName: user.displayName || null,
|
||||
status: user.status || "offline",
|
||||
avatarUrl,
|
||||
});
|
||||
|
||||
@@ -27,6 +27,7 @@ async function enrichMessage(ctx: any, msg: any, userId?: any) {
|
||||
}
|
||||
|
||||
let replyToUsername: string | null = null;
|
||||
let replyToDisplayName: string | null = null;
|
||||
let replyToContent: string | null = null;
|
||||
let replyToNonce: string | null = null;
|
||||
let replyToAvatarUrl: string | null = null;
|
||||
@@ -35,6 +36,7 @@ async function enrichMessage(ctx: any, msg: any, userId?: any) {
|
||||
if (repliedMsg) {
|
||||
const repliedSender = await ctx.db.get(repliedMsg.senderId);
|
||||
replyToUsername = repliedSender?.username || "Unknown";
|
||||
replyToDisplayName = repliedSender?.displayName || null;
|
||||
replyToContent = repliedMsg.ciphertext;
|
||||
replyToNonce = repliedMsg.nonce;
|
||||
if (repliedSender?.avatarStorageId) {
|
||||
@@ -53,11 +55,13 @@ async function enrichMessage(ctx: any, msg: any, userId?: any) {
|
||||
key_version: msg.keyVersion,
|
||||
created_at: new Date(msg._creationTime).toISOString(),
|
||||
username: sender?.username || "Unknown",
|
||||
displayName: sender?.displayName || null,
|
||||
public_signing_key: sender?.publicSigningKey || "",
|
||||
avatarUrl,
|
||||
reactions: Object.keys(reactions).length > 0 ? reactions : null,
|
||||
replyToId: msg.replyTo || null,
|
||||
replyToUsername,
|
||||
replyToDisplayName,
|
||||
replyToContent,
|
||||
replyToNonce,
|
||||
replyToAvatarUrl,
|
||||
|
||||
@@ -12,6 +12,7 @@ const PERMISSION_KEYS = [
|
||||
"attach_files",
|
||||
"move_members",
|
||||
"mute_members",
|
||||
"manage_nicknames",
|
||||
] as const;
|
||||
|
||||
export async function getRolesForUser(
|
||||
@@ -191,6 +192,7 @@ export const getMyPermissions = query({
|
||||
attach_files: v.boolean(),
|
||||
move_members: v.boolean(),
|
||||
mute_members: v.boolean(),
|
||||
manage_nicknames: v.boolean(),
|
||||
}),
|
||||
handler: async (ctx, args) => {
|
||||
const roles = await getRolesForUser(ctx, args.userId);
|
||||
@@ -211,6 +213,7 @@ export const getMyPermissions = query({
|
||||
attach_files: boolean;
|
||||
move_members: boolean;
|
||||
mute_members: boolean;
|
||||
manage_nicknames: boolean;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -64,6 +64,7 @@ export const getTyping = query({
|
||||
v.object({
|
||||
userId: v.id("userProfiles"),
|
||||
username: v.string(),
|
||||
displayName: v.union(v.string(), v.null()),
|
||||
})
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
@@ -73,9 +74,17 @@ export const getTyping = query({
|
||||
.withIndex("by_channel", (q) => q.eq("channelId", args.channelId))
|
||||
.collect();
|
||||
|
||||
return indicators
|
||||
.filter((t) => t.expiresAt > now)
|
||||
.map((t) => ({ userId: t.userId, username: t.username }));
|
||||
const active = indicators.filter((t) => t.expiresAt > now);
|
||||
const results = [];
|
||||
for (const t of active) {
|
||||
const user = await ctx.db.get(t.userId);
|
||||
results.push({
|
||||
userId: t.userId,
|
||||
username: t.username,
|
||||
displayName: user?.displayName || null,
|
||||
});
|
||||
}
|
||||
return results;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ export const getAll = query({
|
||||
const grouped: Record<string, Array<{
|
||||
userId: string;
|
||||
username: string;
|
||||
displayName: string | null;
|
||||
isMuted: boolean;
|
||||
isDeafened: boolean;
|
||||
isScreenSharing: boolean;
|
||||
@@ -169,6 +170,7 @@ export const getAll = query({
|
||||
(grouped[s.channelId] ??= []).push({
|
||||
userId: s.userId,
|
||||
username: s.username,
|
||||
displayName: user?.displayName || null,
|
||||
isMuted: s.isMuted,
|
||||
isDeafened: s.isDeafened,
|
||||
isScreenSharing: s.isScreenSharing,
|
||||
|
||||
Reference in New Issue
Block a user