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
+52
View File
@@ -192,6 +192,8 @@ export interface MessageWithUser extends Message {
attachments: Attachment[];
reactions: Reaction[];
replyTo?: MessageWithUser | null;
stickerId?: string | null;
sticker?: Sticker | null;
}
// ─── Reaction Types ────────────────────────────────────────────────────────
@@ -255,6 +257,8 @@ export interface DmMessageWithUser extends DmMessage {
attachments: Attachment[];
reactions: Reaction[];
replyTo?: DmMessageWithUser | null;
stickerId?: string | null;
sticker?: Sticker | null;
}
// ─── WebSocket Event Types ──────────────────────────────────────────────────
@@ -338,6 +342,11 @@ export type ServerEvent =
| { type: 'category_deleted'; categoryId: string; spaceId: string }
| { type: 'channel_layout_updated'; spaceId: string; channels: Channel[]; categories: ChannelCategory[] }
| { type: 'space_layout_updated'; layout: SpaceLayoutItem[]; folders: SpaceFolder[]; updatedAt?: number }
| { type: 'sticker_pack_created'; spaceId: string; pack: StickerPack }
| { type: 'sticker_pack_updated'; spaceId: string; pack: StickerPack }
| { type: 'sticker_pack_deleted'; spaceId: string; packId: string }
| { type: 'sticker_created'; spaceId: string; sticker: Sticker }
| { type: 'sticker_deleted'; spaceId: string; stickerId: string }
| { type: 'pong' }
| { type: 'error'; message: string };
@@ -417,6 +426,7 @@ export interface CreateMessageRequest {
content: string;
attachments?: string[];
replyToId?: string;
stickerId?: string;
}
export interface UpdateMessageRequest {
@@ -448,6 +458,7 @@ export interface CreateDmMessageRequest {
content?: string;
attachments?: string[];
replyToId?: string;
stickerId?: string;
}
export interface PaginatedQuery {
@@ -517,12 +528,53 @@ export interface UpdateFriendRequest {
status: 'accepted' | 'declined';
}
// ─── GIF Types ──────────────────────────────────────────────────────────────
export interface GifResult {
id: string;
title: string;
previewUrl: string;
url: string;
width: number;
height: number;
}
// ─── Sticker Types ──────────────────────────────────────────────────────────
export interface Sticker {
id: string;
packId: string;
spaceId: string;
name: string;
tags: string;
filename: string;
mimetype: string;
size: number;
width: number | null;
height: number | null;
uploadedBy: string;
createdAt: number;
}
export interface StickerPack {
id: string;
spaceId: string;
name: string;
description: string | null;
createdBy: string;
createdAt: number;
stickers: Sticker[];
spaceName?: string;
}
// ─── Instance Settings Types ────────────────────────────────────────────────
export interface InstanceAdminSettings {
instanceName: string;
registrationOpen: boolean;
discoveryEnabled: boolean;
gifApiKey?: string;
gifEnabled?: boolean;
}
export interface InstanceStreamingLimits {