Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
5.8 kB · 187 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188import { useEffect, useState } from "react";import { place } from "streamplace";import { useLivestreamStore } from "../livestream-store";import { usePDSAgent } from "./xrpc";
export interface ModerationPermissions { canBan: boolean; canHide: boolean; canPin: boolean; canManageLivestream: boolean; isOwner: boolean; isLoading: boolean; error: string | null;}
/** * Hook to check if the current user can moderate for a given streamer. * Returns permission flags based on: * - Owner: full permissions if userDID === streamerDID * - Delegated: permissions from place.stream.moderation.permission records */export function useCanModerate( streamerDID: string | null | undefined,): ModerationPermissions { const agent = usePDSAgent(); const userDID = agent?.did;
// Get moderation permissions from livestream store (updated via WebSocket) const moderationPermissions = useLivestreamStore( (state) => state.moderationPermissions, ); const setModerationPermissions = useLivestreamStore( (state) => state.setModerationPermissions, );
const [isOwner, setIsOwner] = useState(false); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState<string | null>(null);
useEffect(() => { if (!userDID || !streamerDID) { setModerationPermissions([]); setIsOwner(false); setError(null); return; }
// If user is the streamer, they have full permissions if (userDID === streamerDID) { setIsOwner(true); setModerationPermissions([]); // Not needed for owner setIsLoading(false); setError(null); return; }
// Otherwise, fetch delegation records from the streamer's repo // This initial fetch populates the store, then WebSocket updates will keep it in sync const fetchDelegation = async () => { if (!agent) { setModerationPermissions([]); setIsLoading(false); return; }
setIsLoading(true); setError(null); setIsOwner(false);
try { // Use authenticated agent to list permission records from the streamer's repo const result = await agent.com.atproto.repo.listRecords({ repo: streamerDID, collection: "place.stream.moderation.permission", limit: 100, });
const records = result.data.records || []; const permissionRecords: place.stream.moderation.permission.Main[] = records .map((r: { value: any }) => r.value) .filter( (v: any) => v && v.$type === "place.stream.moderation.permission", );
// Store all permissions in the livestream store // WebSocket updates will keep this in sync setModerationPermissions(permissionRecords); } catch (err) { console.error("[useCanModerate] Error fetching permissions:", err); setError( `Could not fetch moderation permissions: ${err instanceof Error ? err.message : `Unknown error: ${err}`}`, ); setModerationPermissions([]); } finally { setIsLoading(false); } };
// Fetch immediately on mount or when dependencies change fetchDelegation(); }, [userDID, streamerDID, agent, setModerationPermissions]);
// If permissions were cleared (e.g., due to deletion), trigger a refetch useEffect(() => { // If permissions were cleared and we're not the owner, refetch if ( moderationPermissions.length === 0 && !isOwner && userDID && streamerDID && agent ) { const fetchDelegation = async () => { try { const result = await agent.com.atproto.repo.listRecords({ repo: streamerDID, collection: "place.stream.moderation.permission", limit: 100, });
const records = result.data.records || []; const permissionRecords: place.stream.moderation.permission.Main[] = records .map((r: { value: any }) => r.value) .filter( (v: any) => v && v.$type === "place.stream.moderation.permission", );
setModerationPermissions(permissionRecords); } catch (err) { console.error("[useCanModerate] Error refetching permissions:", err); } };
// Small delay to avoid rapid refetches const timeout = setTimeout(fetchDelegation, 100); return () => clearTimeout(timeout); } }, [ moderationPermissions.length, isOwner, userDID, streamerDID, agent, setModerationPermissions, ]);
// Find ALL delegation records for this moderator and merge their permissions const delegations = moderationPermissions.filter( (perm) => perm.moderator === userDID, );
// 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 } }
// 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"), canHide: isOwner || permissions.includes("hide"), canPin: isOwner || permissions.includes("message.pin"), canManageLivestream: isOwner || permissions.includes("livestream.manage"), isOwner, isLoading, error, };}