From b32007acd1645422f2d2922b8c9eec1111fd476e Mon Sep 17 00:00:00 2001 From: Pacharapol Withayasakpunt Date: Mon, 25 Oct 2021 06:46:53 +0700 Subject: [PATCH] update --- assets/api.ts | 112 +++-- components/ContextMenu.vue | 5 +- components/cards/LibraryCard.vue | 12 +- components/cards/QuizCard.vue | 837 +++++++++++++++++++++++++++++++ components/tabs/BrowseTab.vue | 22 +- components/tabs/CharacterTab.vue | 6 +- 6 files changed, 939 insertions(+), 55 deletions(-) create mode 100644 components/cards/QuizCard.vue diff --git a/assets/api.ts b/assets/api.ts index aa45b9c..ae730d5 100644 --- a/assets/api.ts +++ b/assets/api.ts @@ -9,17 +9,21 @@ export interface ILevelBrowserByType { export type ILevelBrowser = Record -export interface IUserSettings { - identifier: string - level: number +export interface IUserLocalSettings { levelMin?: number level_browser?: ILevelBrowser } +export interface IUserSettings extends IUserLocalSettings { + identifier: string + level: number +} + export interface IEntry { id: string - entry: string[] + list: string[] reading: string[] + audio: string[] translation: string[] type: IType description: string @@ -36,6 +40,17 @@ export interface ILibrary { isShared?: boolean } +export interface IQuiz { + id: string + type: IType + direction: 'forward' | 'backward' + entry: string + nextReview?: string // format: date-time + wrongStreak?: number + hint: string + mnemonic: string +} + export type ILookup = T & { offset?: number limit?: number @@ -109,17 +124,23 @@ class Api { return r } - async user_updateSettings(u: Omit) { + async user_updateSettings(u: IUserLocalSettings) { return await this.axios.patch('/api/user', u) } - async quiz_getMany(payload: { + async quiz_getById(params: { id: string }) { + return await this.axios + .get('/api/quiz', { params }) + .then((r) => r.data as IQuiz) + .catch(() => null) + } + + async quiz_getManyByEntries(payload: { entries: string[] - select: string[] - type?: string + type: string direction?: string }) { - return await this.axios.post('/api/quiz/getMany', payload).then( + return await this.axios.post('/api/quiz/getManyByEntries', payload).then( (r) => r.data as { result: { @@ -142,6 +163,10 @@ class Api { ) } + async quiz_updateSrsLevel(params: { id: string; df: number }) { + return await this.axios.patch('/api/quiz/srsLevel', { params }) + } + async quiz_create(payload: { entries: string[]; type: IType }) { return await this.axios.put('/api/quiz', payload).then( (r) => @@ -153,6 +178,12 @@ class Api { ) } + async quiz_update(payload: Partial & { id: string }) { + return await this.axios.patch('/api/quiz', payload, { + params: { id: payload.id }, + }) + } + async quiz_deleteMany(params: { entries: string[]; type: IType }) { return await this.axios.post('/api/quiz/deleteMany', params) } @@ -185,39 +216,51 @@ class Api { } async character_getRadical(params: { entry: string }) { - return await this.axios.get('/api/character/radical', { params }).then( - (r) => - r.data as { - result: { + return await this.axios + .get('/api/character/radical', { params }) + .then( + (r) => + r.data as { sub: string[] sup: string[] variant: string[] visual: string[] - } | null - } - ) + } + ) + .catch(() => null) } async character_getVocabulary( params: ILookup<{ entry: string; distinct?: 'id' }> ) { - return await this.axios.get('/api/character/vocabulary', { params }).then( - (r) => - r.data as ILookupResult<{ - entry: string - }> - ) + params.offset = params.offset || 0 + params.limit = params.limit || 5 + + return await this.axios + .get('/api/character/vocabulary', { params }) + .then((r) => r.data as ILookupResult) } async character_getSentence( params: ILookup<{ entry: string; distinct?: 'id' }> ) { - return await this.axios.get('/api/character/sentence', { params }).then( - (r) => - r.data as ILookupResult<{ - entry: string - }> - ) + params.offset = params.offset || 0 + params.limit = params.limit || 5 + + return await this.axios + .get('/api/character/sentence', { params }) + .then((r) => r.data as ILookupResult) + } + + async vocabulary_getSentence( + params: ILookup<{ entry: string; distinct?: 'id' }> + ) { + params.offset = params.offset || 0 + params.limit = params.limit || 5 + + return await this.axios + .get('/api/vocabulary/sentence', { params }) + .then((r) => r.data as ILookupResult) } async all_getRandom(params: { type: IType }) { @@ -263,6 +306,19 @@ class Api { ) } + async all_tokenize(params: { entry: string }) { + return await this.axios + .get('/api/all/tokenize', { + params, + }) + .then( + (r) => + r.data as { + result: string[] + } + ) + } + async all_getReading(params: { entry: string }) { return await this.axios .get('/api/all/reading', { diff --git a/components/ContextMenu.vue b/components/ContextMenu.vue index a61ef87..0997d4c 100644 --- a/components/ContextMenu.vue +++ b/components/ContextMenu.vue @@ -50,7 +50,7 @@
  • - {{ type === 'character' ? 'Show details' : 'Search for Hanzi' }} + {{ type === 'character' ? 'Show details' : 'Search for Kanji' }}
  • @@ -135,9 +135,8 @@ export default class ContextMenu extends Vue { async setQuiz() { if (this.entries.length && this.type) { - const { result } = await api.quiz_getMany({ + const { result } = await api.quiz_getManyByEntries({ entries: this.entries, - select: ['id', 'entry'], type: this.type, direction: this.direction, }) diff --git a/components/cards/LibraryCard.vue b/components/cards/LibraryCard.vue index 782953b..c376122 100644 --- a/components/cards/LibraryCard.vue +++ b/components/cards/LibraryCard.vue @@ -154,9 +154,9 @@ import ContextMenu from '../ContextMenu.vue' type ILibraryEntry = Pick< IEntry, - 'entry' | 'reading' | 'translation' | 'type' + 'list' | 'reading' | 'translation' | 'type' > & { - originalEntry: string + entry: string } @Component({ @@ -204,8 +204,8 @@ export default class LibraryCard extends Vue { isList = false list: ILibraryEntry[] = this.entries.map((el) => ({ - originalEntry: el, - entry: [el], + entry: el, + list: [el], reading: [], translation: [], type: this.type, @@ -267,7 +267,7 @@ export default class LibraryCard extends Vue { [...listMap].map(([k, v]) => api .all_getLookup({ - entry: v.originalEntry, + entry: v.entry, type: this.type, forced: true, limit: 1, @@ -290,7 +290,7 @@ export default class LibraryCard extends Vue { getTagClass(it: IEntry) { const srsLevel = Math.min( - ...it.entry.map((el) => this.srsLevel[el] || -Infinity) + ...it.list.map((el) => this.srsLevel[el] || -Infinity) ) if (srsLevel === -Infinity) return 'is-light' diff --git a/components/cards/QuizCard.vue b/components/cards/QuizCard.vue new file mode 100644 index 0000000..e482aa7 --- /dev/null +++ b/components/cards/QuizCard.vue @@ -0,0 +1,837 @@ + + + + + diff --git a/components/tabs/BrowseTab.vue b/components/tabs/BrowseTab.vue index 110e335..94f4691 100644 --- a/components/tabs/BrowseTab.vue +++ b/components/tabs/BrowseTab.vue @@ -144,7 +144,7 @@ v-if="selected.entry.every((el) => el.length === 1)" value="character" > - Hanzi + Kanji @@ -222,14 +222,14 @@ export default class BrowseTab extends Vue { type: 'desc', } - selected: Omit & { + selected: IEntry & { entry: string - list: string[] } = { id: '', entry: '', list: [], reading: [], + audio: [], translation: [], type: 'vocabulary', description: '', @@ -296,10 +296,7 @@ export default class BrowseTab extends Vue { } async doCreate() { - const { id } = await api.extra_create({ - ...this.selected, - entry: this.selected.list, - }) + const { id } = await api.extra_create(this.selected) this.selected.id = id await this.context.addToQuiz() @@ -314,7 +311,6 @@ export default class BrowseTab extends Vue { if (id) { await api.extra_update({ ...this.selected, - entry: this.selected.list, id, }) @@ -345,6 +341,7 @@ export default class BrowseTab extends Vue { entry: '', list: [], reading: [], + audio: [], translation: [], type: 'vocabulary', description: '', @@ -354,15 +351,10 @@ export default class BrowseTab extends Vue { this.isEditModal = true } - async onTableContextmenu(row: IEntry, evt: MouseEvent) { + async onTableContextmenu(row: IEntry & { entry: string }, evt: MouseEvent) { evt.preventDefault() - const o = jsonClone(row) - this.selected = { - ...o, - entry: o.entry[0]!, - list: o.entry, - } + this.selected = jsonClone(row) this.context.open(evt) } diff --git a/components/tabs/CharacterTab.vue b/components/tabs/CharacterTab.vue index 96f4109..d13701e 100644 --- a/components/tabs/CharacterTab.vue +++ b/components/tabs/CharacterTab.vue @@ -416,7 +416,7 @@ export default class CharacterTab extends Vue { await Promise.all([ (async () => { - const { result: r } = await api.character_getRadical({ + const r = await api.character_getRadical({ entry: this.current, }) @@ -457,7 +457,7 @@ export default class CharacterTab extends Vue { result .map((r) => api - .all_getLookup({ entry: r.entry, type: 'vocabulary', limit: 1 }) + .all_getLookup({ entry: r.list[0]!, type: 'vocabulary', limit: 1 }) .then((r) => r.result[0]!) ) .filter((r) => r) @@ -479,7 +479,7 @@ export default class CharacterTab extends Vue { result .map((r) => api - .all_getLookup({ entry: r.entry, type: 'sentence', limit: 1 }) + .all_getLookup({ entry: r.list[0]!, type: 'sentence', limit: 1 }) .then((r) => r.result[0]!) ) .filter((r) => r)