mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-11-09 16:50:01 +00:00
Implement dashboard content
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
9004ced776
commit
2df250aad9
16 changed files with 444 additions and 10 deletions
|
@ -11,6 +11,7 @@ const buildOpts = {
|
||||||
entryPoints: ['src/main.ts'],
|
entryPoints: ['src/main.ts'],
|
||||||
legalComments: 'none',
|
legalComments: 'none',
|
||||||
loader: {
|
loader: {
|
||||||
|
'.md': 'text',
|
||||||
'.ttf': 'file',
|
'.ttf': 'file',
|
||||||
'.woff2': 'file',
|
'.woff2': 'file',
|
||||||
},
|
},
|
||||||
|
|
13
package-lock.json
generated
13
package-lock.json
generated
|
@ -8,6 +8,7 @@
|
||||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"codejar": "^4.2.0",
|
"codejar": "^4.2.0",
|
||||||
|
"marked": "^13.0.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"prismjs": "^1.29.0",
|
"prismjs": "^1.29.0",
|
||||||
"vue": "^3.4.28",
|
"vue": "^3.4.28",
|
||||||
|
@ -2687,6 +2688,18 @@
|
||||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/marked": {
|
||||||
|
"version": "13.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz",
|
||||||
|
"integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"marked": "bin/marked.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"codejar": "^4.2.0",
|
"codejar": "^4.2.0",
|
||||||
|
"marked": "^13.0.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"prismjs": "^1.29.0",
|
"prismjs": "^1.29.0",
|
||||||
"vue": "^3.4.28",
|
"vue": "^3.4.28",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<nav class="navbar navbar-expand-lg bg-body-tertiary user-select-none">
|
<nav class="navbar fixed-top navbar-expand-lg bg-body-tertiary user-select-none">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<span class="navbar-brand user-select-none">
|
<span class="navbar-brand user-select-none">
|
||||||
<i class="fas fa-robot fa-fw me-1 text-info" />
|
<i class="fas fa-robot fa-fw me-1 text-info" />
|
||||||
|
@ -91,7 +91,7 @@ export default defineComponent({
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
.nav-profile-image {
|
.nav-profile-image {
|
||||||
max-width: 24px;
|
max-width: 24px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, type PropType } from 'vue'
|
import { defineComponent, type PropType } from 'vue'
|
||||||
import { Toast } from 'bootstrap'
|
import { Toast } from 'bootstrap'
|
||||||
|
|
|
@ -33,6 +33,7 @@ import Toaster from './_toaster.vue'
|
||||||
min-height: calc(100vh - 56px);
|
min-height: calc(100vh - 56px);
|
||||||
min-width: 1;
|
min-width: 1;
|
||||||
padding-left: 225px;
|
padding-left: 225px;
|
||||||
|
padding-top: 56px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,54 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container my-3">
|
||||||
<!---->
|
<div class="row justify-content-center">
|
||||||
|
<!-- Here Number Scheduled Events Panel -->
|
||||||
|
<div
|
||||||
|
v-for="component in statusComponents"
|
||||||
|
:key="component"
|
||||||
|
class="col col-2"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="component"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col">
|
||||||
|
<!-- Here Bot-Logs: #44 -->
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<DashboardChangelog />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import DashboardActiveRaffles from './dashboard/activeRaffles.vue'
|
||||||
|
import DashboardBotScopes from './dashboard/scopes.vue'
|
||||||
|
import DashboardChangelog from './dashboard/changelog.vue'
|
||||||
|
import DashboardHealthCheck from './dashboard/healthcheck.vue'
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
DashboardActiveRaffles,
|
||||||
|
DashboardBotScopes,
|
||||||
|
DashboardChangelog,
|
||||||
|
DashboardHealthCheck,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
statusComponents: [
|
||||||
|
'DashboardHealthCheck',
|
||||||
|
'DashboardBotScopes',
|
||||||
|
'DashboardActiveRaffles',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
name: 'TwitchBotEditorDashboard',
|
name: 'TwitchBotEditorDashboard',
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
107
src/components/dashboard/_statuspanel.vue
Normal file
107
src/components/dashboard/_statuspanel.vue
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="cardClass"
|
||||||
|
@click="navigate"
|
||||||
|
>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="fs-6 text-center">
|
||||||
|
{{ header }}
|
||||||
|
</div>
|
||||||
|
<template v-if="loading">
|
||||||
|
<div class="fs-1 text-center">
|
||||||
|
<i class="fa-solid fa-circle-notch fa-spin" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div :class="valueClass">
|
||||||
|
{{ value }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
v-if="caption"
|
||||||
|
class="text-muted text-center"
|
||||||
|
>
|
||||||
|
<small>{{ caption }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, type PropType } from 'vue'
|
||||||
|
import { type RouteLocationRaw } from 'vue-router'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
computed: {
|
||||||
|
cardClass(): string {
|
||||||
|
const classList = ['card user-select-none']
|
||||||
|
|
||||||
|
if (this.clickRoute) {
|
||||||
|
classList.push('pointer-click')
|
||||||
|
}
|
||||||
|
|
||||||
|
return classList.join(' ')
|
||||||
|
},
|
||||||
|
|
||||||
|
valueClass(): string {
|
||||||
|
const classList = ['fs-1 text-center']
|
||||||
|
|
||||||
|
if (this.valueExtraClass) {
|
||||||
|
classList.push(this.valueExtraClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
return classList.join(' ')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
navigate(): void {
|
||||||
|
if (!this.clickRoute) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$router.push(this.clickRoute)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
name: 'DashboardStatusPanel',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
caption: {
|
||||||
|
default: null,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
clickRoute: {
|
||||||
|
default: null,
|
||||||
|
type: {} as PropType<RouteLocationRaw>,
|
||||||
|
},
|
||||||
|
|
||||||
|
header: {
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
loading: {
|
||||||
|
default: false,
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
|
||||||
|
value: {
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
valueExtraClass: {
|
||||||
|
default: null,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pointer-click {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
52
src/components/dashboard/activeRaffles.vue
Normal file
52
src/components/dashboard/activeRaffles.vue
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<template>
|
||||||
|
<StatusPanel
|
||||||
|
:header="$t('dashboard.activeRaffles.header')"
|
||||||
|
:loading="loading"
|
||||||
|
:value="value"
|
||||||
|
:click-route="{name: 'rafflesList'}"
|
||||||
|
:caption="$t('dashboard.activeRaffles.caption')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import BusEventTypes from '../../helpers/busevents'
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import StatusPanel from './_statuspanel.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { StatusPanel },
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
value(): string {
|
||||||
|
return `${this.activeRaffles}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeRaffles: 0,
|
||||||
|
loading: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetchRaffleCount(): void {
|
||||||
|
fetch('raffle/', this.$root?.fetchOpts)
|
||||||
|
.then((resp: Response) => this.$root?.parseResponseFromJSON(resp))
|
||||||
|
.then((data: any) => {
|
||||||
|
this.activeRaffles = data.filter((raffle: any) => raffle.status === 'active').length
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.bus.on(BusEventTypes.RaffleChanged, () => {
|
||||||
|
this.fetchRaffleCount()
|
||||||
|
})
|
||||||
|
this.fetchRaffleCount()
|
||||||
|
},
|
||||||
|
|
||||||
|
name: 'DashboardBotRaffles',
|
||||||
|
})
|
||||||
|
</script>
|
59
src/components/dashboard/changelog.vue
Normal file
59
src/components/dashboard/changelog.vue
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<template>
|
||||||
|
<div class="card user-select-none">
|
||||||
|
<div class="card-header">
|
||||||
|
{{ $t('dashboard.changelog.heading') }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="card-body"
|
||||||
|
v-html="changelog"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
// @ts-ignore - Has an esbuild loader to be loaded as text
|
||||||
|
import ChangeLog from '../../../History.md'
|
||||||
|
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import { parse as marked } from 'marked'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
computed: {
|
||||||
|
changelog(): string {
|
||||||
|
const latestVersions = (ChangeLog as string)
|
||||||
|
.split('\n')
|
||||||
|
.filter((line: string) => line) // Remove empty lines to fix broken output
|
||||||
|
.join('\n')
|
||||||
|
.split('#')
|
||||||
|
.slice(0, 3) // Last 2 versions (first element is empty)
|
||||||
|
.join('###')
|
||||||
|
|
||||||
|
const parts = [
|
||||||
|
latestVersions,
|
||||||
|
'---',
|
||||||
|
this.$t('dashboard.changelog.fullLink'),
|
||||||
|
]
|
||||||
|
|
||||||
|
return marked(parts.join('\n'), {
|
||||||
|
async: false,
|
||||||
|
breaks: false,
|
||||||
|
extensions: null,
|
||||||
|
gfm: true,
|
||||||
|
hooks: null,
|
||||||
|
pedantic: false,
|
||||||
|
silent: true,
|
||||||
|
tokenizer: null,
|
||||||
|
walkTokens: null,
|
||||||
|
}) as string
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
name: 'DashboardChangelog',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.card-body {
|
||||||
|
user-select: text !important;
|
||||||
|
}
|
||||||
|
</style>
|
63
src/components/dashboard/healthcheck.vue
Normal file
63
src/components/dashboard/healthcheck.vue
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<StatusPanel
|
||||||
|
:header="$t('dashboard.healthCheck.header')"
|
||||||
|
:loading="!status.checks"
|
||||||
|
:value="value"
|
||||||
|
:value-extra-class="valueClass"
|
||||||
|
:caption="$t('dashboard.healthCheck.caption')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import StatusPanel from './_statuspanel.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { StatusPanel },
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
nChecks(): Number {
|
||||||
|
return this.status?.checks?.length || 0
|
||||||
|
},
|
||||||
|
|
||||||
|
nSuccess(): Number {
|
||||||
|
return this.status?.checks?.filter((check: any) => check?.success).length || 0
|
||||||
|
},
|
||||||
|
|
||||||
|
value(): string {
|
||||||
|
return `${this.nSuccess} / ${this.nChecks}`
|
||||||
|
},
|
||||||
|
|
||||||
|
valueClass(): string {
|
||||||
|
return this.nSuccess === this.nChecks ? 'text-success' : 'text-danger'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: {} as any,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetchStatus(): void {
|
||||||
|
fetch('status/status.json?fail-status=200')
|
||||||
|
.then((resp: Response) => resp.json())
|
||||||
|
.then((data: any) => {
|
||||||
|
this.status = data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$root?.registerTicker('dashboardHealthCheck', () => this.fetchStatus(), 30000)
|
||||||
|
this.fetchStatus()
|
||||||
|
},
|
||||||
|
|
||||||
|
name: 'DashboardHealthCheck',
|
||||||
|
|
||||||
|
unmounted() {
|
||||||
|
this.$root?.unregisterTicker('dashboardHealthCheck')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
62
src/components/dashboard/scopes.vue
Normal file
62
src/components/dashboard/scopes.vue
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<StatusPanel
|
||||||
|
:header="$t('dashboard.botScopes.header')"
|
||||||
|
:loading="loading"
|
||||||
|
:value="value"
|
||||||
|
:value-extra-class="valueClass"
|
||||||
|
:caption="$t('dashboard.botScopes.caption')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import BusEventTypes from '../../helpers/busevents'
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import StatusPanel from './_statuspanel.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { StatusPanel },
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
nMissing(): number {
|
||||||
|
return this.$root?.vars?.DefaultBotScopes
|
||||||
|
?.filter((scope: string) => !this.botScopes.includes(scope))
|
||||||
|
.length || 0
|
||||||
|
},
|
||||||
|
|
||||||
|
value(): string {
|
||||||
|
return `${this.nMissing}`
|
||||||
|
},
|
||||||
|
|
||||||
|
valueClass(): string {
|
||||||
|
return this.nMissing === 0 ? 'text-success' : 'text-warning'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
botScopes: [] as string[],
|
||||||
|
loading: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetchGeneralConfig(): void {
|
||||||
|
fetch('config-editor/general', this.$root?.fetchOpts)
|
||||||
|
.then((resp: Response) => this.$root?.parseResponseFromJSON(resp))
|
||||||
|
.then((data: any) => {
|
||||||
|
this.botScopes = data.channel_scopes[data.bot_name] || []
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.bus.on(BusEventTypes.ConfigReload, () => {
|
||||||
|
this.fetchGeneralConfig()
|
||||||
|
})
|
||||||
|
this.fetchGeneralConfig()
|
||||||
|
},
|
||||||
|
|
||||||
|
name: 'DashboardBotScopes',
|
||||||
|
})
|
||||||
|
</script>
|
12
src/global.d.ts
vendored
12
src/global.d.ts
vendored
|
@ -1,7 +1,10 @@
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
|
/* global RequestInit, TimerHandler */
|
||||||
|
|
||||||
import { Emitter, EventType } from 'mitt'
|
import { Emitter, EventType } from 'mitt'
|
||||||
|
|
||||||
|
type CheckAccessFunction = (resp: Response) => Response
|
||||||
|
|
||||||
type EditorVars = {
|
type EditorVars = {
|
||||||
DefaultBotScopes: string[]
|
DefaultBotScopes: string[]
|
||||||
IRCBadges: string[]
|
IRCBadges: string[]
|
||||||
|
@ -11,6 +14,10 @@ type EditorVars = {
|
||||||
Version: string
|
Version: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ParseResponseFunction = (resp: Response) => Promise<any>
|
||||||
|
type TickerRegisterFunction = (id: string, func: TimerHandler, intervalMs: number) => void
|
||||||
|
type TickerUnregisterFunction = (id: string) => void
|
||||||
|
|
||||||
type UserInfo = {
|
type UserInfo = {
|
||||||
display_name: string
|
display_name: string
|
||||||
id: string
|
id: string
|
||||||
|
@ -23,6 +30,11 @@ declare module '@vue/runtime-core' {
|
||||||
bus: Emitter<Record<EventType, unknown>>
|
bus: Emitter<Record<EventType, unknown>>
|
||||||
|
|
||||||
// On the $root
|
// On the $root
|
||||||
|
check403: CheckAccessFunction
|
||||||
|
fetchOpts: RequestInit
|
||||||
|
parseResponseFromJSON: ParseResponseFunction
|
||||||
|
registerTicker: TickerRegisterFunction
|
||||||
|
unregisterTicker: TickerUnregisterFunction
|
||||||
userInfo: UserInfo | null
|
userInfo: UserInfo | null
|
||||||
vars: EditorVars | null
|
vars: EditorVars | null
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ enum BusEventTypes {
|
||||||
FetchError = 'fetchError',
|
FetchError = 'fetchError',
|
||||||
LoadingData = 'loadingData',
|
LoadingData = 'loadingData',
|
||||||
LoginProcessing = 'loginProcessing',
|
LoginProcessing = 'loginProcessing',
|
||||||
|
RaffleChanged = 'raffleChanged',
|
||||||
|
RaffleEntryChanged = 'raffleEntryChanged',
|
||||||
Toast = 'toast',
|
Toast = 'toast',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,22 @@
|
||||||
{
|
{
|
||||||
|
"dashboard": {
|
||||||
|
"activeRaffles": {
|
||||||
|
"caption": "Active",
|
||||||
|
"header": "Raffles"
|
||||||
|
},
|
||||||
|
"botScopes": {
|
||||||
|
"caption": "Missing Scopes",
|
||||||
|
"header": "Bot-Scopes"
|
||||||
|
},
|
||||||
|
"changelog": {
|
||||||
|
"fullLink": "See full changelog in [History.md](https://github.com/Luzifer/twitch-bot/blob/master/History.md) on Github",
|
||||||
|
"heading": "Changelog"
|
||||||
|
},
|
||||||
|
"healthCheck": {
|
||||||
|
"caption": "Checks OK",
|
||||||
|
"header": "Bot-Health"
|
||||||
|
}
|
||||||
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"headers": {
|
"headers": {
|
||||||
"chatInteraction": "Chat Interaction",
|
"chatInteraction": "Chat Interaction",
|
||||||
|
|
11
src/main.ts
11
src/main.ts
|
@ -91,8 +91,7 @@ const app = createApp({
|
||||||
window.history.replaceState(null, '', window.location.href.split('#')[0])
|
window.history.replaceState(null, '', window.location.href.split('#')[0])
|
||||||
|
|
||||||
fetch(`config-editor/user?user=${this.tokenUser}`, this.$root.fetchOpts)
|
fetch(`config-editor/user?user=${this.tokenUser}`, this.$root.fetchOpts)
|
||||||
.then((resp: Response) => this.$root.check403(resp))
|
.then((resp: Response) => this.$root.parseResponseFromJSON(resp))
|
||||||
.then((resp: Response) => resp.json())
|
|
||||||
.then((data: any) => {
|
.then((data: any) => {
|
||||||
this.userInfo = data
|
this.userInfo = data
|
||||||
})
|
})
|
||||||
|
@ -105,6 +104,11 @@ const app = createApp({
|
||||||
this.tokenUser = ''
|
this.tokenUser = ''
|
||||||
},
|
},
|
||||||
|
|
||||||
|
parseResponseFromJSON(resp: Response): Promise<any> {
|
||||||
|
this.check403(resp)
|
||||||
|
return resp.json()
|
||||||
|
},
|
||||||
|
|
||||||
registerTicker(id: string, func: TimerHandler, intervalMs: number): void {
|
registerTicker(id: string, func: TimerHandler, intervalMs: number): void {
|
||||||
this.unregisterTicker(id)
|
this.unregisterTicker(id)
|
||||||
this.tickers[id] = window.setInterval(func, intervalMs)
|
this.tickers[id] = window.setInterval(func, intervalMs)
|
||||||
|
@ -116,8 +120,7 @@ const app = createApp({
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch('config-editor/refreshToken', this.$root.fetchOpts)
|
fetch('config-editor/refreshToken', this.$root.fetchOpts)
|
||||||
.then((resp: Response) => this.$root.check403(resp))
|
.then((resp: Response) => this.$root.parseResponseFromJSON(resp))
|
||||||
.then((resp: Response) => resp.json())
|
|
||||||
.then((data: any) => this.login(data.token, new Date(data.expiresAt), data.user))
|
.then((data: any) => this.login(data.token, new Date(data.expiresAt), data.user))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue