feat: DM infrastructure overhaul + volume sliders + LiveKit dynamic URL
- Add DM edit/delete endpoints (REST + WebSocket) - Add DM typing indicators with real-time broadcast - Fix volume sliders to control LiveKit mic/speaker - Fix Send Message button on user profile popout - Add New DM modal with user search - Fix message pagination (SQL cursor instead of in-memory) - Guard optional attachments on DM messages - Dynamic LiveKit URL from request Host header - DM sidebar auto-sorts by most recent message
This commit is contained in:
@@ -117,6 +117,15 @@ function createTables(db: Database.Database): void {
|
||||
UNIQUE(message_id, user_id, emoji)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dm_reactions (
|
||||
id TEXT PRIMARY KEY,
|
||||
dm_message_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
emoji TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
UNIQUE(dm_message_id, user_id, emoji)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
id TEXT PRIMARY KEY,
|
||||
server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
|
||||
|
||||
@@ -22,6 +22,19 @@ export function runMigrations(db: Database.Database): void {
|
||||
columns: [
|
||||
{ name: 'permissions', type: 'TEXT' }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'dm_messages',
|
||||
columns: [
|
||||
{ name: 'edited_at', type: 'INTEGER' },
|
||||
{ name: 'reply_to_id', type: 'TEXT' }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'attachments',
|
||||
columns: [
|
||||
{ name: 'dm_message_id', type: 'TEXT' }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ export const messages = sqliteTable('messages', {
|
||||
export const attachments = sqliteTable('attachments', {
|
||||
id: text('id').primaryKey(),
|
||||
messageId: text('message_id').references(() => messages.id, { onDelete: 'cascade' }),
|
||||
dmMessageId: text('dm_message_id'),
|
||||
filename: text('filename').notNull(),
|
||||
originalName: text('original_name').notNull(),
|
||||
mimetype: text('mimetype').notNull(),
|
||||
@@ -81,7 +82,9 @@ export const dmMessages = sqliteTable('dm_messages', {
|
||||
id: text('id').primaryKey(),
|
||||
dmChannelId: text('dm_channel_id').notNull().references(() => dmChannels.id, { onDelete: 'cascade' }),
|
||||
userId: text('user_id').notNull().references(() => users.id),
|
||||
replyToId: text('reply_to_id'),
|
||||
content: text('content'),
|
||||
editedAt: integer('edited_at'),
|
||||
createdAt: integer('created_at').notNull(),
|
||||
});
|
||||
|
||||
@@ -109,6 +112,14 @@ export const reactions = sqliteTable('reactions', {
|
||||
createdAt: integer('created_at').notNull(),
|
||||
});
|
||||
|
||||
export const dmReactions = sqliteTable('dm_reactions', {
|
||||
id: text('id').primaryKey(),
|
||||
dmMessageId: text('dm_message_id').notNull(),
|
||||
userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
emoji: text('emoji').notNull(),
|
||||
createdAt: integer('created_at').notNull(),
|
||||
});
|
||||
|
||||
export const roles = sqliteTable('roles', {
|
||||
id: text('id').primaryKey(),
|
||||
serverId: text('server_id').notNull().references(() => servers.id, { onDelete: 'cascade' }),
|
||||
|
||||
Reference in New Issue
Block a user