feat: render activity history and live events
This commit is contained in:
+21
-1
@@ -15,6 +15,7 @@
|
|||||||
let connectionNotice = '';
|
let connectionNotice = '';
|
||||||
let realtimeState = 'DISCONNECTED';
|
let realtimeState = 'DISCONNECTED';
|
||||||
let realtimeSocket: WebSocket | undefined;
|
let realtimeSocket: WebSocket | undefined;
|
||||||
|
let activityEvents: Array<{ event_id: string; event_type: string; occurred_at: string }> = [];
|
||||||
let oauthLink = '';
|
let oauthLink = '';
|
||||||
let oauthIdentity: 'streamer' | 'bot' = 'streamer';
|
let oauthIdentity: 'streamer' | 'bot' = 'streamer';
|
||||||
let oauthBusy = false;
|
let oauthBusy = false;
|
||||||
@@ -62,6 +63,7 @@
|
|||||||
authenticated = true;
|
authenticated = true;
|
||||||
mustReplaceCredentials = result.must_replace_credentials;
|
mustReplaceCredentials = result.must_replace_credentials;
|
||||||
connectionState = 'CONNECTED';
|
connectionState = 'CONNECTED';
|
||||||
|
await loadActivity();
|
||||||
connectRealtime();
|
connectRealtime();
|
||||||
if (!mustReplaceCredentials) password = '';
|
if (!mustReplaceCredentials) password = '';
|
||||||
if (mustReplaceCredentials) newUsername = username;
|
if (mustReplaceCredentials) newUsername = username;
|
||||||
@@ -123,6 +125,12 @@
|
|||||||
try {
|
try {
|
||||||
const event: { event_type?: string } = JSON.parse(message.data);
|
const event: { event_type?: string } = JSON.parse(message.data);
|
||||||
if (event.event_type === 'connection.ready') realtimeState = 'CONNECTED';
|
if (event.event_type === 'connection.ready') realtimeState = 'CONNECTED';
|
||||||
|
else if ('event_id' in event && event.event_type) {
|
||||||
|
const activity = event as { event_id: string; event_type: string; occurred_at: string };
|
||||||
|
if (!activityEvents.some((item) => item.event_id === activity.event_id)) {
|
||||||
|
activityEvents = [activity, ...activityEvents].slice(0, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
realtimeState = 'DISCONNECTED';
|
realtimeState = 'DISCONNECTED';
|
||||||
}
|
}
|
||||||
@@ -136,6 +144,18 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadActivity() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${serverAddress}/api/v1/activity?limit=100`, {
|
||||||
|
headers: { authorization: `Bearer ${sessionToken}` },
|
||||||
|
});
|
||||||
|
if (!response.ok) return;
|
||||||
|
activityEvents = await response.json();
|
||||||
|
} catch {
|
||||||
|
activityEvents = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function createOAuthLink(identity: 'streamer' | 'bot') {
|
async function createOAuthLink(identity: 'streamer' | 'bot') {
|
||||||
oauthBusy = true;
|
oauthBusy = true;
|
||||||
oauthError = '';
|
oauthError = '';
|
||||||
@@ -243,7 +263,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<div class="dashboard-grid">
|
<div class="dashboard-grid">
|
||||||
<section class="panel preview-panel"><div class="panel-header"><span>STREAM PREVIEW</span><span class="panel-menu">⋮</span></div><div class="preview-stage"><div class="offline-mark">STREAM OFFLINE</div><div class="offline-subtitle">CONNECT TWITCH TO BEGIN</div></div><div class="panel-footer"><span class="micro">VIDEO PLAYER</span><span class="badge">OFFLINE</span></div></section>
|
<section class="panel preview-panel"><div class="panel-header"><span>STREAM PREVIEW</span><span class="panel-menu">⋮</span></div><div class="preview-stage"><div class="offline-mark">STREAM OFFLINE</div><div class="offline-subtitle">CONNECT TWITCH TO BEGIN</div></div><div class="panel-footer"><span class="micro">VIDEO PLAYER</span><span class="badge">OFFLINE</span></div></section>
|
||||||
<section class="panel activity-panel"><div class="panel-header"><span>ACTIVITY FEED</span><span class="panel-menu">⋮</span></div><div class="panel-tabs"><button class="tab active">ALL ACTIVITY</button><button class="tab">MODERATION</button></div><div class="empty-state"><div class="empty-glyph">◌</div><strong>Nothing here yet</strong><span>Follows, subscriptions, raids, and server events will appear here.</span></div></section>
|
<section class="panel activity-panel"><div class="panel-header"><span>ACTIVITY FEED</span><span class="panel-menu">⋮</span></div><div class="panel-tabs"><button class="tab active">ALL ACTIVITY</button><button class="tab">MODERATION</button></div>{#if activityEvents.length}<div class="activity-list">{#each activityEvents as event}<div class="activity-row"><span class="activity-type">{event.event_type}</span><span class="activity-time">{new Date(event.occurred_at).toLocaleTimeString()}</span></div>{/each}</div>{:else}<div class="empty-state"><div class="empty-glyph">◌</div><strong>Nothing here yet</strong><span>Follows, subscriptions, raids, and server events will appear here.</span></div>{/if}</section>
|
||||||
<section class="panel chat-panel"><div class="panel-header"><span>MY CHAT</span><span class="panel-menu">⋮</span></div><div class="chat-empty"><div class="empty-glyph">⌁</div><strong>Chat is waiting</strong><span>Connect a streamer or bot account to begin.</span></div><div class="chat-composer"><input class="control" placeholder="Send a message" disabled /><button class="primary" disabled>SEND</button></div></section>
|
<section class="panel chat-panel"><div class="panel-header"><span>MY CHAT</span><span class="panel-menu">⋮</span></div><div class="chat-empty"><div class="empty-glyph">⌁</div><strong>Chat is waiting</strong><span>Connect a streamer or bot account to begin.</span></div><div class="chat-composer"><input class="control" placeholder="Send a message" disabled /><button class="primary" disabled>SEND</button></div></section>
|
||||||
<section class="panel quick-panel"><div class="panel-header"><span>QUICK ACTIONS</span><span class="panel-menu">⋮</span></div><div class="quick-grid"><button class="quick-action accent">EDIT STREAM INFO</button><button class="quick-action">MODERATION</button><button class="quick-action">BOT COMMANDS</button><button class="quick-action">OVERLAYS</button></div></section>
|
<section class="panel quick-panel"><div class="panel-header"><span>QUICK ACTIONS</span><span class="panel-menu">⋮</span></div><div class="quick-grid"><button class="quick-action accent">EDIT STREAM INFO</button><button class="quick-action">MODERATION</button><button class="quick-action">BOT COMMANDS</button><button class="quick-action">OVERLAYS</button></div></section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -425,6 +425,24 @@ label {
|
|||||||
color: var(--ed-accent);
|
color: var(--ed-accent);
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
|
.activity-list {
|
||||||
|
height: calc(100% - 70px);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.activity-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 9px 12px;
|
||||||
|
border-bottom: 1px solid var(--ui-divider);
|
||||||
|
font: 10px var(--font-data);
|
||||||
|
}
|
||||||
|
.activity-type {
|
||||||
|
color: var(--ed-fg);
|
||||||
|
}
|
||||||
|
.activity-time {
|
||||||
|
color: var(--ed-dim);
|
||||||
|
}
|
||||||
.chat-composer {
|
.chat-composer {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
|||||||
Reference in New Issue
Block a user