diff --git a/frontend/src/locale/en/translation.json b/frontend/src/locale/en/translation.json
index 1a4fa11..21ce389 100644
--- a/frontend/src/locale/en/translation.json
+++ b/frontend/src/locale/en/translation.json
@@ -286,7 +286,10 @@
},
"quick-links": "Useful links",
"link-user-guide": "User guide",
- "link-api": "API reference"
+ "link-api": "API reference",
+ "problems": {
+ "eventsub-scope": "{{APPNAME}} needs new permissions in your Twitch app to work correctly.
Click here to re-authenticate."
+ }
},
"onboarding": {
"welcome-header": "Welcome to {{APPNAME}}",
diff --git a/frontend/src/ui/pages/Dashboard.tsx b/frontend/src/ui/pages/Dashboard.tsx
index 19056ec..8db3bd1 100644
--- a/frontend/src/ui/pages/Dashboard.tsx
+++ b/frontend/src/ui/pages/Dashboard.tsx
@@ -1,4 +1,9 @@
-import { CircleIcon, InfoCircledIcon, UpdateIcon } from '@radix-ui/react-icons';
+import {
+ CircleIcon,
+ ExclamationTriangleIcon,
+ InfoCircledIcon,
+ UpdateIcon,
+} from '@radix-ui/react-icons';
import { Trans, useTranslation } from 'react-i18next';
import {
EventSubNotification,
@@ -9,11 +14,16 @@ import { useLiveKey, useModule } from '~/lib/react';
import { useAppDispatch, useAppSelector } from '~/store';
import { modules } from '~/store/api/reducer';
import * as HoverCard from '@radix-ui/react-hover-card';
+import { useEffect, useState } from 'react';
+import { main } from '@wailsapp/go/models';
+import { GetProblems, GetTwitchAuthURL } from '@wailsapp/go/main/App';
+import { BrowserOpenURL } from '@wailsapp/runtime/runtime';
import {
PageContainer,
SectionHeader,
styled,
TextBlock,
+ theme,
TooltipContent,
} from '../theme';
import BrowserLink from '../components/BrowserLink';
@@ -440,8 +450,88 @@ function TwitchSection() {
);
}
+const ProblemBlock = styled('div', {
+ border: '2px solid $gray6',
+ padding: '0.5rem 1rem',
+ borderRadius: theme.borderRadius.toolbar,
+ variants: {
+ severity: {
+ warn: {
+ borderColor: '$yellow6',
+ backgroundColor: '$yellow3',
+ color: '$yellow12',
+ svg: {
+ color: '$yellow11',
+ },
+ },
+ },
+ },
+ display: 'flex',
+ gap: '1rem',
+ alignItems: 'center',
+ lineHeight: '1.4',
+ svg: {
+ marginTop: '0.25rem',
+ },
+ a: {
+ cursor: 'pointer',
+ },
+});
+
function ProblemList() {
- return <>>;
+ const [problems, setProblems] = useState([]);
+ const { t } = useTranslation();
+ const kv = useAppSelector((state) => state.api.client);
+
+ useEffect(() => {
+ void GetProblems().then(setProblems);
+ }, []);
+
+ const reauthenticate = async () => {
+ // Wait for re-auth so we can clear the banner
+ const onKeyChange = () => {
+ void GetProblems().then(setProblems);
+ void kv.unsubscribeKey('twitch/auth-keys', onKeyChange);
+ };
+ void kv.subscribeKey('twitch/auth-keys', onKeyChange);
+
+ const url = await GetTwitchAuthURL();
+ BrowserOpenURL(url);
+ };
+
+ return (
+ <>
+ {problems.map((p) => {
+ switch (p.id) {
+ case 'twitch:eventsub_scope':
+ return (
+
+
+
+ {
+ void reauthenticate();
+ }}
+ >
+ ),
+ }}
+ />
+
+
+ );
+ default:
+ return null;
+ }
+ })}
+ >
+ );
}
export default function Dashboard(): React.ReactElement {
diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts
index 2e06400..076a83a 100644
--- a/frontend/wailsjs/go/main/App.d.ts
+++ b/frontend/wailsjs/go/main/App.d.ts
@@ -16,6 +16,8 @@ export function GetKilovoltBind():Promise;
export function GetLastLogs():Promise>;
+export function GetProblems():Promise>;
+
export function GetTwitchAuthURL():Promise;
export function GetTwitchLoggedUser():Promise;
diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js
index cea3de4..890f9aa 100644
--- a/frontend/wailsjs/go/main/App.js
+++ b/frontend/wailsjs/go/main/App.js
@@ -26,6 +26,10 @@ export function GetLastLogs() {
return window['go']['main']['App']['GetLastLogs']();
}
+export function GetProblems() {
+ return window['go']['main']['App']['GetProblems']();
+}
+
export function GetTwitchAuthURL() {
return window['go']['main']['App']['GetTwitchAuthURL']();
}
diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts
index 72a84f2..9a70c56 100644
--- a/frontend/wailsjs/go/models.ts
+++ b/frontend/wailsjs/go/models.ts
@@ -92,6 +92,20 @@ export namespace main {
this.data = source["data"];
}
}
+ export class Problem {
+ id: string;
+ details: any;
+
+ static createFrom(source: any = {}) {
+ return new Problem(source);
+ }
+
+ constructor(source: any = {}) {
+ if ('string' === typeof source) source = JSON.parse(source);
+ this.id = source["id"];
+ this.details = source["details"];
+ }
+ }
export class VersionInfo {
release: string;
// Go type: debug
diff --git a/go.mod b/go.mod
index 3298e32..4a4d875 100644
--- a/go.mod
+++ b/go.mod
@@ -22,6 +22,8 @@ require (
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
+replace github.com/nicklaw5/helix/v2 => github.com/ashkeel/helix/v2 v2.26.0-chat.2
+
require (
github.com/DataDog/zstd v1.4.5 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
diff --git a/go.sum b/go.sum
index 504727f..e1416d1 100644
--- a/go.sum
+++ b/go.sum
@@ -54,6 +54,8 @@ github.com/apenwarr/fixconsole v0.0.0-20191012055117-5a9f6489cc29 h1:muXWUcay7DD
github.com/apenwarr/fixconsole v0.0.0-20191012055117-5a9f6489cc29/go.mod h1:JYWahgHer+Z2xbsgHPtaDYVWzeHDminu+YIBWkxpCAY=
github.com/apenwarr/w32 v0.0.0-20190407065021-aa00fece76ab h1:CMGzRRCjnD50RjUFSArBLuCxiDvdp7b8YPAcikBEQ+k=
github.com/apenwarr/w32 v0.0.0-20190407065021-aa00fece76ab/go.mod h1:nfFtvHn2Hgs9G1u0/J6LHQv//EksNC+7G8vXmd1VTJ8=
+github.com/ashkeel/helix/v2 v2.26.0-chat.2 h1:dedyfwwLEAegbeBuyMhvs4X608bN7YBYjuZ6rT5IOTA=
+github.com/ashkeel/helix/v2 v2.26.0-chat.2/go.mod h1:zZcKsyyBWDli34x3QleYsVMiiNGMXPAEU5NjsiZDtvY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -253,8 +255,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/nicklaw5/helix/v2 v2.26.0 h1:Qkc/R0eCDdWtUmnczk2g03+mObPUfc49Kz2Bt4B5d0g=
-github.com/nicklaw5/helix/v2 v2.26.0/go.mod h1:zZcKsyyBWDli34x3QleYsVMiiNGMXPAEU5NjsiZDtvY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
diff --git a/problem.go b/problem.go
new file mode 100644
index 0000000..7b7ac70
--- /dev/null
+++ b/problem.go
@@ -0,0 +1,38 @@
+package main
+
+import "go.uber.org/zap"
+
+type ProblemID string
+
+const (
+ ProblemIDTwitchNoApp = "twitch:app_missing"
+ ProblemIDEventSubNoAuth = "twitch:eventsub_no_auth"
+ ProblemIDEventSubScope = "twitch:eventsub_scope"
+)
+
+type Problem struct {
+ ID ProblemID `json:"id"`
+ Details any `json:"details"`
+}
+
+func (a *App) GetProblems() (problems []Problem) {
+ problems = []Problem{}
+ if a.twitchManager != nil {
+ client := a.twitchManager.Client()
+ if client != nil {
+ // Check if the app needs to be authorized again
+ scopesMatch, err := client.CheckScopes()
+ if err != nil {
+ logger.Warn("Could not check scopes for problems", zap.Error(err))
+ } else {
+ if !scopesMatch {
+ problems = append(problems, Problem{
+ ID: ProblemIDEventSubScope,
+ Details: nil,
+ })
+ }
+ }
+ }
+ }
+ return
+}