import _axios, { AxiosInstance } from 'axios' export type ITypeLevel = 'character' | 'vocabulary' export type IType = ITypeLevel | 'sentence' export interface ILevelBrowserByType { whatToShow: string[] } export type ILevelBrowser = Record export interface IUserLocalSettings { levelMin?: number level_browser?: ILevelBrowser } export interface IUserSettings extends IUserLocalSettings { identifier: string level: number } export interface IDictionaryLookup { id: string list: string[] reading: string[] audio: { mimetype?: string url: string }[] translation: string[] type: IType description: string tag: string[] level?: number } export interface IEntryLookup { entry: string sub: string[] sup: string[] variant: string[] visual: { entry: string }[] amalgamation: { entry: string type?: IType }[] component: { entry: string type?: IType }[] } export interface ILibrary { id: string title: string list: string[] type: IType description: string tag: string[] 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 } export interface ILookupResult { result: T[] count: number } class Api { axios: AxiosInstance _isAuthorized = true _user = '' constructor() { this.axios = _axios.create() } async setWanikaniApiKey(apiKey?: string) { const setApiKey = () => { if (apiKey) { this.axios.defaults.headers.common['Authorization'] = `Bearer ${apiKey}` } else { delete this.axios.defaults.headers.common['Authorization'] } } setApiKey() return await this.isAuthorized().then((r) => { if (!r) { apiKey = '' setApiKey() } return r }) } async isAuthorized(): Promise { if (this._isAuthorized && !this._user) { const r = await this.user_getSettings({ select: ['identifier'] }) if (r) { this._user = r.identifier this._isAuthorized = true } } return this._isAuthorized } async user_getSettings(params: { select: (keyof IUserSettings)[] }) { const r = await this.axios .get('/api/user', { params: { ...params, select: params.select.join(','), }, }) .then((r) => r.data as IUserSettings) .then((r) => ({ ...r, levelMin: r.levelMin || 1, })) .catch(() => null) if (!r) { return null } return r } async user_updateSettings(u: IUserLocalSettings) { return await this.axios.patch('/api/user', u) } 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[] type: string direction?: string }) { return await this.axios.post('/api/quiz/getManyByEntries', payload).then( (r) => r.data as { result: { id: string entry: string }[] } ) } async quiz_getSrsLevel(payload: { entries: string[]; type: IType }) { return await this.axios.post('/api/quiz/getSrsLevel', payload).then( (r) => r.data as { result: { entry: string srsLevel: number }[] } ) } 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) => r.data as { result: { ids: string[] }[] } ) } 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) } async extra_getQuery(params: ILookup<{ q: string }>) { params.offset = params.offset || 0 params.limit = params.limit || 5 return await this.axios .get('/api/extra/q', { params }) .then((r) => r.data as ILookupResult) } async extra_create(payload: Omit) { return await this.axios .put('/api/extra', payload) .then((r) => r.data as { id: string }) } async extra_update(payload: IDictionaryLookup) { return await this.axios.patch('/api/extra', payload, { params: { id: payload.id }, }) } async extra_delete(params: { id: string }) { return await this.axios.delete('/api/extra', { params, }) } async character_getVocabulary( params: ILookup<{ entry: string; distinct?: 'id' }> ) { 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' }> ) { 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_getVocabulary( params: ILookup<{ entry: string; distinct?: 'id' }> ) { params.offset = params.offset || 0 params.limit = params.limit || 5 return await this.axios .get('/api/vocabulary/vocabulary', { 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 }) { return await this.axios.get('/api/all/random', { params }).then( (r) => r.data as { result: string level: number } ) } async all_getQuery( params: ILookup<{ q: string; type?: IType; distinct?: 'id' }> ) { params.offset = params.offset || 0 params.limit = params.limit || 5 return await this.axios.get('/api/all/q', { params }).then( (r) => r.data as ILookupResult<{ entry: string type: IType }> ) } async all_getLookup( params: ILookup<{ entry: string; type: IType; forced?: boolean }> ) { params.offset = params.offset || 0 params.limit = params.limit || 5 return await this.axios .get('/api/all', { params, }) .then((r) => r.data as ILookupResult) } async all_getComponent(params: { entry: string; type: IType }) { return await this.axios .get('/api/all/components', { params }) .then((r) => r.data as IEntryLookup) .catch(() => null) } 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', { params, }) .then( (r) => r.data as { result: string } ) } async all_getMeaning(params: { entry: string }) { return await this.axios .get('/api/all/meaning', { params, }) .then( (r) => r.data as { result: { entry?: string meaning: string }[] } ) } async library_getQuery(params: ILookup<{ q: string }>) { return await this.axios .get('/api/library/q', { params }) .then((r) => r.data as ILookupResult) } async library_getLevelList(payload: { type: ITypeLevel settings: ILevelBrowserByType }) { return await this.axios.post('/api/library/getLevelList', payload).then( (r) => r.data as { result: { entry: string level: number }[] } ) } async library_create(payload: Omit) { return await this.axios.put('/api/library', payload).then( (r) => r.data as { id: string } ) } async library_update(payload: ILibrary) { return await this.axios.patch('/api/library', payload, { params: { id: payload.id }, }) } async library_delete(params: { id: string }) { return await this.axios.delete('/api/library', { params }) } } export const api = new Api()