diff --git a/src/App.svelte b/src/App.svelte index 41a186a..600d2f5 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -12,6 +12,10 @@ let newPassword = ''; let authError = ''; let authBusy = false; + let oauthLink = ''; + let oauthIdentity: 'streamer' | 'bot' = 'streamer'; + let oauthBusy = false; + let oauthError = ''; async function checkServer() { connectionState = 'CONNECTING'; @@ -82,6 +86,33 @@ sessionToken = ''; authenticated = false; mustReplaceCredentials = false; + oauthLink = ''; + } + + async function createOAuthLink(identity: 'streamer' | 'bot') { + oauthBusy = true; + oauthError = ''; + oauthIdentity = identity; + oauthLink = ''; + try { + const response = await fetch(`${serverAddress}/api/v1/twitch/oauth/link?identity=${identity}`, { + headers: { authorization: `Bearer ${sessionToken}` }, + }); + if (!response.ok) throw new Error('The server could not create the Twitch authorization link.'); + const result: { authorization_url: string } = await response.json(); + oauthLink = result.authorization_url; + } catch (error) { + oauthError = error instanceof Error ? error.message : 'The Twitch authorization link could not be created.'; + } finally { + oauthBusy = false; + } + } + + async function copyOAuthLink() { + if (!oauthLink) return; + await navigator.clipboard.writeText(oauthLink).catch(() => { + oauthError = 'Copy was unavailable. Select and copy the link manually.'; + }); } @@ -122,7 +153,26 @@ {#if authError}
{authError}
{/if} {/if} -
CHANNEL
No Twitch account connected
+
+
TWITCH ACCOUNTS
+ {#if authenticated} +
+ + +
+ {#if oauthLink} + + {/if} + {#if oauthError}
{oauthError}
{/if} + {:else} +
Sign in as administrator to connect accounts.
+ {/if} +
diff --git a/src/styles.css b/src/styles.css index 12385b1..0c9ee9e 100644 --- a/src/styles.css +++ b/src/styles.css @@ -285,6 +285,36 @@ label { color: var(--ed-dim); font-size: 11px; } +.oauth-actions { + display: grid; + gap: 6px; + margin-top: 9px; +} +.secondary-action { + width: 100%; + padding: 6px; + color: var(--ed-dim); + font-size: 10px; +} +.oauth-link-card { + display: grid; + gap: 6px; + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid var(--ui-divider); +} +.oauth-link { + width: 100%; + min-height: 72px; + resize: vertical; + overflow-wrap: anywhere; + color: var(--ed-dim); + font-size: 9px; +} +.oauth-hint { + color: var(--ed-dim); + font: 9px/1.4 var(--font-data); +} .mono { font-family: var(--font-data); }