diff --git a/js/components/src/livestream-store/websocket-consumer.tsx b/js/components/src/livestream-store/websocket-consumer.tsx index da7762e46..35d0cce63 100644 --- a/js/components/src/livestream-store/websocket-consumer.tsx +++ b/js/components/src/livestream-store/websocket-consumer.tsx @@ -144,18 +144,33 @@ export const handleWebSocketMessages = ( }; } else { // Handle new/updated permission: add or update in the list - const newPerm = permRecord as PlaceStreamModerationPermission.Record; - const existingIndex = state.moderationPermissions.findIndex( - (p) => p.moderator === newPerm.moderator, - ); + // Use createdAt as a unique identifier since multiple records can exist for the same moderator + // (e.g., one record with "ban" permission, another with "hide" permission) + // Note: rkey would be ideal but isn't always present in the WebSocket message + const newPerm = permRecord as PlaceStreamModerationPermission.Record & { + rkey?: string; + }; + const existingIndex = state.moderationPermissions.findIndex((p) => { + const pWithRkey = p as PlaceStreamModerationPermission.Record & { + rkey?: string; + }; + // Prefer matching by rkey if available, fall back to createdAt + if (newPerm.rkey && pWithRkey.rkey) { + return pWithRkey.rkey === newPerm.rkey; + } + return ( + p.moderator === newPerm.moderator && + p.createdAt === newPerm.createdAt + ); + }); let newPermissions: PlaceStreamModerationPermission.Record[]; if (existingIndex >= 0) { - // Update existing + // Update existing record with same moderator AND createdAt newPermissions = [...state.moderationPermissions]; newPermissions[existingIndex] = newPerm; } else { - // Add new + // Add new record (could be a new record for an existing moderator with different permissions) newPermissions = [...state.moderationPermissions, newPerm]; } diff --git a/js/components/src/streamplace-store/moderation.tsx b/js/components/src/streamplace-store/moderation.tsx index 65f7e97c6..7169b8d26 100644 --- a/js/components/src/streamplace-store/moderation.tsx +++ b/js/components/src/streamplace-store/moderation.tsx @@ -146,23 +146,33 @@ export function useCanModerate( setModerationPermissions, ]); - const delegation = moderationPermissions.find( + // Find ALL delegation records for this moderator and merge their permissions + const delegations = moderationPermissions.filter( (perm) => perm.moderator === userDID, ); - // Extract permissions from the delegation record - const permissions: string[] = delegation - ? (() => { - // Check if delegation has expired - if (delegation.expirationTime) { - const expiration = new Date(delegation.expirationTime); - if (new Date() > expiration) { - return []; - } + // Merge permissions from all delegation records for this moderator + const permissions: string[] = delegations.reduce( + (acc: string[], delegation) => { + // Check if delegation has expired + if (delegation.expirationTime) { + const expiration = new Date(delegation.expirationTime); + if (new Date() > expiration) { + return acc; // Skip expired delegations } - return delegation.permissions || []; - })() - : []; + } + + // Add all permissions from this delegation, avoiding duplicates + const delegationPerms = delegation.permissions || []; + for (const perm of delegationPerms) { + if (!acc.includes(perm)) { + acc.push(perm); + } + } + return acc; + }, + [], + ); return { canBan: isOwner || permissions.includes("ban"),