Add Guest Star participant controls
Client checks / checks (push) Canceled after 0s
Client dependency and security checks / audit (push) Canceled after 0s

This commit is contained in:
2026-10-25 06:21:28 +01:00
parent 027e7434f6
commit 8a28596f47
+35
View File
@@ -633,6 +633,40 @@
}
}
async function updateCollaborationSlot(action: 'assign' | 'remove', guestId: string, slotId: string) {
if (!collaborationSession || !slotId.trim()) return;
collaborationBusy = true;
collaborationError = '';
try {
const query = `session_id=${encodeURIComponent(collaborationSession.id)}&guest_id=${encodeURIComponent(guestId)}&slot_id=${encodeURIComponent(slotId.trim())}`;
const response = await fetch(`${serverAddress}/api/v1/twitch/collaboration/slot/${action}?${query}`, { method: action === 'assign' ? 'POST' : 'DELETE', headers: { authorization: `Bearer ${sessionToken}` } });
if (!response.ok) throw new Error(`Twitch could not ${action} the Guest Star slot.`);
collaborationNotice = action === 'assign' ? 'Guest assigned to slot.' : 'Guest removed from slot.';
await loadCollaboration();
} catch (error) {
collaborationError = error instanceof Error ? error.message : 'Guest Star slot update failed.';
} finally {
collaborationBusy = false;
}
}
async function updateCollaborationSlotSettings(guestId: string, slotId: string, isLive: boolean) {
if (!collaborationSession || !slotId.trim()) return;
collaborationBusy = true;
collaborationError = '';
try {
const query = `session_id=${encodeURIComponent(collaborationSession.id)}&guest_id=${encodeURIComponent(guestId)}&slot_id=${encodeURIComponent(slotId.trim())}&is_live=${isLive}`;
const response = await fetch(`${serverAddress}/api/v1/twitch/collaboration/slot/settings?${query}`, { method: 'PATCH', headers: { authorization: `Bearer ${sessionToken}` } });
if (!response.ok) throw new Error('Twitch could not update the Guest Star slot settings.');
collaborationNotice = 'Guest Star slot settings updated.';
await loadCollaboration();
} catch (error) {
collaborationError = error instanceof Error ? error.message : 'Guest Star slot settings update failed.';
} finally {
collaborationBusy = false;
}
}
async function markNotificationSeen(notificationId: string) {
const response = await fetch(`${serverAddress}/api/v1/admin/notifications/${notificationId}/seen`, {
method: 'POST',
@@ -1513,6 +1547,7 @@
</div>
{#if collaborationNotice}<div class="collaboration-notice">{collaborationNotice}</div>{/if}{#if collaborationError}<div class="auth-error">{collaborationError}</div>{/if}
</section>
<section class="panel collaboration-slot-panel"><div class="panel-header"><span>PARTICIPANT CONTROLS</span><span class="micro">TWITCH-AUTHORITATIVE</span></div><div class="settings-body"><p class="settings-note">Assign, remove, or toggle live state for ready participants. Twitch remains authoritative for slot availability.</p>{#each collaborationSession.guests as guest}<div class="collaboration-invite"><span><strong>{guest.user_display_name || guest.user_id}</strong><span class="micro">{guest.slot_id ? ` SLOT ${guest.slot_id}` : ' UNSLOTTED'}</span></span><div class="chat-send-row"><input class="control" value={guest.slot_id || ''} maxlength="8" inputmode="numeric" placeholder="Slot" aria-label={`Slot for ${guest.user_display_name || guest.user_id}`} /><button class="secondary-action" onclick={(event) => { const input = event.currentTarget.parentElement?.querySelector('input') as HTMLInputElement | null; updateCollaborationSlot('assign', guest.user_id, input?.value || ''); }} disabled={collaborationBusy}>ASSIGN</button>{#if guest.slot_id}<button class="secondary-action" onclick={() => updateCollaborationSlot('remove', guest.user_id, guest.slot_id || '')} disabled={collaborationBusy}>REMOVE</button><button class="secondary-action" onclick={() => updateCollaborationSlotSettings(guest.user_id, guest.slot_id || '', !guest.is_live)} disabled={collaborationBusy}>{guest.is_live ? 'SET WAITING' : 'SET LIVE'}</button>{/if}</div></div>{/each}{#if !collaborationSession.guests.length}<div class="empty-row">No participants are ready for slot controls.</div>{/if}</div></section>
{:else}
<section class="collaboration-page panel">
<div class="panel-header"><span>COLLABORATION</span><span class="micro">SEARCH OR REVIEW INCOMING</span></div>