diff --git a/src/App.svelte b/src/App.svelte index 858769d..d896484 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -24,6 +24,9 @@ let streamLanguage = 'en'; let streamTags = ''; let streamClassificationLabels = ''; + let categorySearch = ''; + let categoryOptions: Array<{ id: string; name: string; box_art_url: string }> = []; + let categorySearchTimer: number | undefined; let oauthLink = ''; let oauthIdentity: 'streamer' | 'bot' = 'streamer'; let oauthBusy = false; @@ -171,9 +174,10 @@ headers: { authorization: `Bearer ${sessionToken}` }, }); if (!response.ok) return; - const info: { title: string; game_id: string; language: string; tags: string[]; content_classification_labels: string[] } = await response.json(); + const info: { title: string; game_id: string; game_name: string; language: string; tags: string[]; content_classification_labels: string[] } = await response.json(); streamTitle = info.title; streamGameId = info.game_id; + categorySearch = info.game_name; streamLanguage = info.language; streamTags = info.tags.join(', '); streamClassificationLabels = info.content_classification_labels.join(', '); @@ -182,6 +186,23 @@ } } + function searchCategories(value: string) { + categorySearch = value; + categoryOptions = []; + if (categorySearchTimer) window.clearTimeout(categorySearchTimer); + if (!value.trim()) return; + categorySearchTimer = window.setTimeout(async () => { + try { + const response = await fetch(`${serverAddress}/api/v1/twitch/games?query=${encodeURIComponent(value.trim())}`, { + headers: { authorization: `Bearer ${sessionToken}` }, + }); + if (response.ok) categoryOptions = await response.json(); + } catch { + categoryOptions = []; + } + }, 200); + } + async function saveStreamInfo() { streamBusy = true; streamError = ''; @@ -315,7 +336,7 @@ STREAM PREVIEW⋮STREAM OFFLINECONNECT TWITCH TO BEGIN ACTIVITY FEED⋮ALL ACTIVITYMODERATION{#if activityEvents.length}{#each activityEvents as event}{event.event_type}{new Date(event.occurred_at).toLocaleTimeString()}{/each}{:else}◌Nothing here yetFollows, subscriptions, raids, and server events will appear here.{/if} MY CHAT⋮⌁Chat is waitingConnect a streamer or bot account to begin.SEND - QUICK ACTIONS⋮ (showStreamEditor = !showStreamEditor)}>EDIT STREAM INFOMODERATIONBOT COMMANDSOVERLAYS{#if showStreamEditor}TITLECATEGORY / GAME IDLANGUAGETAGSCLASSIFICATION LABELSSAVE STREAM INFO{#if streamError}{streamError}{/if}{/if} + QUICK ACTIONS⋮ (showStreamEditor = !showStreamEditor)}>EDIT STREAM INFOMODERATIONBOT COMMANDSOVERLAYS{#if showStreamEditor}TITLECATEGORY searchCategories(event.currentTarget.value)} autocomplete="off" placeholder="Search Twitch categories" />{#if categoryOptions.length}{#each categoryOptions as category} { streamGameId = category.id; categorySearch = category.name; categoryOptions = []; }}>{category.name}{category.id}{/each}{/if}LANGUAGETAGSCLASSIFICATION LABELSSAVE STREAM INFO{#if streamError}{streamError}{/if}{/if} {/if} diff --git a/src/styles.css b/src/styles.css index 0984dc4..a353704 100644 --- a/src/styles.css +++ b/src/styles.css @@ -477,6 +477,22 @@ label { .stream-editor label { margin-top: 4px; } +.category-options { + display: grid; + max-height: 180px; + overflow: auto; + border: 1px solid var(--ui-line); + background: var(--ed-bg); +} +.category-option { + display: flex; + justify-content: space-between; + gap: 8px; + border: 0; + border-bottom: 1px solid var(--ui-divider); + border-radius: 0; + text-align: left; +} .quick-action:hover, .quick-action.accent { border-color: var(--ui-line-focus);