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:
@@ -130,14 +130,20 @@ export function ChannelSidebar() {
|
||||
});
|
||||
}, [collapseKey]);
|
||||
|
||||
// Filter channels by VIEW_CHANNEL (defense-in-depth — server already filters,
|
||||
// but this catches transient races where channels and permissions are briefly out of sync)
|
||||
const visibleChannels = useMemo(() =>
|
||||
channels.filter(ch => hasPermissionBit(channelPermissions.get(ch.id), PermissionBits.VIEW_CHANNEL)),
|
||||
[channels, channelPermissions]);
|
||||
|
||||
// Group channels by category
|
||||
const sortedCategories = useMemo(() =>
|
||||
[...categories].sort((a, b) => a.position - b.position), [categories]);
|
||||
const uncategorizedChannels = useMemo(() =>
|
||||
channels.filter(c => !c.categoryId).sort((a, b) => a.position - b.position), [channels]);
|
||||
visibleChannels.filter(c => !c.categoryId).sort((a, b) => a.position - b.position), [visibleChannels]);
|
||||
const channelsByCategory = useMemo(() => {
|
||||
const map = new Map<string, typeof channels>();
|
||||
for (const ch of channels) {
|
||||
for (const ch of visibleChannels) {
|
||||
if (!ch.categoryId) continue;
|
||||
let arr = map.get(ch.categoryId);
|
||||
if (!arr) { arr = []; map.set(ch.categoryId, arr); }
|
||||
@@ -147,7 +153,7 @@ export function ChannelSidebar() {
|
||||
map.set(key, arr.sort((a, b) => a.position - b.position));
|
||||
}
|
||||
return map;
|
||||
}, [channels]);
|
||||
}, [visibleChannels]);
|
||||
|
||||
// Check if a collapsed category has unread channels
|
||||
const categoryHasUnread = useCallback((categoryId: string) => {
|
||||
|
||||
Reference in New Issue
Block a user