Add custom emojis
All checks were successful
Build and Release / build-and-release (push) Successful in 10m4s
All checks were successful
Build and Release / build-and-release (push) Successful in 10m4s
This commit is contained in:
2
convex/_generated/api.d.ts
vendored
2
convex/_generated/api.d.ts
vendored
@@ -12,6 +12,7 @@ import type * as auth from "../auth.js";
|
||||
import type * as categories from "../categories.js";
|
||||
import type * as channelKeys from "../channelKeys.js";
|
||||
import type * as channels from "../channels.js";
|
||||
import type * as customEmojis from "../customEmojis.js";
|
||||
import type * as dms from "../dms.js";
|
||||
import type * as files from "../files.js";
|
||||
import type * as gifs from "../gifs.js";
|
||||
@@ -39,6 +40,7 @@ declare const fullApi: ApiFromModules<{
|
||||
categories: typeof categories;
|
||||
channelKeys: typeof channelKeys;
|
||||
channels: typeof channels;
|
||||
customEmojis: typeof customEmojis;
|
||||
dms: typeof dms;
|
||||
files: typeof files;
|
||||
gifs: typeof gifs;
|
||||
|
||||
98
convex/customEmojis.ts
Normal file
98
convex/customEmojis.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { query, mutation } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
import { getRolesForUser } from "./roles";
|
||||
import { getPublicStorageUrl } from "./storageUrl";
|
||||
|
||||
export const list = query({
|
||||
args: {},
|
||||
returns: v.any(),
|
||||
handler: async (ctx) => {
|
||||
const emojis = await ctx.db.query("customEmojis").collect();
|
||||
const results = await Promise.all(
|
||||
emojis.map(async (emoji) => {
|
||||
const src = await getPublicStorageUrl(ctx, emoji.storageId);
|
||||
const user = await ctx.db.get(emoji.uploadedBy);
|
||||
return {
|
||||
_id: emoji._id,
|
||||
name: emoji.name,
|
||||
src,
|
||||
createdAt: emoji.createdAt,
|
||||
uploadedByUsername: user?.username || "Unknown",
|
||||
};
|
||||
})
|
||||
);
|
||||
return results.filter((e) => e.src !== null);
|
||||
},
|
||||
});
|
||||
|
||||
export const upload = mutation({
|
||||
args: {
|
||||
userId: v.id("userProfiles"),
|
||||
name: v.string(),
|
||||
storageId: v.id("_storage"),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
// Permission check
|
||||
const roles = await getRolesForUser(ctx, args.userId);
|
||||
const canManage = roles.some(
|
||||
(role) => (role.permissions as Record<string, boolean>)?.["manage_channels"]
|
||||
);
|
||||
if (!canManage) {
|
||||
throw new Error("You don't have permission to manage emojis");
|
||||
}
|
||||
|
||||
// Validate name format
|
||||
const name = args.name.trim();
|
||||
if (!/^[a-zA-Z0-9_]+$/.test(name)) {
|
||||
throw new Error("Emoji name can only contain letters, numbers, and underscores");
|
||||
}
|
||||
if (name.length < 2 || name.length > 32) {
|
||||
throw new Error("Emoji name must be between 2 and 32 characters");
|
||||
}
|
||||
|
||||
// Check for duplicate name among custom emojis
|
||||
const existing = await ctx.db
|
||||
.query("customEmojis")
|
||||
.withIndex("by_name", (q) => q.eq("name", name))
|
||||
.first();
|
||||
if (existing) {
|
||||
throw new Error(`A custom emoji named "${name}" already exists`);
|
||||
}
|
||||
|
||||
await ctx.db.insert("customEmojis", {
|
||||
name,
|
||||
storageId: args.storageId,
|
||||
uploadedBy: args.userId,
|
||||
createdAt: Date.now(),
|
||||
});
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
export const remove = mutation({
|
||||
args: {
|
||||
userId: v.id("userProfiles"),
|
||||
emojiId: v.id("customEmojis"),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
// Permission check
|
||||
const roles = await getRolesForUser(ctx, args.userId);
|
||||
const canManage = roles.some(
|
||||
(role) => (role.permissions as Record<string, boolean>)?.["manage_channels"]
|
||||
);
|
||||
if (!canManage) {
|
||||
throw new Error("You don't have permission to manage emojis");
|
||||
}
|
||||
|
||||
const emoji = await ctx.db.get(args.emojiId);
|
||||
if (!emoji) throw new Error("Emoji not found");
|
||||
|
||||
await ctx.storage.delete(emoji.storageId);
|
||||
await ctx.db.delete(args.emojiId);
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
@@ -132,4 +132,11 @@ export default defineSchema({
|
||||
afkTimeout: v.number(), // seconds (default 300 = 5 min)
|
||||
iconStorageId: v.optional(v.id("_storage")),
|
||||
}),
|
||||
|
||||
customEmojis: defineTable({
|
||||
name: v.string(),
|
||||
storageId: v.id("_storage"),
|
||||
uploadedBy: v.id("userProfiles"),
|
||||
createdAt: v.number(),
|
||||
}).index("by_name", ["name"]),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user