feat: GIF search (Klipy), stickers, emoji picker, and bug fixes

- Add GIF search powered by Klipy API with correct response mapping
  (file.sm/hd tiers, not flat files structure)
- Add sticker system: packs, upload with auto-downscale, send in messages
- Add tabbed InputPopover with emoji, GIF, and sticker pickers
- Fix GIF API key migration race condition (column-add loop vs rename)
- Fix masked API key corruption on settings save (server + client guards)
- Fix sticker packs 403 (reversed isMember parameter order)
- Fix emoji picker not filling popover width (perLine 8→9, CSS 100%)
- Add error logging for Klipy API failures
This commit is contained in:
Jannis Braun
2026-03-15 02:04:37 +01:00
parent 7113f47b17
commit 3de6e4a668
26 changed files with 2160 additions and 50 deletions
+4
View File
@@ -8,6 +8,7 @@ import { broadcastDmMessage, getDmMessageWithUser } from '../routes/dm.js';
import { MAX_MESSAGE_LENGTH, type MessageWithUser, type Attachment, type DmMessageWithUser } from '@backspace/shared';
import { sanitizeUser } from '../utils/sanitize.js';
import { deleteAttachmentFiles } from '../utils/fileCleanup.js';
import { hydrateSticker } from '../routes/messages.js';
/**
* Re-evaluate SPEAK permission for all participants in voice channels
@@ -99,6 +100,7 @@ function getMessageWithUser(messageId: string): MessageWithUser | null {
}
}
const stickerId = (message as any).stickerId ?? null;
return {
id: message.id,
channelId: message.channelId,
@@ -111,6 +113,8 @@ function getMessageWithUser(messageId: string): MessageWithUser | null {
attachments,
reactions,
replyTo,
stickerId,
sticker: hydrateSticker(stickerId),
};
}