From 559e8259be084c11993befdc75e6ebdffafb3d3d Mon Sep 17 00:00:00 2001 From: MaysWind Date: Mon, 20 Jan 2025 00:33:22 +0800 Subject: [PATCH] migrate root store to composition API and typescript --- src/DesktopApp.vue | 2 +- src/MobileApp.vue | 2 +- src/models/user.ts | 73 ++- src/stores/index.js | 531 ------------------ src/stores/index.ts | 529 +++++++++++++++++ src/views/base/LoginPageBase.ts | 3 +- src/views/base/SignupPageBase.ts | 3 +- src/views/base/UnlockPageBase.ts | 3 +- src/views/desktop/ForgetPasswordPage.vue | 2 +- src/views/desktop/LoginPage.vue | 2 +- src/views/desktop/MainLayout.vue | 2 +- src/views/desktop/ResetPasswordPage.vue | 2 +- src/views/desktop/SignupPage.vue | 2 +- src/views/desktop/VerifyEmailPage.vue | 2 +- .../settings/tabs/UserBasicSettingTab.vue | 2 +- .../tabs/UserDataManagementSettingTab.vue | 2 +- .../settings/tabs/UserSecuritySettingTab.vue | 2 +- src/views/mobile/LoginPage.vue | 2 +- src/views/mobile/SettingsPage.vue | 2 +- src/views/mobile/SignupPage.vue | 2 +- src/views/mobile/users/DataManagementPage.vue | 2 +- src/views/mobile/users/UserProfilePage.vue | 2 +- 22 files changed, 618 insertions(+), 556 deletions(-) delete mode 100644 src/stores/index.js create mode 100644 src/stores/index.ts diff --git a/src/DesktopApp.vue b/src/DesktopApp.vue index ed2ab007..7429d9b3 100644 --- a/src/DesktopApp.vue +++ b/src/DesktopApp.vue @@ -23,7 +23,7 @@ import { register } from 'register-service-worker'; import { useI18n } from '@/locales/helpers.ts'; -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import { useSettingsStore } from '@/stores/setting.ts'; import { useUserStore } from '@/stores/user.ts'; import { useTokensStore } from '@/stores/token.ts'; diff --git a/src/MobileApp.vue b/src/MobileApp.vue index 73ed5e26..450692ce 100644 --- a/src/MobileApp.vue +++ b/src/MobileApp.vue @@ -13,7 +13,7 @@ import routes from './router/mobile.js'; import { useI18n } from '@/locales/helpers.ts'; -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import { useSettingsStore } from '@/stores/setting.ts'; import { useEnvironmentsStore } from '@/stores/environment.ts'; import { useUserStore } from '@/stores/user.ts'; diff --git a/src/models/user.ts b/src/models/user.ts index 087dfb7e..e172eae8 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -2,10 +2,9 @@ import { LongDateFormat, ShortDateFormat, LongTimeFormat, ShortTimeFormat } from import { DecimalSeparator, DigitGroupingSymbol, DigitGroupingType } from '@/core/numeral.ts'; import { CurrencyDisplayType } from '@/core/currency.ts'; import { PresetAmountColor } from '@/core/color.ts'; +import type { LocalizedPresetCategory } from '@/core/category.ts'; import { TransactionEditScopeType } from '@/core/transaction.ts'; -import type { TransactionCategoryCreateBatchRequest } from './transaction_category.ts'; - export class User { public username: string = ''; public password: string = ''; @@ -16,12 +15,80 @@ export class User { public defaultCurrency: string; public firstDayOfWeek: number; + public defaultAccountId?: string; + public transactionEditScope?: number; + public longDateFormat?: number; + public shortDateFormat?: number; + public longTimeFormat?: number; + public shortTimeFormat?: number; + public decimalSeparator?: number; + public digitGroupingSymbol?: number; + public digitGrouping?: number; + public currencyDisplayType?: number; + public expenseAmountColor?: number; + public incomeAmountColor?: number; + private constructor(language: string, defaultCurrency: string, firstDayOfWeek: number) { this.language = language; this.defaultCurrency = defaultCurrency; this.firstDayOfWeek = firstDayOfWeek; } + public toRegisterRequest(categories?: LocalizedPresetCategory[]): UserRegisterRequest { + return { + username: this.username, + email: this.email, + nickname: this.nickname, + password: this.password, + language: this.language, + defaultCurrency: this.defaultCurrency, + firstDayOfWeek: this.firstDayOfWeek, + categories: categories + }; + } + + public toProfileUpdateRequest(currentPassword?: string): UserProfileUpdateRequest { + return { + email: this.email, + nickname: this.nickname, + password: this.password, + oldPassword: currentPassword, + defaultAccountId: this.defaultAccountId, + transactionEditScope: this.transactionEditScope, + language: this.language, + defaultCurrency: this.defaultCurrency, + firstDayOfWeek: this.firstDayOfWeek, + longDateFormat: this.longDateFormat, + shortDateFormat: this.shortDateFormat, + longTimeFormat: this.longTimeFormat, + shortTimeFormat: this.shortTimeFormat, + decimalSeparator: this.decimalSeparator, + digitGroupingSymbol: this.digitGroupingSymbol, + digitGrouping: this.digitGrouping, + currencyDisplayType: this.currencyDisplayType, + expenseAmountColor: this.expenseAmountColor, + incomeAmountColor: this.incomeAmountColor + }; + } + + public static of(profileResponse: UserProfileResponse): User { + const user = new User(profileResponse.language, profileResponse.defaultCurrency, profileResponse.firstDayOfWeek); + user.defaultAccountId = profileResponse.defaultAccountId; + user.transactionEditScope = profileResponse.transactionEditScope; + user.longDateFormat = profileResponse.longDateFormat; + user.shortDateFormat = profileResponse.shortDateFormat; + user.longTimeFormat = profileResponse.longTimeFormat; + user.shortTimeFormat = profileResponse.shortTimeFormat; + user.decimalSeparator = profileResponse.decimalSeparator; + user.digitGroupingSymbol = profileResponse.digitGroupingSymbol; + user.digitGrouping = profileResponse.digitGrouping; + user.currencyDisplayType = profileResponse.currencyDisplayType; + user.expenseAmountColor = profileResponse.expenseAmountColor; + user.incomeAmountColor = profileResponse.incomeAmountColor; + + return user; + } + public static createNewUser(language: string, defaultCurrency: string, firstDayOfWeek: number): User { return new User(language, defaultCurrency, firstDayOfWeek); } @@ -64,7 +131,7 @@ export interface UserRegisterRequest { readonly language: string; readonly defaultCurrency: string; readonly firstDayOfWeek: number; - readonly categories?: TransactionCategoryCreateBatchRequest; + readonly categories?: LocalizedPresetCategory[]; } export interface UserVerifyEmailResponse { diff --git a/src/stores/index.js b/src/stores/index.js deleted file mode 100644 index a5b797a0..00000000 --- a/src/stores/index.js +++ /dev/null @@ -1,531 +0,0 @@ -import { defineStore } from 'pinia'; - -import { useSettingsStore } from './setting.ts'; -import { useUserStore } from './user.ts'; -import { useAccountsStore } from './account.ts'; -import { useTransactionCategoriesStore } from './transactionCategory.ts'; -import { useTransactionTagsStore } from './transactionTag.ts'; -import { useTransactionTemplatesStore } from './transactionTemplate.js'; -import { useTransactionsStore } from './transaction.js'; -import { useOverviewStore } from './overview.ts'; -import { useStatisticsStore } from './statistics.js'; -import { useExchangeRatesStore } from './exchangeRates.ts'; - -import { - hasUserAppLockState, - getUserAppLockState, - updateCurrentToken, - clearWebAuthnConfig, - clearCurrentSessionToken, - clearCurrentTokenAndUserInfo -} from '@/lib/userstate.ts'; -import services from '@/lib/services.ts'; -import logger from '@/lib/logger.ts'; -import { isObject, isString } from '@/lib/common.ts'; - -export const useRootStore = defineStore('root', { - state: () => ({ - currentNotification: null - }), - actions: { - resetAllStates(resetUserInfoAndSettings) { - if (resetUserInfoAndSettings) { - const exchangeRatesStore = useExchangeRatesStore(); - exchangeRatesStore.resetLatestExchangeRates(); - } - - this.setNotificationContent(null); - - const statisticsStore = useStatisticsStore(); - statisticsStore.resetTransactionStatistics(); - - const overviewStore = useOverviewStore(); - overviewStore.resetTransactionOverview(); - - const transactionsStore = useTransactionsStore(); - transactionsStore.resetTransactions(); - - const transactionTagsStore = useTransactionTagsStore(); - transactionTagsStore.resetTransactionTags(); - - const transactionCategoriesStore = useTransactionCategoriesStore(); - transactionCategoriesStore.resetTransactionCategories(); - - const transactionTemplatesStore = useTransactionTemplatesStore(); - transactionTemplatesStore.resetTransactionTemplates(); - - const accountsStore = useAccountsStore(); - accountsStore.resetAccounts(); - - if (resetUserInfoAndSettings) { - const userStore = useUserStore(); - userStore.resetUserBasicInfo(); - } - }, - authorize({ loginName, password }) { - const settingsStore = useSettingsStore(); - - return new Promise((resolve, reject) => { - services.authorize({ - loginName: loginName, - password: password - }).then(response => { - const data = response.data; - - if (!data || !data.success || !data.result || !data.result.token) { - reject({ message: 'Unable to log in' }); - return; - } - - if (data.result.need2FA) { - resolve(data.result); - return; - } - - if (settingsStore.appSettings.applicationLock || hasUserAppLockState()) { - const appLockState = getUserAppLockState(); - - if (!appLockState || appLockState.username !== data.result.user.username) { - clearCurrentTokenAndUserInfo(true); - settingsStore.setEnableApplicationLock(false); - settingsStore.setEnableApplicationLockWebAuthn(false); - clearWebAuthnConfig(); - } - } - - updateCurrentToken(data.result.token); - - if (data.result.user && isObject(data.result.user)) { - const userStore = useUserStore(); - userStore.storeUserBasicInfo(data.result.user); - } - - resolve(data.result); - }).catch(error => { - logger.error('failed to login', error); - - if (error && error.processed) { - reject(error); - } else if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else { - reject({ message: 'Unable to log in' }); - } - }); - }); - }, - authorize2FA({ token, passcode, recoveryCode }) { - const settingsStore = useSettingsStore(); - - return new Promise((resolve, reject) => { - let promise = null; - - if (passcode) { - promise = services.authorize2FA({ - passcode: passcode, - token: token - }); - } else if (recoveryCode) { - promise = services.authorize2FAByBackupCode({ - recoveryCode: recoveryCode, - token: token - }); - } else { - reject({ message: 'An error occurred' }); - return; - } - - promise.then(response => { - const data = response.data; - - if (!data || !data.success || !data.result || !data.result.token) { - reject({ message: 'Unable to verify' }); - return; - } - - if (settingsStore.appSettings.applicationLock || hasUserAppLockState()) { - const appLockState = getUserAppLockState(); - - if (!appLockState || appLockState.username !== data.result.user.username) { - clearCurrentTokenAndUserInfo(true); - settingsStore.setEnableApplicationLock(false); - settingsStore.setEnableApplicationLockWebAuthn(false); - clearWebAuthnConfig(); - } - } - - updateCurrentToken(data.result.token); - - if (data.result.user && isObject(data.result.user)) { - const userStore = useUserStore(); - userStore.storeUserBasicInfo(data.result.user); - } - - resolve(data.result); - }).catch(error => { - logger.error('failed to verify 2fa', error); - - if (error && error.processed) { - reject(error); - } else if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else { - reject({ message: 'Unable to verify' }); - } - }); - }); - }, - register({ user, presetCategories }) { - const settingsStore = useSettingsStore(); - - return new Promise((resolve, reject) => { - services.register({ - username: user.username, - password: user.password, - email: user.email, - nickname: user.nickname, - language: user.language, - defaultCurrency: user.defaultCurrency, - firstDayOfWeek: user.firstDayOfWeek, - categories: presetCategories - }).then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to sign up' }); - return; - } - - if (settingsStore.appSettings.applicationLock) { - settingsStore.setEnableApplicationLock(false); - settingsStore.setEnableApplicationLockWebAuthn(false); - clearWebAuthnConfig(); - } - - if (data.result.token && isString(data.result.token)) { - updateCurrentToken(data.result.token); - } - - if (data.result.user && isObject(data.result.user)) { - const userStore = useUserStore(); - userStore.storeUserBasicInfo(data.result.user); - } - - resolve(data.result); - }).catch(error => { - logger.error('failed to sign up', error); - - if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else if (!error.processed) { - reject({ message: 'Unable to sign up' }); - } else { - reject(error); - } - }); - }); - }, - lock() { - clearCurrentSessionToken(); - this.resetAllStates(false); - }, - logout() { - const self = this; - - return new Promise((resolve, reject) => { - services.logout().then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to logout' }); - return; - } - - clearCurrentTokenAndUserInfo(true); - clearWebAuthnConfig(); - self.resetAllStates(true); - - resolve(data.result); - }).catch(error => { - logger.error('failed to log out', error); - - if (error && error.processed) { - reject(error); - } else if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else { - reject({ message: 'Unable to logout' }); - } - }); - }); - }, - forceLogout() { - clearCurrentTokenAndUserInfo(true); - clearWebAuthnConfig(); - this.resetAllStates(true); - }, - verifyEmail({ token, requestNewToken }) { - return new Promise((resolve, reject) => { - services.verifyEmail({ - token, - requestNewToken - }).then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to verify email' }); - return; - } - - if (data.result.newToken && isString(data.result.newToken)) { - updateCurrentToken(data.result.newToken); - } - - if (data.result.user && isObject(data.result.user)) { - const userStore = useUserStore(); - userStore.storeUserBasicInfo(data.result.user); - } - - resolve(data.result); - }).catch(error => { - logger.error('failed to verify email', error); - - if (error && error.processed) { - reject(error); - } else if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else { - reject({ message: 'Unable to verify email' }); - } - }); - }); - }, - resendVerifyEmailByUnloginUser({ email, password }) { - return new Promise((resolve, reject) => { - services.resendVerifyEmailByUnloginUser({ - email, - password - }).then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to resend validation email' }); - return; - } - - resolve(data.result); - }).catch(error => { - logger.error('failed to resend verify email', error); - - if (error && error.processed) { - reject(error); - } else if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else { - reject({ message: 'Unable to resend validation email' }); - } - }); - }); - }, - requestResetPassword({ email }) { - return new Promise((resolve, reject) => { - services.requestResetPassword({ - email - }).then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to send password reset email' }); - return; - } - - resolve(data.result); - }).catch(error => { - logger.error('failed to send password reset email', error); - - if (error && error.processed) { - reject(error); - } else if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else { - reject({ message: 'Unable to send password reset email' }); - } - }); - }); - }, - resetPassword({ email, token, password }) { - return new Promise((resolve, reject) => { - services.resetPassword({ - token, - email, - password - }).then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to reset password' }); - return; - } - - resolve(data.result); - }).catch(error => { - logger.error('failed to reset password', error); - - if (error && error.processed) { - reject(error); - } else if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else { - reject({ message: 'Unable to reset password' }); - } - }); - }); - }, - updateUserProfile({ profile, currentPassword }) { - return new Promise((resolve, reject) => { - services.updateProfile({ - password: profile.password, - oldPassword: currentPassword, - email: profile.email, - nickname: profile.nickname, - defaultAccountId: profile.defaultAccountId, - transactionEditScope: profile.transactionEditScope, - language: profile.language, - defaultCurrency: profile.defaultCurrency, - firstDayOfWeek: profile.firstDayOfWeek, - longDateFormat: profile.longDateFormat, - shortDateFormat: profile.shortDateFormat, - longTimeFormat: profile.longTimeFormat, - shortTimeFormat: profile.shortTimeFormat, - decimalSeparator: profile.decimalSeparator, - digitGroupingSymbol: profile.digitGroupingSymbol, - digitGrouping: profile.digitGrouping, - currencyDisplayType: profile.currencyDisplayType, - expenseAmountColor: profile.expenseAmountColor, - incomeAmountColor: profile.incomeAmountColor - }).then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to update user profile' }); - return; - } - - if (data.result.newToken && isString(data.result.newToken)) { - updateCurrentToken(data.result.newToken); - } - - if (data.result.user && isObject(data.result.user)) { - const userStore = useUserStore(); - userStore.storeUserBasicInfo(data.result.user); - } - - const accountsStore = useAccountsStore(); - if (!accountsStore.accountListStateInvalid) { - accountsStore.updateAccountListInvalidState(true); - } - - const overviewStore = useOverviewStore(); - if (!overviewStore.transactionOverviewStateInvalid) { - overviewStore.updateTransactionOverviewInvalidState(true); - } - - const statisticsStore = useStatisticsStore(); - if (!statisticsStore.transactionStatisticsStateInvalid) { - statisticsStore.updateTransactionStatisticsInvalidState(true); - } - - resolve(data.result); - }).catch(error => { - logger.error('failed to save user profile', error); - - if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else if (!error.processed) { - reject({ message: 'Unable to update user profile' }); - } else { - reject(error); - } - }); - }); - }, - resendVerifyEmailByLoginedUser() { - return new Promise((resolve, reject) => { - services.resendVerifyEmailByLoginedUser().then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to resend validation email' }); - return; - } - - resolve(data.result); - }).catch(error => { - logger.error('failed to resend verify email', error); - - if (error && error.processed) { - reject(error); - } else if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else { - reject({ message: 'Unable to resend validation email' }); - } - }); - }); - }, - clearUserData({ password }) { - return new Promise((resolve, reject) => { - services.clearData({ - password: password - }).then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to clear user data' }); - return; - } - - const accountsStore = useAccountsStore(); - if (!accountsStore.accountListStateInvalid) { - accountsStore.updateAccountListInvalidState(true); - } - - const transactionCategoriesStore = useTransactionCategoriesStore(); - if (!transactionCategoriesStore.transactionCategoryListStateInvalid) { - transactionCategoriesStore.updateTransactionCategoryListInvalidState(true); - } - - const transactionTagsStore = useTransactionTagsStore(); - if (!transactionTagsStore.transactionTagListStateInvalid) { - transactionTagsStore.updateTransactionTagListInvalidState(true); - } - - const overviewStore = useOverviewStore(); - if (!overviewStore.transactionOverviewStateInvalid) { - overviewStore.updateTransactionOverviewInvalidState(true); - } - - const statisticsStore = useStatisticsStore(); - if (!statisticsStore.transactionStatisticsStateInvalid) { - statisticsStore.updateTransactionStatisticsInvalidState(true); - } - - resolve(data.result); - }).catch(error => { - logger.error('failed to clear user data', error); - - if (error && error.processed) { - reject(error); - } else if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else { - reject({ message: 'Unable to clear user data' }); - } - }); - }); - }, - setNotificationContent(content) { - this.currentNotification = content; - } - } -}); diff --git a/src/stores/index.ts b/src/stores/index.ts new file mode 100644 index 00000000..e5dffd86 --- /dev/null +++ b/src/stores/index.ts @@ -0,0 +1,529 @@ +import { ref } from 'vue'; +import { defineStore } from 'pinia'; + +import { useSettingsStore } from './setting.ts'; +import { useUserStore } from './user.ts'; +import { useAccountsStore } from './account.ts'; +import { useTransactionCategoriesStore } from './transactionCategory.ts'; +import { useTransactionTagsStore } from './transactionTag.ts'; +// @ts-expect-error the above file is migrating to ts +import { useTransactionTemplatesStore } from './transactionTemplate.js'; +// @ts-expect-error the above file is migrating to ts +import { useTransactionsStore } from './transaction.js'; +import { useOverviewStore } from './overview.ts'; +// @ts-expect-error the above file is migrating to ts +import { useStatisticsStore } from './statistics.js'; +import { useExchangeRatesStore } from './exchangeRates.ts'; + +import type { AuthResponse, RegisterResponse } from '@/models/auth_response.ts'; +import type { User, UserLoginRequest, UserResendVerifyEmailRequest, UserVerifyEmailResponse, UserProfileUpdateResponse } from '@/models/user.ts'; +import type { LocalizedPresetCategory } from '@/core/category.ts'; +import type { ForgetPasswordRequest } from '@/models/forget_password.ts'; + +import { + hasUserAppLockState, + getUserAppLockState, + updateCurrentToken, + clearWebAuthnConfig, + clearCurrentSessionToken, + clearCurrentTokenAndUserInfo +} from '@/lib/userstate.ts'; +import services, { type ApiResponsePromise } from '@/lib/services.ts'; +import logger from '@/lib/logger.ts'; +import { isObject, isString } from '@/lib/common.ts'; + +export const useRootStore = defineStore('root', () => { + const settingsStore = useSettingsStore(); + const userStore = useUserStore(); + const accountsStore = useAccountsStore(); + const transactionCategoriesStore = useTransactionCategoriesStore(); + const transactionTagsStore = useTransactionTagsStore(); + const transactionTemplatesStore = useTransactionTemplatesStore(); + const transactionsStore = useTransactionsStore(); + const overviewStore = useOverviewStore(); + const statisticsStore = useStatisticsStore(); + const exchangeRatesStore = useExchangeRatesStore(); + + const currentNotification = ref(null); + + function resetAllStates(resetUserInfoAndSettings: boolean): void { + if (resetUserInfoAndSettings) { + exchangeRatesStore.resetLatestExchangeRates(); + } + + setNotificationContent(null); + + statisticsStore.resetTransactionStatistics(); + overviewStore.resetTransactionOverview(); + transactionsStore.resetTransactions(); + transactionTagsStore.resetTransactionTags(); + transactionCategoriesStore.resetTransactionCategories(); + transactionTemplatesStore.resetTransactionTemplates(); + accountsStore.resetAccounts(); + + if (resetUserInfoAndSettings) { + userStore.resetUserBasicInfo(); + } + } + + function setNotificationContent(content: string | null): void { + currentNotification.value = content; + } + + function authorize(req: UserLoginRequest): Promise { + return new Promise((resolve, reject) => { + services.authorize(req).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result || !data.result.token) { + reject({ message: 'Unable to log in' }); + return; + } + + if (data.result.need2FA) { + resolve(data.result); + return; + } + + if (settingsStore.appSettings.applicationLock || hasUserAppLockState()) { + const appLockState = getUserAppLockState(); + + if (!appLockState || appLockState.username !== data.result.user?.username) { + clearCurrentTokenAndUserInfo(true); + settingsStore.setEnableApplicationLock(false); + settingsStore.setEnableApplicationLockWebAuthn(false); + clearWebAuthnConfig(); + } + } + + updateCurrentToken(data.result.token); + + if (data.result.user && isObject(data.result.user)) { + userStore.storeUserBasicInfo(data.result.user); + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to login', error); + + if (error && error.processed) { + reject(error); + } else if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else { + reject({ message: 'Unable to log in' }); + } + }); + }); + } + + function authorize2FA({ token, passcode, recoveryCode }: { token: string, passcode: string | null, recoveryCode: string | null }): Promise { + return new Promise((resolve, reject) => { + let promise: ApiResponsePromise; + + if (passcode) { + promise = services.authorize2FA({ + passcode: passcode, + token: token + }); + } else if (recoveryCode) { + promise = services.authorize2FAByBackupCode({ + recoveryCode: recoveryCode, + token: token + }); + } else { + reject({ message: 'An error occurred' }); + return; + } + + promise.then(response => { + const data = response.data; + + if (!data || !data.success || !data.result || !data.result.token) { + reject({ message: 'Unable to verify' }); + return; + } + + if (settingsStore.appSettings.applicationLock || hasUserAppLockState()) { + const appLockState = getUserAppLockState(); + + if (!appLockState || appLockState.username !== data.result.user?.username) { + clearCurrentTokenAndUserInfo(true); + settingsStore.setEnableApplicationLock(false); + settingsStore.setEnableApplicationLockWebAuthn(false); + clearWebAuthnConfig(); + } + } + + updateCurrentToken(data.result.token); + + if (data.result.user && isObject(data.result.user)) { + userStore.storeUserBasicInfo(data.result.user); + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to verify 2fa', error); + + if (error && error.processed) { + reject(error); + } else if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else { + reject({ message: 'Unable to verify' }); + } + }); + }); + } + + function register({ user, presetCategories }: { user: User, presetCategories?: LocalizedPresetCategory[] }): Promise { + return new Promise((resolve, reject) => { + services.register(user.toRegisterRequest(presetCategories)).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to sign up' }); + return; + } + + if (settingsStore.appSettings.applicationLock) { + settingsStore.setEnableApplicationLock(false); + settingsStore.setEnableApplicationLockWebAuthn(false); + clearWebAuthnConfig(); + } + + if (data.result.token && isString(data.result.token)) { + updateCurrentToken(data.result.token); + } + + if (data.result.user && isObject(data.result.user)) { + userStore.storeUserBasicInfo(data.result.user); + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to sign up', error); + + if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else if (!error.processed) { + reject({ message: 'Unable to sign up' }); + } else { + reject(error); + } + }); + }); + } + + function lock(): void { + clearCurrentSessionToken(); + resetAllStates(false); + } + + function logout(): Promise { + return new Promise((resolve, reject) => { + services.logout().then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to logout' }); + return; + } + + clearCurrentTokenAndUserInfo(true); + clearWebAuthnConfig(); + resetAllStates(true); + + resolve(data.result); + }).catch(error => { + logger.error('failed to log out', error); + + if (error && error.processed) { + reject(error); + } else if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else { + reject({ message: 'Unable to logout' }); + } + }); + }); + } + + function forceLogout(): void { + clearCurrentTokenAndUserInfo(true); + clearWebAuthnConfig(); + resetAllStates(true); + } + + function verifyEmail({ token, requestNewToken }: { token: string, requestNewToken: boolean }): Promise { + return new Promise((resolve, reject) => { + services.verifyEmail({ + token, + requestNewToken + }).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to verify email' }); + return; + } + + if (data.result.newToken && isString(data.result.newToken)) { + updateCurrentToken(data.result.newToken); + } + + if (data.result.user && isObject(data.result.user)) { + userStore.storeUserBasicInfo(data.result.user); + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to verify email', error); + + if (error && error.processed) { + reject(error); + } else if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else { + reject({ message: 'Unable to verify email' }); + } + }); + }); + } + + function resendVerifyEmailByUnloginUser(req: UserResendVerifyEmailRequest): Promise { + return new Promise((resolve, reject) => { + services.resendVerifyEmailByUnloginUser(req).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to resend validation email' }); + return; + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to resend verify email', error); + + if (error && error.processed) { + reject(error); + } else if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else { + reject({ message: 'Unable to resend validation email' }); + } + }); + }); + } + + function requestResetPassword(req: ForgetPasswordRequest): Promise { + return new Promise((resolve, reject) => { + services.requestResetPassword(req).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to send password reset email' }); + return; + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to send password reset email', error); + + if (error && error.processed) { + reject(error); + } else if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else { + reject({ message: 'Unable to send password reset email' }); + } + }); + }); + } + + function resetPassword({ email, token, password }: { email: string, token: string, password: string }): Promise { + return new Promise((resolve, reject) => { + services.resetPassword({ + email, + token, + password + }).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to reset password' }); + return; + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to reset password', error); + + if (error && error.processed) { + reject(error); + } else if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else { + reject({ message: 'Unable to reset password' }); + } + }); + }); + } + + function updateUserProfile({ profile, currentPassword }: { profile: User, currentPassword?: string }): Promise { + return new Promise((resolve, reject) => { + services.updateProfile({ + password: profile.password, + oldPassword: currentPassword, + email: profile.email, + nickname: profile.nickname, + defaultAccountId: profile.defaultAccountId, + transactionEditScope: profile.transactionEditScope, + language: profile.language, + defaultCurrency: profile.defaultCurrency, + firstDayOfWeek: profile.firstDayOfWeek, + longDateFormat: profile.longDateFormat, + shortDateFormat: profile.shortDateFormat, + longTimeFormat: profile.longTimeFormat, + shortTimeFormat: profile.shortTimeFormat, + decimalSeparator: profile.decimalSeparator, + digitGroupingSymbol: profile.digitGroupingSymbol, + digitGrouping: profile.digitGrouping, + currencyDisplayType: profile.currencyDisplayType, + expenseAmountColor: profile.expenseAmountColor, + incomeAmountColor: profile.incomeAmountColor + }).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to update user profile' }); + return; + } + + if (data.result.newToken && isString(data.result.newToken)) { + updateCurrentToken(data.result.newToken); + } + + if (data.result.user && isObject(data.result.user)) { + userStore.storeUserBasicInfo(data.result.user); + } + + if (!accountsStore.accountListStateInvalid) { + accountsStore.updateAccountListInvalidState(true); + } + + if (!overviewStore.transactionOverviewStateInvalid) { + overviewStore.updateTransactionOverviewInvalidState(true); + } + + if (!statisticsStore.transactionStatisticsStateInvalid) { + statisticsStore.updateTransactionStatisticsInvalidState(true); + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to save user profile', error); + + if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else if (!error.processed) { + reject({ message: 'Unable to update user profile' }); + } else { + reject(error); + } + }); + }); + } + + function resendVerifyEmailByLoginedUser(): Promise { + return new Promise((resolve, reject) => { + services.resendVerifyEmailByLoginedUser().then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to resend validation email' }); + return; + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to resend verify email', error); + + if (error && error.processed) { + reject(error); + } else if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else { + reject({ message: 'Unable to resend validation email' }); + } + }); + }); + } + + function clearUserData({ password }: { password: string }): Promise { + return new Promise((resolve, reject) => { + services.clearData({ + password: password + }).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to clear user data' }); + return; + } + + if (!accountsStore.accountListStateInvalid) { + accountsStore.updateAccountListInvalidState(true); + } + + if (!transactionCategoriesStore.transactionCategoryListStateInvalid) { + transactionCategoriesStore.updateTransactionCategoryListInvalidState(true); + } + + if (!transactionTagsStore.transactionTagListStateInvalid) { + transactionTagsStore.updateTransactionTagListInvalidState(true); + } + + if (!overviewStore.transactionOverviewStateInvalid) { + overviewStore.updateTransactionOverviewInvalidState(true); + } + + if (!statisticsStore.transactionStatisticsStateInvalid) { + statisticsStore.updateTransactionStatisticsInvalidState(true); + } + + resolve(data.result); + }).catch(error => { + logger.error('failed to clear user data', error); + + if (error && error.processed) { + reject(error); + } else if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else { + reject({ message: 'Unable to clear user data' }); + } + }); + }); + } + + return { + // states + currentNotification, + // functions + setNotificationContent, + authorize, + authorize2FA, + register, + lock, + logout, + forceLogout, + verifyEmail, + resendVerifyEmailByUnloginUser, + requestResetPassword, + resetPassword, + updateUserProfile, + resendVerifyEmailByLoginedUser, + clearUserData + }; +}); diff --git a/src/views/base/LoginPageBase.ts b/src/views/base/LoginPageBase.ts index 4dc627d0..6c5c12a6 100644 --- a/src/views/base/LoginPageBase.ts +++ b/src/views/base/LoginPageBase.ts @@ -2,8 +2,7 @@ import { ref, computed } from 'vue'; import { useI18n } from '@/locales/helpers.ts'; -// @ts-expect-error the above file is migrating to ts -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import { useSettingsStore } from '@/stores/setting.ts'; import { useExchangeRatesStore } from '@/stores/exchangeRates.ts'; diff --git a/src/views/base/SignupPageBase.ts b/src/views/base/SignupPageBase.ts index ba1d42c5..8cb0eea2 100644 --- a/src/views/base/SignupPageBase.ts +++ b/src/views/base/SignupPageBase.ts @@ -2,8 +2,7 @@ import { ref, computed } from 'vue'; import { useI18n } from '@/locales/helpers.ts'; -// @ts-expect-error the above file is migrating to ts -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import { useSettingsStore } from '@/stores/setting.ts'; import { useUserStore } from '@/stores/user.ts'; import { useExchangeRatesStore } from '@/stores/exchangeRates.ts'; diff --git a/src/views/base/UnlockPageBase.ts b/src/views/base/UnlockPageBase.ts index 5791a51d..9ab3fd5f 100644 --- a/src/views/base/UnlockPageBase.ts +++ b/src/views/base/UnlockPageBase.ts @@ -2,8 +2,7 @@ import { ref, computed } from 'vue'; import { useI18n } from '@/locales/helpers.ts'; -// @ts-expect-error the above file is migrating to ts -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import { useSettingsStore } from '@/stores/setting.ts'; import { useUserStore } from '@/stores/user.ts'; import { useTokensStore } from '@/stores/token.ts'; diff --git a/src/views/desktop/ForgetPasswordPage.vue b/src/views/desktop/ForgetPasswordPage.vue index 0a1d9b17..75664cf6 100644 --- a/src/views/desktop/ForgetPasswordPage.vue +++ b/src/views/desktop/ForgetPasswordPage.vue @@ -115,7 +115,7 @@ import { useTheme } from 'vuetify'; import type { LanguageOption } from '@/locales/index.ts'; import { useI18n } from '@/locales/helpers.ts'; -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import { useSettingsStore } from '@/stores/setting.ts'; import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts'; diff --git a/src/views/desktop/LoginPage.vue b/src/views/desktop/LoginPage.vue index 30585441..c09cfeca 100644 --- a/src/views/desktop/LoginPage.vue +++ b/src/views/desktop/LoginPage.vue @@ -179,7 +179,7 @@ import type { LanguageOption } from '@/locales/index.ts'; import { useI18n } from '@/locales/helpers.ts'; import { useLoginPageBase } from '@/views/base/LoginPageBase.ts'; -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts'; import { KnownErrorCode } from '@/consts/api.ts'; diff --git a/src/views/desktop/MainLayout.vue b/src/views/desktop/MainLayout.vue index 182133f7..bebaa150 100644 --- a/src/views/desktop/MainLayout.vue +++ b/src/views/desktop/MainLayout.vue @@ -201,7 +201,7 @@ import { useRoute, useRouter } from 'vue-router'; import { useI18n } from '@/locales/helpers.ts'; -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import { useSettingsStore } from '@/stores/setting.ts'; import { useUserStore } from '@/stores/user.ts'; diff --git a/src/views/desktop/ResetPasswordPage.vue b/src/views/desktop/ResetPasswordPage.vue index e0817e9b..34708e91 100644 --- a/src/views/desktop/ResetPasswordPage.vue +++ b/src/views/desktop/ResetPasswordPage.vue @@ -142,7 +142,7 @@ import { useTheme } from 'vuetify'; import type { LanguageOption } from '@/locales/index.ts'; import { useI18n } from '@/locales/helpers.ts'; -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import { useSettingsStore } from '@/stores/setting.ts'; import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts'; diff --git a/src/views/desktop/SignupPage.vue b/src/views/desktop/SignupPage.vue index 6cdd6c30..71cb93cf 100644 --- a/src/views/desktop/SignupPage.vue +++ b/src/views/desktop/SignupPage.vue @@ -246,7 +246,7 @@ import type { LanguageOption } from '@/locales/index.ts'; import { useI18n } from '@/locales/helpers.ts'; import { useSignupPageBase } from '@/views/base/SignupPageBase.ts'; -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import type { PartialRecord, TypeAndDisplayName } from '@/core/base.ts'; import type { LocalizedCurrencyInfo } from '@/core/currency.ts'; diff --git a/src/views/desktop/VerifyEmailPage.vue b/src/views/desktop/VerifyEmailPage.vue index d74d30ae..329ca8c9 100644 --- a/src/views/desktop/VerifyEmailPage.vue +++ b/src/views/desktop/VerifyEmailPage.vue @@ -120,7 +120,7 @@ import { useTheme } from 'vuetify'; import type { LanguageOption } from '@/locales/index.ts'; import { useI18n } from '@/locales/helpers.ts'; -import { useRootStore } from '@/stores/index.js'; +import { useRootStore } from '@/stores/index.ts'; import { useSettingsStore } from '@/stores/setting.ts'; import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts'; diff --git a/src/views/desktop/user/settings/tabs/UserBasicSettingTab.vue b/src/views/desktop/user/settings/tabs/UserBasicSettingTab.vue index 0b14c172..f27700f8 100644 --- a/src/views/desktop/user/settings/tabs/UserBasicSettingTab.vue +++ b/src/views/desktop/user/settings/tabs/UserBasicSettingTab.vue @@ -331,7 +331,7 @@