minimal working
This commit is contained in:
1 parent
2093433b96
commit
13ea8c80da
11 files changed
+727
-337
No files matched your search
+12
-18
@@ -1,5 +1,7 @@
|
||||
import _axios, { AxiosInstance } from 'axios'
|
||||
|
||||
const WANIKANI_API_KEY = 'WANIKANI_API_KEY'
|
||||
|
||||
export type ITypeLevel = 'character' | 'vocabulary'
|
||||
export type IType = ITypeLevel | 'sentence'
|
||||
|
||||
@@ -93,35 +95,27 @@ class Api {
|
||||
}
|
||||
|
||||
async setWanikaniApiKey(apiKey?: string) {
|
||||
apiKey = apiKey || localStorage.getItem(WANIKANI_API_KEY) || ''
|
||||
console.log(apiKey)
|
||||
|
||||
const setApiKey = () => {
|
||||
if (apiKey) {
|
||||
this.axios.defaults.headers.common['Authorization'] = `Bearer ${apiKey}`
|
||||
localStorage.setItem(WANIKANI_API_KEY, apiKey)
|
||||
} else {
|
||||
delete this.axios.defaults.headers.common['Authorization']
|
||||
localStorage.removeItem(WANIKANI_API_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
setApiKey()
|
||||
const r = await window.$nuxt.$accessor.updateSettings()
|
||||
|
||||
return await this.isAuthorized().then((r) => {
|
||||
if (!r) {
|
||||
apiKey = ''
|
||||
setApiKey()
|
||||
}
|
||||
return r
|
||||
})
|
||||
}
|
||||
|
||||
async isAuthorized(): Promise<boolean> {
|
||||
if (this._isAuthorized && !this._user) {
|
||||
const r = await this.user_getSettings({ select: ['identifier'] })
|
||||
if (r) {
|
||||
this._user = r.identifier
|
||||
this._isAuthorized = true
|
||||
}
|
||||
if (!r) {
|
||||
apiKey = ''
|
||||
setApiKey()
|
||||
}
|
||||
|
||||
return this._isAuthorized
|
||||
return r
|
||||
}
|
||||
|
||||
async user_getSettings(params: { select: (keyof IUserSettings)[] }) {
|
||||
|
||||
+100
-177
@@ -30,12 +30,10 @@
|
||||
<div class="column is-6 entry-display">
|
||||
<div
|
||||
class="character-display clickable font-han"
|
||||
@click="(evt) => openContext(evt, current, 'character')"
|
||||
@contextmenu.prevent="
|
||||
(evt) => openContext(evt, current, 'character')
|
||||
"
|
||||
@click="(evt) => openContext(evt, entry)"
|
||||
@contextmenu.prevent="(evt) => openContext(evt, entry)"
|
||||
>
|
||||
{{ current }}
|
||||
{{ entry.entry }}
|
||||
</div>
|
||||
|
||||
<div class="buttons has-addons">
|
||||
@@ -57,7 +55,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="tag.length" class="mb-4">
|
||||
<div v-if="entry.tag.length" class="mb-4">
|
||||
Tags:
|
||||
<b-taglist style="display: inline-flex">
|
||||
<b-tag v-for="t in tag.slice(0, 5)" :key="t" type="is-info">
|
||||
@@ -66,11 +64,11 @@
|
||||
</b-taglist>
|
||||
</div>
|
||||
|
||||
<div v-if="level && level <= 60" class="mb-4">
|
||||
<div v-if="entry.level <= 60" class="mb-4">
|
||||
<b-taglist attached>
|
||||
<b-tag type="is-dark">Level</b-tag>
|
||||
<b-tag type="is-primary">
|
||||
{{ level }}
|
||||
{{ entry.level }}
|
||||
</b-tag>
|
||||
</b-taglist>
|
||||
</div>
|
||||
@@ -91,14 +89,14 @@
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
{{ reading.join(' / ') }}
|
||||
{{ entry.reading.join(' / ') }}
|
||||
</div>
|
||||
</b-collapse>
|
||||
|
||||
<b-collapse
|
||||
class="card"
|
||||
animation="slide"
|
||||
:open="!!translation.length"
|
||||
:open="!!entry.translation.length"
|
||||
>
|
||||
<div
|
||||
slot="trigger"
|
||||
@@ -113,11 +111,11 @@
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
{{ translation.join(' / ') }}
|
||||
{{ entry.translation.join(' / ') }}
|
||||
</div>
|
||||
</b-collapse>
|
||||
|
||||
<b-collapse class="card" animation="slide" :open="!!sub.length">
|
||||
<b-collapse class="card" animation="slide" :open="!!entry.sub.length">
|
||||
<div
|
||||
slot="trigger"
|
||||
slot-scope="props"
|
||||
@@ -132,18 +130,22 @@
|
||||
|
||||
<div class="card-content">
|
||||
<span
|
||||
v-for="h in sub"
|
||||
v-for="h in entry.sub"
|
||||
:key="h"
|
||||
class="font-han clickable"
|
||||
@click="(evt) => openContext(evt, h, 'character')"
|
||||
@contextmenu.prevent="(evt) => openContext(evt, h, 'character')"
|
||||
@click="
|
||||
(evt) => openContext(evt, { entry: h, type: 'character' })
|
||||
"
|
||||
@contextmenu.prevent="
|
||||
(evt) => openContext(evt, { entry: h, type: 'character' })
|
||||
"
|
||||
>
|
||||
{{ h }}
|
||||
</span>
|
||||
</div>
|
||||
</b-collapse>
|
||||
|
||||
<b-collapse class="card" animation="slide" :open="!!sup.length">
|
||||
<b-collapse class="card" animation="slide" :open="!!entry.sup.length">
|
||||
<div
|
||||
slot="trigger"
|
||||
slot-scope="props"
|
||||
@@ -158,18 +160,26 @@
|
||||
|
||||
<div class="card-content">
|
||||
<span
|
||||
v-for="h in sup"
|
||||
v-for="h in entry.sup"
|
||||
:key="h"
|
||||
class="font-han clickable"
|
||||
@click="(evt) => openContext(evt, h, 'character')"
|
||||
@contextmenu.prevent="(evt) => openContext(evt, h, 'character')"
|
||||
@click="
|
||||
(evt) => openContext(evt, { entry: h, type: 'character' })
|
||||
"
|
||||
@contextmenu.prevent="
|
||||
(evt) => openContext(evt, { entry: h, type: 'character' })
|
||||
"
|
||||
>
|
||||
{{ h }}
|
||||
</span>
|
||||
</div>
|
||||
</b-collapse>
|
||||
|
||||
<b-collapse class="card" animation="slide" :open="!!variants.length">
|
||||
<b-collapse
|
||||
class="card"
|
||||
animation="slide"
|
||||
:open="!!entry.variant.length"
|
||||
>
|
||||
<div
|
||||
slot="trigger"
|
||||
slot-scope="props"
|
||||
@@ -184,82 +194,20 @@
|
||||
|
||||
<div class="card-content">
|
||||
<span
|
||||
v-for="h in variants"
|
||||
v-for="h in entry.variant"
|
||||
:key="h"
|
||||
class="font-han clickable"
|
||||
@click="(evt) => openContext(evt, h, 'character')"
|
||||
@contextmenu.prevent="(evt) => openContext(evt, h, 'character')"
|
||||
@click="
|
||||
(evt) => openContext(evt, { entry: h, type: 'character' })
|
||||
"
|
||||
@contextmenu.prevent="
|
||||
(evt) => openContext(evt, { entry: h, type: 'character' })
|
||||
"
|
||||
>
|
||||
{{ h }}
|
||||
</span>
|
||||
</div>
|
||||
</b-collapse>
|
||||
|
||||
<b-collapse class="card" animation="slide" :open="!!vocabs.length">
|
||||
<div
|
||||
slot="trigger"
|
||||
slot-scope="props"
|
||||
class="card-header"
|
||||
role="button"
|
||||
>
|
||||
<h2 class="card-header-title">Vocabularies</h2>
|
||||
<a role="button" class="card-header-icon">
|
||||
<fontawesome :icon="props.open ? 'caret-down' : 'caret-up'" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<div v-for="(v, i) in vocabs" :key="i" class="long-item">
|
||||
<span
|
||||
class="clickable"
|
||||
@click="(evt) => openContext(evt, v.entry, 'vocabulary')"
|
||||
@contextmenu.prevent="
|
||||
(evt) => openContext(evt, v.entry, 'vocabulary')
|
||||
"
|
||||
>
|
||||
{{ v.entry }}
|
||||
</span>
|
||||
|
||||
<span v-if="v.alt" class="clickable">
|
||||
{{ v.alt.join(' ') }}
|
||||
</span>
|
||||
|
||||
<span class="pinyin">[{{ v.reading.join(' / ') }}]</span>
|
||||
|
||||
<span>{{ v.translation.join(' / ') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</b-collapse>
|
||||
|
||||
<b-collapse class="card" animation="slide" :open="!!sentences.length">
|
||||
<div
|
||||
slot="trigger"
|
||||
slot-scope="props"
|
||||
class="card-header"
|
||||
role="button"
|
||||
>
|
||||
<h2 class="card-header-title">Sentences</h2>
|
||||
<a role="button" class="card-header-icon">
|
||||
<fontawesome :icon="props.open ? 'caret-down' : 'caret-up'" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<div v-for="(s, i) in sentences" :key="i" class="long-item">
|
||||
<span
|
||||
class="clickable"
|
||||
@click="(evt) => openContext(evt, s.entry[0], 'sentence')"
|
||||
@contextmenu.prevent="
|
||||
(evt) => openContext(evt, s.entry[0], 'sentence')
|
||||
"
|
||||
>
|
||||
{{ s.entry[0] }}
|
||||
</span>
|
||||
|
||||
<span>{{ s.translation[0] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</b-collapse>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -278,10 +226,31 @@
|
||||
import { Component, Prop, Ref, Vue, Watch } from 'nuxt-property-decorator'
|
||||
import ContextMenu from '@/components/ContextMenu.vue'
|
||||
import { api, IDictionaryLookup, IEntryLookup, IType } from '~/assets/api'
|
||||
import { capitalize } from '~/assets/util'
|
||||
|
||||
type ILookupEntry = Omit<IDictionaryLookup, 'id'> & IEntryLookup
|
||||
|
||||
const makeLookupEntry = (
|
||||
entry = '',
|
||||
type: IType = 'vocabulary'
|
||||
): ILookupEntry => {
|
||||
return {
|
||||
entry,
|
||||
list: [],
|
||||
type,
|
||||
reading: [],
|
||||
translation: [],
|
||||
audio: [],
|
||||
description: '',
|
||||
tag: [],
|
||||
sub: [],
|
||||
sup: [],
|
||||
variant: [],
|
||||
visual: [],
|
||||
amalgamation: [],
|
||||
component: [],
|
||||
}
|
||||
}
|
||||
|
||||
@Component<LookupTab>({
|
||||
components: {
|
||||
ContextMenu,
|
||||
@@ -300,13 +269,12 @@ export default class LookupTab extends Vue {
|
||||
@Prop({ default: () => ({}) }) query!: {
|
||||
q?: string
|
||||
entry?: string
|
||||
type?: 'character' | 'vocabulary'
|
||||
}
|
||||
|
||||
@Prop() type!: 'character' | 'vocabulary'
|
||||
|
||||
@Ref() context!: ContextMenu
|
||||
|
||||
title = this.type === 'character' ? 'Kanji' : capitalize(this.type)
|
||||
title = 'Lookup'
|
||||
|
||||
entries: ILookupEntry[] = []
|
||||
i = 0
|
||||
@@ -320,6 +288,7 @@ export default class LookupTab extends Vue {
|
||||
selected: {
|
||||
entry: string
|
||||
type: string
|
||||
audio?: ILookupEntry['audio']
|
||||
} = {
|
||||
entry: '',
|
||||
type: '',
|
||||
@@ -328,8 +297,8 @@ export default class LookupTab extends Vue {
|
||||
q = ''
|
||||
q0 = ''
|
||||
|
||||
get current() {
|
||||
return this.entries[this.i]?.entry || ''
|
||||
get entry() {
|
||||
return this.entries[this.i] || makeLookupEntry()
|
||||
}
|
||||
|
||||
get additionalContext() {
|
||||
@@ -338,7 +307,7 @@ export default class LookupTab extends Vue {
|
||||
{
|
||||
name: 'Reload',
|
||||
handler: async () => {
|
||||
const { result } = await api.all_getRandom({ type: this.type })
|
||||
const { result } = await api.all_getRandom({ type: 'vocabulary' })
|
||||
this.q0 = result
|
||||
},
|
||||
},
|
||||
@@ -348,12 +317,8 @@ export default class LookupTab extends Vue {
|
||||
return []
|
||||
}
|
||||
|
||||
openContext(
|
||||
evt: MouseEvent,
|
||||
entry = this.selected.entry,
|
||||
type = this.selected.type
|
||||
) {
|
||||
this.selected = { entry, type }
|
||||
openContext(evt: MouseEvent, entry = this.selected) {
|
||||
this.selected = entry
|
||||
this.context.open(evt)
|
||||
}
|
||||
|
||||
@@ -362,72 +327,23 @@ export default class LookupTab extends Vue {
|
||||
this.$emit('title', (q ? q + ' - ' : '') + this.title)
|
||||
this.entries = []
|
||||
|
||||
if (this.type === 'character') {
|
||||
if (/\p{sc=Han}/u.test(q)) {
|
||||
let qs = q.split('').filter((h) => /\p{sc=Han}/u.test(h))
|
||||
qs = qs.filter((h, i) => qs.indexOf(h) === i)
|
||||
|
||||
this.entries = qs.map(
|
||||
(entry) =>
|
||||
({
|
||||
entry,
|
||||
list: [],
|
||||
type: this.type,
|
||||
reading: [],
|
||||
translation: [],
|
||||
audio: [],
|
||||
description: '',
|
||||
tag: [],
|
||||
sub: [],
|
||||
sup: [],
|
||||
variant: [],
|
||||
visual: [],
|
||||
amalgamation: [],
|
||||
component: [],
|
||||
} as ILookupEntry)
|
||||
)
|
||||
} else {
|
||||
if (!q.trim()) {
|
||||
const { result } = await api.all_getRandom({ type: this.type })
|
||||
|
||||
this.q0 = result
|
||||
this.q = result
|
||||
return
|
||||
} else {
|
||||
const r = await api.all_getQuery({
|
||||
q,
|
||||
type: this.type,
|
||||
distinct: 'id',
|
||||
})
|
||||
this.entries = r.result.map(
|
||||
({ entry }) =>
|
||||
({
|
||||
entry,
|
||||
list: [],
|
||||
type: this.type,
|
||||
reading: [],
|
||||
translation: [],
|
||||
audio: [],
|
||||
description: '',
|
||||
tag: [],
|
||||
sub: [],
|
||||
sup: [],
|
||||
variant: [],
|
||||
visual: [],
|
||||
amalgamation: [],
|
||||
component: [],
|
||||
} as ILookupEntry)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (q.trim()) {
|
||||
const r = await api.all_getQuery({
|
||||
q,
|
||||
type: this.query.type,
|
||||
distinct: 'id',
|
||||
})
|
||||
this.entries = r.result.map(({ entry, type }) =>
|
||||
makeLookupEntry(entry, type)
|
||||
)
|
||||
}
|
||||
|
||||
this.i = 0
|
||||
}
|
||||
|
||||
@Watch('current')
|
||||
@Watch('entry.entry')
|
||||
load() {
|
||||
if (this.current) {
|
||||
if (this.entry.entry) {
|
||||
this.loadComponents()
|
||||
this.loadVocabularies()
|
||||
this.loadSentences()
|
||||
@@ -441,17 +357,18 @@ export default class LookupTab extends Vue {
|
||||
}
|
||||
|
||||
async loadComponents() {
|
||||
if (!this.current.trim()) return
|
||||
const entry = this.entries[this.i]
|
||||
if (!entry) return
|
||||
|
||||
const [comp, dict] = await Promise.all([
|
||||
api.all_getComponent({
|
||||
entry: this.current,
|
||||
type: this.type,
|
||||
entry: entry.entry,
|
||||
type: entry.type,
|
||||
}),
|
||||
api
|
||||
.all_getLookup({
|
||||
entry: this.current,
|
||||
type: this.type,
|
||||
entry: entry.entry,
|
||||
type: entry.type,
|
||||
limit: 1,
|
||||
})
|
||||
.then(({ result: [r] }) => r),
|
||||
@@ -459,9 +376,9 @@ export default class LookupTab extends Vue {
|
||||
|
||||
this.$set(this.entries, this.i, {
|
||||
...(dict || {
|
||||
entry: this.current,
|
||||
entry: entry.entry,
|
||||
list: [],
|
||||
type: this.type,
|
||||
type: entry.type,
|
||||
reading: [],
|
||||
translation: [],
|
||||
audio: [],
|
||||
@@ -480,22 +397,25 @@ export default class LookupTab extends Vue {
|
||||
}
|
||||
|
||||
async loadVocabularies() {
|
||||
if (this.type === 'character') {
|
||||
if (!/^\p{sc=Han}$/u.test(this.current)) {
|
||||
const entry = this.entries[this.i]
|
||||
if (!entry) return
|
||||
|
||||
if (entry.type === 'character') {
|
||||
if (!/^\p{sc=Han}$/u.test(entry.entry)) {
|
||||
this.lookup.vocabulary = []
|
||||
return
|
||||
}
|
||||
|
||||
this.lookup.vocabulary = await api
|
||||
.character_getVocabulary({
|
||||
entry: this.current,
|
||||
entry: entry.entry,
|
||||
distinct: 'id',
|
||||
})
|
||||
.then((r) => r.result)
|
||||
} else {
|
||||
this.lookup.vocabulary = await api
|
||||
.vocabulary_getVocabulary({
|
||||
entry: this.current,
|
||||
entry: entry.entry,
|
||||
distinct: 'id',
|
||||
})
|
||||
.then((r) => r.result)
|
||||
@@ -503,22 +423,25 @@ export default class LookupTab extends Vue {
|
||||
}
|
||||
|
||||
async loadSentences() {
|
||||
if (this.type === 'character') {
|
||||
if (!/^\p{sc=Han}$/u.test(this.current)) {
|
||||
const entry = this.entries[this.i]
|
||||
if (!entry) return
|
||||
|
||||
if (entry.type === 'character') {
|
||||
if (!/^\p{sc=Han}$/u.test(entry.entry)) {
|
||||
this.lookup.sentence = []
|
||||
return
|
||||
}
|
||||
|
||||
this.lookup.sentence = await api
|
||||
.character_getSentence({
|
||||
entry: this.current,
|
||||
entry: entry.entry,
|
||||
distinct: 'id',
|
||||
})
|
||||
.then((r) => r.result)
|
||||
} else {
|
||||
this.lookup.sentence = await api
|
||||
.vocabulary_getSentence({
|
||||
entry: this.current,
|
||||
entry: entry.entry,
|
||||
distinct: 'id',
|
||||
})
|
||||
.then((r) => r.result)
|
||||
|
||||
+31
-68
@@ -1,74 +1,37 @@
|
||||
<template>
|
||||
<div>
|
||||
<nav
|
||||
class="navbar header has-shadow is-primary"
|
||||
role="navigation"
|
||||
aria-label="main navigation"
|
||||
>
|
||||
<div class="navbar-brand">
|
||||
<a
|
||||
class="navbar-item"
|
||||
href="/"
|
||||
>
|
||||
<img
|
||||
src="~assets/buefy.png"
|
||||
alt="Buefy"
|
||||
height="28"
|
||||
>
|
||||
</a>
|
||||
|
||||
<div class="navbar-burger">
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="main-content columns">
|
||||
<aside class="column is-2 section">
|
||||
<p class="menu-label is-hidden-touch">
|
||||
General
|
||||
</p>
|
||||
<ul class="menu-list">
|
||||
<li
|
||||
v-for="(item, key) of items"
|
||||
:key="key"
|
||||
>
|
||||
<NuxtLink
|
||||
:to="item.to"
|
||||
exact-active-class="is-active"
|
||||
>
|
||||
<b-icon :icon="item.icon" /> {{ item.title }}
|
||||
</NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<div class="container column is-10">
|
||||
<Nuxt />
|
||||
</div>
|
||||
</section>
|
||||
<div class="DefaultLayout">
|
||||
<nuxt />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
title: 'Home',
|
||||
icon: 'home',
|
||||
to: { name: 'index' }
|
||||
},
|
||||
{
|
||||
title: 'Inspire',
|
||||
icon: 'lightbulb',
|
||||
to: { name: 'inspire' }
|
||||
}
|
||||
]
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from 'nuxt-property-decorator'
|
||||
import '~/assets/speak'
|
||||
|
||||
@Component<DefaultLayout>({
|
||||
mounted() {
|
||||
if (process.env['NODE_ENV'] !== 'development') {
|
||||
window.onbeforeunload = function (e: any) {
|
||||
e.preventDefault()
|
||||
e.returnValue = 'not returned'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
export default class DefaultLayout extends Vue {}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
html,
|
||||
body,
|
||||
#__nuxt,
|
||||
#__layout,
|
||||
.DefaultLayout {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
overscroll-behavior-y: none;
|
||||
}
|
||||
</style>
|
||||
+55
-3
@@ -1,6 +1,8 @@
|
||||
import { NuxtConfig } from '@nuxt/types'
|
||||
|
||||
export default async (): Promise<NuxtConfig> => {
|
||||
const NODE_ENV = process.env['NODE_ENV'] || ''
|
||||
|
||||
return {
|
||||
// Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
|
||||
ssr: false,
|
||||
@@ -10,11 +12,15 @@ export default async (): Promise<NuxtConfig> => {
|
||||
|
||||
// Global page headers: https://go.nuxtjs.dev/config-head
|
||||
head: {
|
||||
title: 'jaquiz',
|
||||
title: 'WaniKani-based jaquiz',
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||
{ hid: 'description', name: 'description', content: '' },
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: 'WaniKani-based jaquiz',
|
||||
},
|
||||
{ name: 'format-detection', content: 'telephone=no' },
|
||||
],
|
||||
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
|
||||
@@ -24,7 +30,7 @@ export default async (): Promise<NuxtConfig> => {
|
||||
css: [],
|
||||
|
||||
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
|
||||
plugins: [],
|
||||
plugins: ['~/plugins/filter.ts', '~/plugins/vue-context.client.js'],
|
||||
|
||||
// Auto import components: https://go.nuxtjs.dev/config-components
|
||||
components: true,
|
||||
@@ -33,6 +39,7 @@ export default async (): Promise<NuxtConfig> => {
|
||||
buildModules: [
|
||||
// https://go.nuxtjs.dev/typescript
|
||||
'@nuxt/typescript-build',
|
||||
'nuxt-typed-vuex',
|
||||
],
|
||||
|
||||
// Modules: https://go.nuxtjs.dev/config-modules
|
||||
@@ -41,6 +48,43 @@ export default async (): Promise<NuxtConfig> => {
|
||||
'nuxt-buefy',
|
||||
// https://go.nuxtjs.dev/pwa
|
||||
'@nuxtjs/pwa',
|
||||
'@nuxtjs/proxy',
|
||||
[
|
||||
'@nuxtjs/fontawesome',
|
||||
{
|
||||
component: 'fontawesome',
|
||||
icons: {
|
||||
brands: ['faGithub'],
|
||||
solid: [
|
||||
'faAngleLeft',
|
||||
'faAngleRight',
|
||||
'faAngleUp',
|
||||
'faArrowUp',
|
||||
'faBookReader',
|
||||
'faCaretDown',
|
||||
'faCaretUp',
|
||||
'faChalkboardTeacher',
|
||||
'faCog',
|
||||
'faExclamationCircle',
|
||||
'faExclamationTriangle',
|
||||
'faEye',
|
||||
'faEyeSlash',
|
||||
'faInfoCircle',
|
||||
'faRandom',
|
||||
'faSearch',
|
||||
'faTag',
|
||||
'faListUl',
|
||||
'faListOl',
|
||||
'faBars',
|
||||
'faSave',
|
||||
'faTrash',
|
||||
'faTools',
|
||||
'faTh',
|
||||
'faRedoAlt',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
// PWA module configuration: https://go.nuxtjs.dev/pwa
|
||||
@@ -50,6 +94,14 @@ export default async (): Promise<NuxtConfig> => {
|
||||
},
|
||||
},
|
||||
|
||||
proxy: {
|
||||
'/api': 'http://localhost:35594',
|
||||
},
|
||||
|
||||
env: {
|
||||
NODE_ENV,
|
||||
},
|
||||
|
||||
// Build Configuration: https://go.nuxtjs.dev/config-build
|
||||
build: {},
|
||||
}
|
||||
|
||||
+14
-8
@@ -9,22 +9,28 @@
|
||||
"generate": "nuxt generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.36",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||
"@fortawesome/vue-fontawesome": "^2.0.6",
|
||||
"@nuxtjs/fontawesome": "^1.1.2",
|
||||
"@nuxtjs/proxy": "^2.1.0",
|
||||
"@nuxtjs/pwa": "^3.3.5",
|
||||
"axios": "^0.23.0",
|
||||
"core-js": "^3.15.1",
|
||||
"axios": "^0.24.0",
|
||||
"core-js": "^3.19.0",
|
||||
"jsonschema-definer": "^1.3.2",
|
||||
"nuxt": "^2.15.7",
|
||||
"nuxt-buefy": "^0.4.8",
|
||||
"nuxt": "^2.15.8",
|
||||
"nuxt-buefy": "^0.4.11",
|
||||
"nuxt-property-decorator": "^2.9.1",
|
||||
"nuxt-typed-vuex": "^0.2.0",
|
||||
"vue-context": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/types": "^2.15.7",
|
||||
"@nuxt/types": "^2.15.8",
|
||||
"@nuxt/typescript-build": "^2.1.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"prettier": "^2.3.2",
|
||||
"sass": "^1.43.3",
|
||||
"prettier": "^2.4.1",
|
||||
"sass": "^1.43.4",
|
||||
"sass-loader": "~10.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-5
@@ -129,16 +129,14 @@ import { api } from '~/assets/api'
|
||||
LibraryTab,
|
||||
},
|
||||
async mounted() {
|
||||
await this.$accessor.updateSettings()
|
||||
|
||||
if (!this.$accessor.isApp) {
|
||||
this.$router.replace('/')
|
||||
} else {
|
||||
if (await api.setWanikaniApiKey()) {
|
||||
this.$accessor.ADD_TAB({
|
||||
component: 'Lookup',
|
||||
first: true,
|
||||
})
|
||||
this.isReady = true
|
||||
} else {
|
||||
this.$router.replace('/')
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import { api } from '~/assets/api'
|
||||
|
||||
@Component<HomePage>({
|
||||
async mounted() {
|
||||
if (await api.isAuthorized()) {
|
||||
if (await api.setWanikaniApiKey()) {
|
||||
this.$router.replace('/app')
|
||||
} else {
|
||||
this.isReady = true
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Vue.filter('format', (v: any) => {
|
||||
if (typeof v === 'number') {
|
||||
return v || v === 0 ? v.toLocaleString() : ''
|
||||
} else if (v instanceof Date) {
|
||||
return v.toLocaleDateString([], {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
weekday: 'short',
|
||||
})
|
||||
} else if (v && typeof v === 'object') {
|
||||
return JSON.stringify(v)
|
||||
}
|
||||
return v
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Vue.filter('formatDate', (v: any) => {
|
||||
return v
|
||||
? new Date(v).toLocaleDateString([], {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
weekday: 'short',
|
||||
})
|
||||
: ''
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Vue.filter('duration', (v: any) => {
|
||||
return v
|
||||
? new Duration(new Date(v), new Date()).toString({
|
||||
sign: false,
|
||||
granularity: 2,
|
||||
})
|
||||
: ''
|
||||
})
|
||||
|
||||
/**
|
||||
* Possible duration units in this parser
|
||||
*/
|
||||
export type DurationUnit = 'ms' | 's' | 'min' | 'h' | 'd' | 'w' | 'mo' | 'y'
|
||||
|
||||
export interface IDurationOptions {
|
||||
/**
|
||||
* @default true
|
||||
*/
|
||||
sign?: boolean
|
||||
/**
|
||||
* Number of units plus subunits
|
||||
*/
|
||||
granularity?: number
|
||||
/**
|
||||
* Number of max units shown
|
||||
*/
|
||||
maxUnit?: number
|
||||
/**
|
||||
* Smallest unit shown
|
||||
*
|
||||
* @default 's'
|
||||
*/
|
||||
smallest?: DurationUnit
|
||||
/**
|
||||
* Custom naming for units
|
||||
*/
|
||||
unit?: Partial<Record<DurationUnit, string>>
|
||||
}
|
||||
|
||||
export class Duration {
|
||||
/**
|
||||
* Sign in front of the output toString()
|
||||
*/
|
||||
sign: '+' | '-' | '' = '+'
|
||||
|
||||
/**
|
||||
* Milliseconds
|
||||
*/
|
||||
ms: number
|
||||
/**
|
||||
* Seconds
|
||||
*/
|
||||
s: number
|
||||
/**
|
||||
* Minutes
|
||||
*/
|
||||
min: number
|
||||
/**
|
||||
* Hours
|
||||
*/
|
||||
h: number
|
||||
/**
|
||||
* Days
|
||||
*/
|
||||
d: number
|
||||
/**
|
||||
* Weeks
|
||||
*/
|
||||
w: number
|
||||
/**
|
||||
* Months
|
||||
*/
|
||||
mo: number
|
||||
/**
|
||||
* Years
|
||||
*/
|
||||
y: number
|
||||
|
||||
private _dates: [Date, Date] = [new Date(this.from), new Date(this.to)]
|
||||
|
||||
/**
|
||||
* Parse milliseconds (i.e. epoch) to Duration, based on before present time
|
||||
*/
|
||||
static of(msec: number) {
|
||||
const to = new Date()
|
||||
const output = new this(new Date(+to - msec), to)
|
||||
output.sign = ''
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
constructor(
|
||||
/**
|
||||
* Starting Date
|
||||
*/
|
||||
public from: Date,
|
||||
/**
|
||||
* Ending date
|
||||
*/
|
||||
public to: Date
|
||||
) {
|
||||
if (from > to) {
|
||||
this.sign = '-'
|
||||
this._dates = this._dates.reverse() as [Date, Date]
|
||||
}
|
||||
|
||||
this.ms = this._parse((d) => d.getMilliseconds(), {
|
||||
get: (d) => d.getSeconds(),
|
||||
set: (d, v) => d.setSeconds(v),
|
||||
inc: () => 1000,
|
||||
})
|
||||
|
||||
this.s = this._parse((d) => d.getSeconds(), {
|
||||
get: (d) => d.getMinutes(),
|
||||
set: (d, v) => d.setMinutes(v),
|
||||
inc: () => 60,
|
||||
})
|
||||
|
||||
this.min = this._parse((d) => d.getMinutes(), {
|
||||
get: (d) => d.getHours(),
|
||||
set: (d, v) => d.setHours(v),
|
||||
inc: () => 60,
|
||||
})
|
||||
|
||||
this.h = this._parse((d) => d.getHours(), {
|
||||
get: (d) => d.getDate(),
|
||||
set: (d, v) => d.setDate(v),
|
||||
inc: () => 24,
|
||||
})
|
||||
|
||||
this.d = this._parse((d) => d.getDate(), {
|
||||
get: (d) => d.getMonth(),
|
||||
set: (d, v) => d.setMonth(v),
|
||||
inc: (d) => {
|
||||
const y = d.getFullYear()
|
||||
let isLeapYear = true
|
||||
if (y % 4) {
|
||||
isLeapYear = false
|
||||
} else if (y % 100) {
|
||||
isLeapYear = true
|
||||
} else if (y % 400) {
|
||||
isLeapYear = false
|
||||
}
|
||||
|
||||
return [
|
||||
31, // Jan
|
||||
isLeapYear ? 29 : 28, // Feb
|
||||
31, // Mar
|
||||
30, // Apr
|
||||
31, // May
|
||||
30, // Jun
|
||||
31, // Jul
|
||||
31, // Aug
|
||||
30, // Sep
|
||||
31, // Oct
|
||||
30, // Nov
|
||||
31, // Dec
|
||||
][d.getMonth()]!
|
||||
},
|
||||
})
|
||||
|
||||
this.w = Math.floor(this.d / 7)
|
||||
this.d = this.d % 7
|
||||
|
||||
this.mo = this._parse((d) => d.getMonth(), {
|
||||
get: (d) => d.getFullYear(),
|
||||
set: (d, v) => d.setFullYear(v),
|
||||
inc: () => 12,
|
||||
})
|
||||
|
||||
this.y = this._parse((d) => d.getFullYear())
|
||||
}
|
||||
|
||||
/**
|
||||
* To JSON-serializable OrderedDict
|
||||
*/
|
||||
toOrderedDict(): [DurationUnit, number][] {
|
||||
return [
|
||||
['ms', this.ms],
|
||||
['s', this.s],
|
||||
['min', this.min],
|
||||
['h', this.h],
|
||||
['d', this.d],
|
||||
['w', this.w],
|
||||
['mo', this.mo],
|
||||
['y', this.y],
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* To String
|
||||
*
|
||||
* Works with `${duration}` also
|
||||
*/
|
||||
toString({
|
||||
sign = true,
|
||||
granularity,
|
||||
maxUnit,
|
||||
smallest = 's',
|
||||
unit = {},
|
||||
}: IDurationOptions = {}) {
|
||||
const odict = this.toOrderedDict()
|
||||
const smallestIndex = odict.map(([k]) => k).indexOf(smallest)
|
||||
const filteredDict = odict.filter(([, v], i) => v && i >= smallestIndex)
|
||||
|
||||
const str = filteredDict
|
||||
.slice(granularity ? filteredDict.length - granularity : 0)
|
||||
.reverse()
|
||||
.slice(0, maxUnit)
|
||||
.map(([k, v]) => `${v.toLocaleString()}${unit[k] || k}`)
|
||||
.join(' ')
|
||||
|
||||
if (sign) {
|
||||
return this.sign + str
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
private _parse(
|
||||
current: (d: Date) => number,
|
||||
upper?: {
|
||||
get: (d: Date) => number
|
||||
set: (d: Date, v: number) => void
|
||||
inc: (d: Date) => number
|
||||
}
|
||||
) {
|
||||
let a = current(this._dates[1]) - current(this._dates[0])
|
||||
|
||||
if (upper) {
|
||||
while (a < 0) {
|
||||
upper.set(this._dates[1], upper.get(this._dates[1]) - 1)
|
||||
this._dates[1] = new Date(this._dates[1])
|
||||
|
||||
a += upper.inc(this._dates[1])
|
||||
}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Date adding functions
|
||||
*/
|
||||
export function addDate(d: Date): Record<DurationUnit, (n: number) => Date> {
|
||||
return {
|
||||
/**
|
||||
* Milliseconds
|
||||
*/
|
||||
ms: (n) => {
|
||||
d.setMilliseconds(d.getMilliseconds() + n)
|
||||
return new Date(d)
|
||||
},
|
||||
/**
|
||||
* Seconds
|
||||
*/
|
||||
s: (n) => {
|
||||
d.setSeconds(d.getSeconds() + n)
|
||||
return new Date(d)
|
||||
},
|
||||
/**
|
||||
* Minutes
|
||||
*/
|
||||
min: (n) => {
|
||||
d.setMinutes(d.getMinutes() + n)
|
||||
return new Date(d)
|
||||
},
|
||||
/**
|
||||
* Hours
|
||||
*/
|
||||
h: (n) => {
|
||||
d.setHours(d.getHours() + n)
|
||||
return new Date(d)
|
||||
},
|
||||
/**
|
||||
* Date
|
||||
*/
|
||||
d: (n) => {
|
||||
d.setDate(d.getDate() + n)
|
||||
return new Date(d)
|
||||
},
|
||||
/**
|
||||
* Weeks
|
||||
*/
|
||||
w: (n) => {
|
||||
d.setDate(d.getDate() + n * 7)
|
||||
return new Date(d)
|
||||
},
|
||||
/**
|
||||
* Months
|
||||
*/
|
||||
mo: (n) => {
|
||||
d.setMonth(d.getMonth() + n)
|
||||
return new Date(d)
|
||||
},
|
||||
/**
|
||||
* Years
|
||||
*/
|
||||
y: (n) => {
|
||||
d.setFullYear(d.getFullYear() + n)
|
||||
return new Date(d)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import 'vue-context/dist/css/vue-context.css'
|
||||
|
||||
import Vue from 'vue'
|
||||
import VueContext from 'vue-context'
|
||||
|
||||
Vue.component('VueContext', VueContext)
|
||||
+45
-32
@@ -1,4 +1,5 @@
|
||||
import { actionTree, getAccessorType, mutationTree } from 'typed-vuex'
|
||||
|
||||
import { api } from '~/assets/api'
|
||||
import { jsonClone } from '~/assets/util'
|
||||
|
||||
@@ -29,6 +30,24 @@ export const state = () => ({
|
||||
identifier: '',
|
||||
})
|
||||
|
||||
const SET_ALL_TABS = (
|
||||
s: ReturnType<typeof state>,
|
||||
o: {
|
||||
tabs: ITab[]
|
||||
activeTab: number
|
||||
} = s
|
||||
) => {
|
||||
localStorage.setItem(
|
||||
TAB_SETTINGS,
|
||||
JSON.stringify({
|
||||
tabs: o.tabs,
|
||||
activeTabs: o.activeTab,
|
||||
})
|
||||
)
|
||||
s.tabs = jsonClone(o.tabs)
|
||||
s.activeTab = jsonClone(o.activeTab)
|
||||
}
|
||||
|
||||
export const mutations = mutationTree(state, {
|
||||
SET_SETTINGS(state, settings: ISettings) {
|
||||
state.settings = settings
|
||||
@@ -36,26 +55,17 @@ export const mutations = mutationTree(state, {
|
||||
SET_IS_APP(state, isApp: boolean) {
|
||||
state.isApp = isApp
|
||||
},
|
||||
SET_ALL_TABS(
|
||||
state,
|
||||
o: {
|
||||
tabs: ITab[]
|
||||
activeTab: number
|
||||
} = state
|
||||
) {
|
||||
localStorage.setItem(
|
||||
TAB_SETTINGS,
|
||||
JSON.stringify({
|
||||
tabs: o.tabs,
|
||||
activeTabs: o.activeTab,
|
||||
})
|
||||
)
|
||||
state.tabs = jsonClone(o.tabs)
|
||||
state.activeTab = jsonClone(o.activeTab)
|
||||
},
|
||||
SET_TAB_TITLE(state, { i, title }: { i: number; title: string }) {
|
||||
state.tabs[i].title = title
|
||||
this.SET_ALL_TABS(state)
|
||||
if (state.tabs[i]) {
|
||||
state.tabs[i]!.title = title
|
||||
SET_ALL_TABS(state)
|
||||
}
|
||||
},
|
||||
SET_ALL_TABS(state) {
|
||||
const settings = localStorage.getItem(TAB_SETTINGS)
|
||||
if (settings) {
|
||||
Object.assign(state, JSON.parse(settings))
|
||||
}
|
||||
},
|
||||
ADD_TAB(
|
||||
state,
|
||||
@@ -90,7 +100,7 @@ export const mutations = mutationTree(state, {
|
||||
}
|
||||
|
||||
state.activeTab = state.tabs.length - 1
|
||||
this.SET_ALL_TABS(state)
|
||||
SET_ALL_TABS(state)
|
||||
},
|
||||
DELETE_TAB(state, { i }: { i: number }) {
|
||||
state.tabs.splice(i, 1)
|
||||
@@ -99,15 +109,17 @@ export const mutations = mutationTree(state, {
|
||||
state.activeTab = state.tabs.length - 1
|
||||
}
|
||||
|
||||
this.SET_ALL_TABS(state)
|
||||
SET_ALL_TABS(state)
|
||||
},
|
||||
SET_TAB_COMPONENT(state, { i, component }: { i: number; component: string }) {
|
||||
state.tabs[i].component = component
|
||||
this.SET_ALL_TABS(state)
|
||||
if (state.tabs[i]) {
|
||||
state.tabs[i]!.component = component
|
||||
SET_ALL_TABS(state)
|
||||
}
|
||||
},
|
||||
SET_TAB_CURRENT(state, { i }: { i: number }) {
|
||||
state.activeTab = i
|
||||
this.SET_ALL_TABS(state)
|
||||
SET_ALL_TABS(state)
|
||||
},
|
||||
SET_IDENTIFIER(state, name: string) {
|
||||
state.identifier = name
|
||||
@@ -117,16 +129,15 @@ export const mutations = mutationTree(state, {
|
||||
export const actions = actionTree(
|
||||
{ state, mutations },
|
||||
{
|
||||
async nuxtServerInit({ dispatch, commit }) {
|
||||
await dispatch('updateSetings')
|
||||
|
||||
const settings = localStorage.getItem(TAB_SETTINGS)
|
||||
if (settings) commit('SET_ALL_TABS', JSON.parse(settings))
|
||||
async nuxtServerInit({ commit }) {
|
||||
commit('SET_ALL_TABS')
|
||||
},
|
||||
async updateSettings({ commit }) {
|
||||
const r = await api.user_getSettings({
|
||||
select: ['identifier', 'levelMin', 'level'],
|
||||
})
|
||||
const r = await api
|
||||
.user_getSettings({
|
||||
select: ['identifier', 'levelMin', 'level'],
|
||||
})
|
||||
.catch(() => null)
|
||||
commit('SET_IS_APP', !!r)
|
||||
commit('SET_IDENTIFIER', r?.identifier || '')
|
||||
|
||||
@@ -144,6 +155,8 @@ export const actions = actionTree(
|
||||
level: r?.level || null,
|
||||
levelMin: r?.levelMin || null,
|
||||
})
|
||||
|
||||
return !!r
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -908,6 +908,42 @@
|
||||
resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7"
|
||||
integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==
|
||||
|
||||
"@fortawesome/fontawesome-common-types@^0.2.36":
|
||||
version "0.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz#b44e52db3b6b20523e0c57ef8c42d315532cb903"
|
||||
integrity sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==
|
||||
|
||||
"@fortawesome/fontawesome-svg-core@^1.2.27", "@fortawesome/fontawesome-svg-core@^1.2.36":
|
||||
version "1.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz#4f2ea6f778298e0c47c6524ce2e7fd58eb6930e3"
|
||||
integrity sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==
|
||||
dependencies:
|
||||
"@fortawesome/fontawesome-common-types" "^0.2.36"
|
||||
|
||||
"@fortawesome/free-brands-svg-icons@^5.15.4":
|
||||
version "5.15.4"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.4.tgz#ec8a44dd383bcdd58aa7d1c96f38251e6fec9733"
|
||||
integrity sha512-f1witbwycL9cTENJegcmcZRYyawAFbm8+c6IirLmwbbpqz46wyjbQYLuxOc7weXFXfB7QR8/Vd2u5R3q6JYD9g==
|
||||
dependencies:
|
||||
"@fortawesome/fontawesome-common-types" "^0.2.36"
|
||||
|
||||
"@fortawesome/free-solid-svg-icons@^5.15.4":
|
||||
version "5.15.4"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz#2a68f3fc3ddda12e52645654142b9e4e8fbb6cc5"
|
||||
integrity sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==
|
||||
dependencies:
|
||||
"@fortawesome/fontawesome-common-types" "^0.2.36"
|
||||
|
||||
"@fortawesome/vue-fontawesome@^0.1.9":
|
||||
version "0.1.10"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-0.1.10.tgz#eeeec1e4e8850bed0468f938292b06cda793bf34"
|
||||
integrity sha512-b2+SLF31h32LSepVcXe+BQ63yvbq5qmTCy4KfFogCYm2bn68H5sDWUnX+U7MBqnM2aeEk9M7xSoqGnu+wSdY6w==
|
||||
|
||||
"@fortawesome/vue-fontawesome@^2.0.6":
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-2.0.6.tgz#87e691ed87f28f4667238573a29743f543a087f6"
|
||||
integrity sha512-V3vT3flY15AKbUS31aZOP12awQI3aAzkr2B1KnqcHLmwrmy51DW3pwyBczKdypV8QxBZ8U68Hl2XxK2nudTxpg==
|
||||
|
||||
"@gar/promisify@^1.0.1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210"
|
||||
@@ -1165,7 +1201,7 @@
|
||||
rc9 "^1.2.0"
|
||||
std-env "^2.3.0"
|
||||
|
||||
"@nuxt/types@^2.15.7":
|
||||
"@nuxt/types@^2.15.8":
|
||||
version "2.15.8"
|
||||
resolved "https://registry.yarnpkg.com/@nuxt/types/-/types-2.15.8.tgz#1249de448f68169fe17e9379ee7b5caa0eb336b0"
|
||||
integrity sha512-zBAG5Fy+SIaZIerOVF1vxy1zz16ZK07QSbsuQAjdtEFlvr+vKK+0AqCv8r8DBY5IVqdMIaw5FgNUz5py0xWdPg==
|
||||
@@ -1302,6 +1338,21 @@
|
||||
webpack-node-externals "^3.0.0"
|
||||
webpackbar "^4.0.0"
|
||||
|
||||
"@nuxtjs/fontawesome@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/fontawesome/-/fontawesome-1.1.2.tgz#0add6519095b392bdffb6e3ad40f3026d20f5c44"
|
||||
integrity sha512-QAfo7hdc6hiCOohdR861oNQ+riKW/kD22bYyvaC++xXiiC1hBQcrRQ6xXd5gln+6SKCwT09+C4kGjzTgrwtr7w==
|
||||
dependencies:
|
||||
"@fortawesome/fontawesome-svg-core" "^1.2.27"
|
||||
"@fortawesome/vue-fontawesome" "^0.1.9"
|
||||
|
||||
"@nuxtjs/proxy@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/proxy/-/proxy-2.1.0.tgz#fa7715a11d237fa1273503c4e9e137dd1bf5575b"
|
||||
integrity sha512-/qtoeqXgZ4Mg6LRg/gDUZQrFpOlOdHrol/vQYMnKu3aN3bP90UfOUB3QSDghUUK7OISAJ0xp8Ld78aHyCTcKCQ==
|
||||
dependencies:
|
||||
http-proxy-middleware "^1.0.6"
|
||||
|
||||
"@nuxtjs/pwa@^3.3.5":
|
||||
version "3.3.5"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/pwa/-/pwa-3.3.5.tgz#db7c905536ebe8a464a347b6ae3215810642c044"
|
||||
@@ -1470,6 +1521,13 @@
|
||||
"@types/relateurl" "*"
|
||||
"@types/uglify-js" "*"
|
||||
|
||||
"@types/http-proxy@^1.17.5":
|
||||
version "1.17.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.7.tgz#30ea85cc2c868368352a37f0d0d3581e24834c6f"
|
||||
integrity sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8":
|
||||
version "7.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
|
||||
@@ -2121,10 +2179,10 @@ autoprefixer@^9.6.1:
|
||||
postcss "^7.0.32"
|
||||
postcss-value-parser "^4.1.0"
|
||||
|
||||
axios@^0.23.0:
|
||||
version "0.23.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.23.0.tgz#b0fa5d0948a8d1d75e3d5635238b6c4625b05149"
|
||||
integrity sha512-NmvAE4i0YAv5cKq8zlDoPd1VLKAqX5oLuZKs8xkJa4qi6RGn0uhCYFjWtHHC9EM/MwOwYWOs53W+V0aqEXq1sg==
|
||||
axios@^0.24.0:
|
||||
version "0.24.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
|
||||
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.4"
|
||||
|
||||
@@ -2356,10 +2414,10 @@ browserslist@*, browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.16.6,
|
||||
node-releases "^2.0.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
buefy@^0.9.10:
|
||||
version "0.9.10"
|
||||
resolved "https://registry.yarnpkg.com/buefy/-/buefy-0.9.10.tgz#17f64ee1ba43a145d1d3c56f45cba95e4e2975fa"
|
||||
integrity sha512-xXEoy/NTgBNiIfBTCdHi2Vu5SJJdB046py6ekUvYuUgYwRvulySZksdecVNNWdfEVU8iD4esZaRbTLwCegFcVQ==
|
||||
buefy@^0.9.11:
|
||||
version "0.9.11"
|
||||
resolved "https://registry.yarnpkg.com/buefy/-/buefy-0.9.11.tgz#69ab7f46e4f172d43247916f770f29764f9e0931"
|
||||
integrity sha512-WP32SiaM9WVxDtzgdiq7V2zyIvn41NboPgluVqdB6OAi1/QhjO/63m6hd/jy6Vk8r+zuhIZD+aP9KlQ10EhxTQ==
|
||||
dependencies:
|
||||
bulma "0.9.3"
|
||||
|
||||
@@ -2894,10 +2952,10 @@ core-js@^2.6.5:
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
|
||||
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
||||
|
||||
core-js@^3.15.1:
|
||||
version "3.18.3"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.18.3.tgz#86a0bba2d8ec3df860fefcc07a8d119779f01509"
|
||||
integrity sha512-tReEhtMReZaPFVw7dajMx0vlsz3oOb8ajgPoHVYGxr8ErnZ6PcYEvvmjGmXlfpnxpkYSdOQttjB+MvVbCGfvLw==
|
||||
core-js@^3.19.0:
|
||||
version "3.19.0"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.19.0.tgz#9e40098a9bc326c7e81b486abbd5e12b9d275176"
|
||||
integrity sha512-L1TpFRWXZ76vH1yLM+z6KssLZrP8Z6GxxW4auoCj+XiViOzNPJCAuTIkn03BGdFe6Z5clX5t64wRIRypsZQrUg==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.3"
|
||||
@@ -3599,6 +3657,11 @@ etag@^1.8.1, etag@~1.8.1:
|
||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||
|
||||
eventemitter3@^4.0.0:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
||||
|
||||
events@^3.0.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||
@@ -3837,6 +3900,11 @@ flush-write-stream@^1.0.0:
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^2.3.6"
|
||||
|
||||
follow-redirects@^1.0.0:
|
||||
version "1.14.5"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381"
|
||||
integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==
|
||||
|
||||
follow-redirects@^1.14.4:
|
||||
version "1.14.4"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379"
|
||||
@@ -4302,6 +4370,26 @@ http-errors@~1.7.2:
|
||||
statuses ">= 1.5.0 < 2"
|
||||
toidentifier "1.0.0"
|
||||
|
||||
http-proxy-middleware@^1.0.6:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz#43700d6d9eecb7419bf086a128d0f7205d9eb665"
|
||||
integrity sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==
|
||||
dependencies:
|
||||
"@types/http-proxy" "^1.17.5"
|
||||
http-proxy "^1.18.1"
|
||||
is-glob "^4.0.1"
|
||||
is-plain-obj "^3.0.0"
|
||||
micromatch "^4.0.2"
|
||||
|
||||
http-proxy@^1.18.1:
|
||||
version "1.18.1"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
|
||||
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
|
||||
dependencies:
|
||||
eventemitter3 "^4.0.0"
|
||||
follow-redirects "^1.0.0"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
https-browserify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
||||
@@ -4658,6 +4746,11 @@ is-plain-obj@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
||||
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
|
||||
|
||||
is-plain-obj@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
|
||||
integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==
|
||||
|
||||
is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
||||
@@ -5158,7 +5251,7 @@ micromatch@^3.1.10, micromatch@^3.1.4:
|
||||
snapdragon "^0.8.1"
|
||||
to-regex "^3.0.2"
|
||||
|
||||
micromatch@^4.0.0, micromatch@^4.0.4:
|
||||
micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
|
||||
integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
|
||||
@@ -5517,12 +5610,12 @@ num2fraction@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
|
||||
integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
|
||||
|
||||
nuxt-buefy@^0.4.8:
|
||||
version "0.4.10"
|
||||
resolved "https://registry.yarnpkg.com/nuxt-buefy/-/nuxt-buefy-0.4.10.tgz#7e091ebee5c7dd2bf9de46a3b7a36a3a4d0d4fba"
|
||||
integrity sha512-Kk/QrNXUrIe3/iPecb0v1OmcZWfdihioEuPtqW4OUfaDDPrVUdfPPRKLMHNgy1OPxDEGSor/h3zMr23d9/0Nyw==
|
||||
nuxt-buefy@^0.4.11:
|
||||
version "0.4.11"
|
||||
resolved "https://registry.yarnpkg.com/nuxt-buefy/-/nuxt-buefy-0.4.11.tgz#38a4d2a3abae05c64034d005e46728cfd694fa25"
|
||||
integrity sha512-mld92vqY5uo+aQp4E8OnVE0abXPgf5t+k4ckmsMgYGfn0T3pDqJq7O4JLELxfBxfmBxmwce8lJ6ZaJ2GxCNaWQ==
|
||||
dependencies:
|
||||
buefy "^0.9.10"
|
||||
buefy "^0.9.11"
|
||||
|
||||
nuxt-property-decorator@^2.9.1:
|
||||
version "2.9.1"
|
||||
@@ -5542,7 +5635,7 @@ nuxt-typed-vuex@^0.2.0:
|
||||
typed-vuex "0.2.0"
|
||||
upath "^2.0.1"
|
||||
|
||||
nuxt@^2.15.7:
|
||||
nuxt@^2.15.8:
|
||||
version "2.15.8"
|
||||
resolved "https://registry.yarnpkg.com/nuxt/-/nuxt-2.15.8.tgz#946cba46bdaaf0e3918aa27fd9ea0fed8ed303b0"
|
||||
integrity sha512-ceK3qLg/Baj7J8mK9bIxqw9AavrF+LXqwYEreBdY/a4Sj8YV4mIvhqea/6E7VTCNNGvKT2sJ/TTJjtfQ597lTA==
|
||||
@@ -6600,7 +6693,7 @@ prettier@^1.18.2:
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
||||
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
||||
|
||||
prettier@^2.3.2:
|
||||
prettier@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c"
|
||||
integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==
|
||||
@@ -6925,6 +7018,11 @@ repeat-string@^1.6.1:
|
||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
|
||||
|
||||
requires-port@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||
|
||||
resolve-from@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
|
||||
@@ -7062,10 +7160,10 @@ sass-loader@10.1.1, sass-loader@~10.1.1:
|
||||
schema-utils "^3.0.0"
|
||||
semver "^7.3.2"
|
||||
|
||||
sass@^1.43.3:
|
||||
version "1.43.3"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.3.tgz#aa16a69131b84f0cd23189a242571e8905f1ce43"
|
||||
integrity sha512-BJnLngqWpMeS65UvlYYEuCb3/fLxDxhHtOB/gWPxs6NKrslTxGt3ZxwIvOe/0Jm4tWwM/+tIpE3wj4dLEhPDeQ==
|
||||
sass@^1.43.4:
|
||||
version "1.43.4"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.4.tgz#68c7d6a1b004bef49af0d9caf750e9b252105d1f"
|
||||
integrity sha512-/ptG7KE9lxpGSYiXn7Ar+lKOv37xfWsZRtFYal2QHNigyVQDx685VFT/h7ejVr+R8w7H4tmUgtulsKl5YpveOg==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
|
||||
|
||||
Reference in new issue
Block a user