feat: channel permissions UI, DM Sans font, private channel filtering, migration fix
- Rewrite ChannelSettingsModal with full tri-state permission override UI for roles and members (allow/neutral/deny per permission bit) - Switch font from Inter to self-hosted DM Sans (woff2 variable fonts) - Add client-side VIEW_CHANNEL filtering in ChannelSidebar for private channels - Broadcast isPrivate flag on channel override changes - Fix voice permission bit migration: gate behind persistent flag to prevent repeated re-runs that stripped STREAM from @everyone roles - Add speakingUserIds set to voice store for efficient user-level lookups - Clear current channel view when a channel is deleted - Move .glass-strip to @layer utilities for proper CSS specificity - Simplify avatar initials font size to proportional formula
This commit is contained in:
@@ -2,7 +2,7 @@ import type { FastifyInstance } from 'fastify';
|
||||
import type { WebSocket } from 'ws';
|
||||
import { verifyJwt } from '../utils/auth.js';
|
||||
import { getDb, schema } from '../db/index.js';
|
||||
import { eq, inArray, desc, sql } from 'drizzle-orm';
|
||||
import { eq, and, inArray, desc, sql } from 'drizzle-orm';
|
||||
import { handleClientEvent } from './events.js';
|
||||
import { computePermissions, PermissionBits, permissionsToString } from '../utils/permissions.js';
|
||||
import type {
|
||||
@@ -698,6 +698,25 @@ function buildReadyPayload(userId: string): {
|
||||
arr.push(ch);
|
||||
}
|
||||
|
||||
// Batch: determine which channels are private (VIEW_CHANNEL denied on @everyone)
|
||||
// @everyone role ID equals the space ID, so we query for overrides targeting role = spaceId
|
||||
const allEveroneOverrides = batchInArray(
|
||||
spaceIds,
|
||||
ids => db.select().from(schema.channelOverrides).where(
|
||||
and(
|
||||
eq(schema.channelOverrides.targetType, 'role'),
|
||||
inArray(schema.channelOverrides.targetId, ids),
|
||||
)
|
||||
).all(),
|
||||
);
|
||||
const privateChannelIds = new Set<string>();
|
||||
for (const o of allEveroneOverrides) {
|
||||
const denyBits = BigInt(o.deny || '0');
|
||||
if ((denyBits & PermissionBits.VIEW_CHANNEL) !== 0n) {
|
||||
privateChannelIds.add(o.channelId);
|
||||
}
|
||||
}
|
||||
|
||||
// Batch: all categories for all spaces (1 query instead of N)
|
||||
const allCategories = batchInArray(
|
||||
spaceIds,
|
||||
@@ -805,6 +824,7 @@ function buildReadyPayload(userId: string): {
|
||||
topic: ch.topic,
|
||||
position: ch.position ?? 0,
|
||||
categoryId: ch.categoryId ?? null,
|
||||
isPrivate: privateChannelIds.has(ch.id),
|
||||
createdAt: ch.createdAt,
|
||||
lastMessageId: lastMsgMap.get(ch.id) ?? null,
|
||||
myPermissions: permissionsToString(chPerms),
|
||||
|
||||
Reference in New Issue
Block a user