mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-11-09 16:50:01 +00:00
Add TS config and I18n
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
da0e0742f7
commit
55ab9ddf5d
17 changed files with 322 additions and 1137 deletions
1107
package-lock.json
generated
1107
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -5,13 +5,16 @@
|
||||||
"codejar": "^4.2.0",
|
"codejar": "^4.2.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"prismjs": "^1.29.0",
|
"prismjs": "^1.29.0",
|
||||||
"vue": "^3.4.27",
|
"vue": "^3.4.28",
|
||||||
|
"vue-i18n": "^9.13.1",
|
||||||
"vue-router": "^4.3.3"
|
"vue-router": "^4.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/eslint-parser": "^7.24.7",
|
"@babel/eslint-parser": "^7.24.7",
|
||||||
|
"@types/bootstrap": "^5.2.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.13.0",
|
"@typescript-eslint/eslint-plugin": "^7.13.0",
|
||||||
"@typescript-eslint/parser": "^7.13.0",
|
"@typescript-eslint/parser": "^7.13.0",
|
||||||
|
"@vue/tsconfig": "^0.5.1",
|
||||||
"esbuild": "^0.21.5",
|
"esbuild": "^0.21.5",
|
||||||
"esbuild-plugin-vue3": "^0.4.2",
|
"esbuild-plugin-vue3": "^0.4.2",
|
||||||
"esbuild-sass-plugin": "^3.3.1",
|
"esbuild-sass-plugin": "^3.3.1",
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
class="dropdown-item"
|
class="dropdown-item"
|
||||||
href="#"
|
href="#"
|
||||||
@click.prevent="logout"
|
@click.prevent="logout"
|
||||||
>Sign-out</a>
|
>{{ $t('nav.signOut') }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
@ -64,7 +64,7 @@ import { Dropdown } from 'bootstrap'
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
computed: {
|
computed: {
|
||||||
profileImage(): string {
|
profileImage(): string {
|
||||||
return this.$root.userInfo?.profile_image_url || ''
|
return this.$root?.userInfo?.profile_image_url || ''
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ export default defineComponent({
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.isLoggedIn) {
|
if (this.isLoggedIn) {
|
||||||
new Dropdown(this.$refs.userMenuToggle)
|
new Dropdown(this.$refs.userMenuToggle as Element)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
87
src/components/_sideNav.vue
Normal file
87
src/components/_sideNav.vue
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<template>
|
||||||
|
<div class="nav flex-grow-1">
|
||||||
|
<template
|
||||||
|
v-for="section in navigation"
|
||||||
|
:key="section.header"
|
||||||
|
>
|
||||||
|
<div class="navHeading">
|
||||||
|
{{ section.header }}
|
||||||
|
</div>
|
||||||
|
<RouterLink
|
||||||
|
v-for="link in section.links"
|
||||||
|
:key="link.target"
|
||||||
|
:to="{name: link.target}"
|
||||||
|
class="nav-link"
|
||||||
|
>
|
||||||
|
<i :class="`${link.icon} fa-fw me-1`" />
|
||||||
|
{{ link.name }}
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import { RouterLink } from 'vue-router'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { RouterLink },
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
navigation: [
|
||||||
|
{
|
||||||
|
header: this.$t('menu.headers.core'),
|
||||||
|
links: [
|
||||||
|
{ icon: 'fas fa-chart-area', name: this.$t('menu.dashboard'), target: 'dashboard' },
|
||||||
|
{ icon: 'fas fa-cog', name: this.$t('menu.generalSettings'), target: 'generalSettings' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: this.$t('menu.headers.chatInteraction'),
|
||||||
|
links: [
|
||||||
|
{ icon: 'fas fa-envelope-open-text', name: this.$t('menu.autoMessages'), target: 'autoMessagesList' },
|
||||||
|
{ icon: 'fas fa-inbox', name: this.$t('menu.rules'), target: 'rulesList' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: this.$t('menu.headers.modules'),
|
||||||
|
links: [{ icon: 'fas fa-dice', name: this.$t('menu.raffles'), target: 'rafflesList' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
name: 'TwitchBotEditorSideNav',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.nav {
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav>.nav-link {
|
||||||
|
align-items: center;
|
||||||
|
color: inherit;
|
||||||
|
display: flex;
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav>.nav-link.disabled {
|
||||||
|
color: var(--bs-nav-link-disabled-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navHeading {
|
||||||
|
color: color-mix(in srgb, var(--bs-body-color) 50%, transparent);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 1.75rem 1rem 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
</style>
|
11
src/components/_template.vue
Normal file
11
src/components/_template.vue
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<template>
|
||||||
|
<!---->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'TwitchBotEditor...',
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, PropType } from 'vue'
|
import { defineComponent, type PropType } from 'vue'
|
||||||
import { Toast } from 'bootstrap'
|
import { Toast } from 'bootstrap'
|
||||||
|
|
||||||
export type ToastContent = {
|
export type ToastContent = {
|
||||||
|
@ -36,7 +36,7 @@ export type ToastContent = {
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
hdl: null,
|
hdl: null as Toast | null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -72,11 +72,15 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$refs.toast.addEventListener('hidden.bs.toast', () => this.$emit('hidden'))
|
const t: Element = this.$refs.toast as Element
|
||||||
this.hdl = new Toast(this.$refs.toast, {
|
|
||||||
|
t.addEventListener('hidden.bs.toast', () => this.$emit('hidden'))
|
||||||
|
|
||||||
|
this.hdl = new Toast(t, {
|
||||||
autohide: this.toast.autoHide !== false,
|
autohide: this.toast.autoHide !== false,
|
||||||
delay: this.toast.delay || 5000,
|
delay: this.toast.delay || 5000,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.hdl.show()
|
this.hdl.show()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Toast, { ToastContent } from './_toast.vue'
|
import Toast, { type ToastContent } from './_toast.vue'
|
||||||
import BusEventTypes from '../helpers/busevents'
|
import BusEventTypes from '../helpers/busevents'
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
@ -30,9 +30,9 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.bus.on(BusEventTypes.Toast, (toast: ToastContent) => this.toasts.push({
|
this.bus.on(BusEventTypes.Toast, (toast: unknown) => this.toasts.push({
|
||||||
...toast,
|
...toast as ToastContent,
|
||||||
id: toast.id || crypto.randomUUID(),
|
id: (toast as ToastContent).id || crypto.randomUUID(),
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -4,47 +4,7 @@
|
||||||
|
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<div class="layoutNav bg-body-tertiary d-flex">
|
<div class="layoutNav bg-body-tertiary d-flex">
|
||||||
<div class="nav flex-grow-1">
|
<SideNav />
|
||||||
<div class="layoutNavHeading">
|
|
||||||
Core
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="nav-link"
|
|
||||||
>
|
|
||||||
<i class="fas fa-cog fa-fw me-1" />
|
|
||||||
General Settings
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class="layoutNavHeading">
|
|
||||||
Chat Interaction
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="nav-link"
|
|
||||||
>
|
|
||||||
<i class="fas fa-envelope-open-text fa-fw me-1" />
|
|
||||||
Auto-Messages
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="nav-link"
|
|
||||||
>
|
|
||||||
<i class="fas fa-inbox fa-fw me-1" />
|
|
||||||
Rules
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class="layoutNavHeading">
|
|
||||||
Modules
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="nav-link"
|
|
||||||
>
|
|
||||||
<i class="fas fa-dice fa-fw me-1" />
|
|
||||||
Raffle
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="layoutContent">
|
<div class="layoutContent">
|
||||||
<router-view />
|
<router-view />
|
||||||
|
@ -55,17 +15,10 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
import HeadNav from './_headNav.vue'
|
import HeadNav from './_headNav.vue'
|
||||||
|
import SideNav from './_sideNav.vue'
|
||||||
import Toaster from './_toaster.vue'
|
import Toaster from './_toaster.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: { HeadNav, Toaster },
|
|
||||||
|
|
||||||
name: 'TwitchBotEditorApp',
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -93,32 +46,4 @@ export default defineComponent({
|
||||||
width: 225px;
|
width: 225px;
|
||||||
z-index: 900;
|
z-index: 900;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layoutNav>.nav {
|
|
||||||
flex-direction: column;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layoutNav>.nav>.nav-link {
|
|
||||||
align-items: center;
|
|
||||||
color: inherit;
|
|
||||||
display: flex;
|
|
||||||
padding-bottom: 0.75rem;
|
|
||||||
padding-left: 1.5rem;
|
|
||||||
padding-top: 0.75rem;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layoutNav>.nav>.nav-link.disabled {
|
|
||||||
color: var(--bs-nav-link-disabled-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.layoutNavHeading {
|
|
||||||
color: color-mix(in srgb, var(--bs-body-color) 50%, transparent);
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 1.75rem 1rem 0.75rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
13
src/components/dashboard.vue
Normal file
13
src/components/dashboard.vue
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'TwitchBotEditorDashboard',
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -27,10 +27,10 @@ export default defineComponent({
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
authURL() {
|
authURL() {
|
||||||
const scopes = []
|
const scopes: string[] = []
|
||||||
|
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
params.set('client_id', this.$root.vars.TwitchClientID)
|
params.set('client_id', this.$root?.vars?.TwitchClientID || '')
|
||||||
params.set('redirect_uri', window.location.href.split('#')[0].split('?')[0])
|
params.set('redirect_uri', window.location.href.split('#')[0].split('?')[0])
|
||||||
params.set('response_type', 'token')
|
params.set('response_type', 'token')
|
||||||
params.set('scope', scopes.join(' '))
|
params.set('scope', scopes.join(' '))
|
||||||
|
@ -52,8 +52,8 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.bus.on(BusEventTypes.LoginProcessing, (loading: boolean) => {
|
this.bus.on(BusEventTypes.LoginProcessing as string, (loading: unknown) => {
|
||||||
this.loading = loading
|
this.loading = loading as boolean
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
31
src/global.d.ts
vendored
Normal file
31
src/global.d.ts
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
|
||||||
|
import { Emitter, EventType } from 'mitt'
|
||||||
|
|
||||||
|
type EditorVars = {
|
||||||
|
DefaultBotScopes: string[]
|
||||||
|
IRCBadges: string[]
|
||||||
|
KnownEvents: string[]
|
||||||
|
TemplateFunctions: string[]
|
||||||
|
TwitchClientID: string
|
||||||
|
Version: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserInfo = {
|
||||||
|
display_name: string
|
||||||
|
id: string
|
||||||
|
login: string
|
||||||
|
profile_image_url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@vue/runtime-core' {
|
||||||
|
interface ComponentCustomProperties {
|
||||||
|
bus: Emitter<Record<EventType, unknown>>
|
||||||
|
|
||||||
|
// On the $root
|
||||||
|
userInfo: UserInfo | null
|
||||||
|
vars: EditorVars | null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {} // Important! See note.
|
|
@ -1,4 +1,4 @@
|
||||||
import { ToastContent } from '../components/_toast.vue'
|
import { type ToastContent } from '../components/_toast.vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the content of an error-toast
|
* Create the content of an error-toast
|
||||||
|
|
16
src/i18n.ts
Normal file
16
src/i18n.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { createI18n } from 'vue-i18n'
|
||||||
|
import en from './langs/en.json'
|
||||||
|
|
||||||
|
const cookieSet = Object.fromEntries(document.cookie.split('; ')
|
||||||
|
.map(el => el.split('=')
|
||||||
|
.map(el => decodeURIComponent(el))))
|
||||||
|
|
||||||
|
export default createI18n({
|
||||||
|
fallbackLocale: 'en',
|
||||||
|
globalInjection: true,
|
||||||
|
legacy: false,
|
||||||
|
locale: cookieSet.lang || navigator?.language || 'en',
|
||||||
|
messages: {
|
||||||
|
en,
|
||||||
|
},
|
||||||
|
})
|
17
src/langs/en.json
Normal file
17
src/langs/en.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"menu": {
|
||||||
|
"headers": {
|
||||||
|
"chatInteraction": "Chat Interaction",
|
||||||
|
"core": "Core",
|
||||||
|
"modules": "Modules"
|
||||||
|
},
|
||||||
|
"autoMessages": "Auto-Messages",
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"generalSettings": "General Settings",
|
||||||
|
"raffles": "Raffles",
|
||||||
|
"rules": "Rules"
|
||||||
|
},
|
||||||
|
"nav": {
|
||||||
|
"signOut": "Sign-Out"
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import BusEventTypes from './helpers/busevents'
|
||||||
import ConfigNotifyListener from './helpers/configNotify'
|
import ConfigNotifyListener from './helpers/configNotify'
|
||||||
import { errorToast } from './helpers/toasts'
|
import { errorToast } from './helpers/toasts'
|
||||||
|
|
||||||
|
import i18n from './i18n'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import App from './components/app.vue'
|
import App from './components/app.vue'
|
||||||
import Login from './components/login.vue'
|
import Login from './components/login.vue'
|
||||||
|
@ -188,5 +189,7 @@ const app = createApp({
|
||||||
})
|
})
|
||||||
|
|
||||||
app.config.globalProperties.bus = mitt()
|
app.config.globalProperties.bus = mitt()
|
||||||
|
app.config.globalProperties.$foo = 'bare'
|
||||||
|
app.use(i18n)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
|
@ -1,28 +1,31 @@
|
||||||
import { createRouter, createMemoryHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory, type RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
//import AuthView from './components/auth.vue'
|
import Dashboard from './components/dashboard.vue'
|
||||||
//import ChatView from './components/chatview.vue'
|
|
||||||
|
|
||||||
const Root = {}
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
// {
|
{ component: Dashboard, name: 'dashboard', path: '/' },
|
||||||
// component: AuthView,
|
|
||||||
// path: '/',
|
// General settings
|
||||||
// },
|
{ component: {}, name: 'generalSettings', path: '/general-settings' },
|
||||||
// {
|
|
||||||
// component: ChatView,
|
// Auto-Messages
|
||||||
// path: '/chat',
|
{ component: {}, name: 'autoMessagesList', path: '/auto-messages' },
|
||||||
// },
|
{ component: {}, name: 'autoMessageEdit', path: '/auto-messages/edit/{id}' },
|
||||||
{
|
{ component: {}, name: 'autoMessageNew', path: '/auto-messages/new' },
|
||||||
component: Root,
|
|
||||||
name: 'root',
|
// Rules
|
||||||
path: '/',
|
{ component: {}, name: 'rulesList', path: '/rules' },
|
||||||
}
|
{ component: {}, name: 'rulesEdit', path: '/rules/edit/{id}' },
|
||||||
]
|
{ component: {}, name: 'rulesNew', path: '/rules/new' },
|
||||||
|
|
||||||
|
// Raffles
|
||||||
|
{ component: {}, name: 'rafflesList', path: '/raffles' },
|
||||||
|
{ component: {}, name: 'rafflesEdit', path: '/raffles/edit/{id}' },
|
||||||
|
{ component: {}, name: 'rafflesNew', path: '/raffles/new' },
|
||||||
|
] as RouteRecordRaw[]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createMemoryHistory(),
|
history: createWebHashHistory(),
|
||||||
routes,
|
routes,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
9
tsconfig.json
Normal file
9
tsconfig.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"src/components/_template.vue"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue