diff --git a/next-ui/src/App.vue b/next-ui/src/App.vue index f803f41a4..d47aabfaf 100644 --- a/next-ui/src/App.vue +++ b/next-ui/src/App.vue @@ -1,6 +1,8 @@ diff --git a/next-ui/src/api/komga-client.ts b/next-ui/src/api/komga-client.ts index 4de9d9c86..4e7f24bca 100644 --- a/next-ui/src/api/komga-client.ts +++ b/next-ui/src/api/komga-client.ts @@ -1,6 +1,7 @@ import type { Middleware } from 'openapi-fetch' import createClient from 'openapi-fetch' import type { paths } from '@/generated/openapi/komga' +import { globalProperties } from '@/main' // Middleware that throws on error, so it works with Pinia Colada const coladaMiddleware: Middleware = { @@ -23,7 +24,11 @@ const coladaMiddleware: Middleware = { onError() { throw new Error('error', { cause: { - message: 'Server is unreachable', + message: globalProperties.$intl.formatMessage({ + description: 'Network error: error message when the server cannot be reached', + defaultMessage: 'Server is unreachable', + id: '/6WuF/', + }), }, }) }, diff --git a/next-ui/src/components.d.ts b/next-ui/src/components.d.ts index ea8a0936e..7da3029ff 100644 --- a/next-ui/src/components.d.ts +++ b/next-ui/src/components.d.ts @@ -29,6 +29,7 @@ declare module 'vue' { LocaleSelector: typeof import('./components/LocaleSelector.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + SnackQueue: typeof import('./components/SnackQueue.vue')['default'] ThemeSelector: typeof import('./components/ThemeSelector.vue')['default'] } } diff --git a/next-ui/src/components/SnackQueue.vue b/next-ui/src/components/SnackQueue.vue new file mode 100644 index 000000000..f5b3dc1b0 --- /dev/null +++ b/next-ui/src/components/SnackQueue.vue @@ -0,0 +1,16 @@ + + + + + + + diff --git a/next-ui/src/main.ts b/next-ui/src/main.ts index c8fc17224..ffffa61e5 100644 --- a/next-ui/src/main.ts +++ b/next-ui/src/main.ts @@ -18,3 +18,5 @@ const app = createApp(App) registerPlugins(app) app.mount('#app') + +export const globalProperties = app.config.globalProperties diff --git a/next-ui/src/pages/login.vue b/next-ui/src/pages/login.vue index 07caf3dbc..4605ca721 100644 --- a/next-ui/src/pages/login.vue +++ b/next-ui/src/pages/login.vue @@ -4,9 +4,9 @@ :disabled="isLoading" @submit.prevent="submitForm()" > - + - + @@ -41,12 +41,17 @@ " type="password" :rules="[rules.required()]" + :error-messages="loginError" + @update:modelValue="loginError = ''" /> - - + + + + {{ + $formatMessage({ + description: 'Login screen: Forgot your password link', + defaultMessage: 'Forgot your password?', + id: 'r6JNfI', + }) + }} + + @@ -73,28 +94,19 @@ " :loading="isLoading" type="submit" + block /> - - - - + + + + +
+ + +
@@ -105,15 +117,19 @@ import { type ErrorCause, komgaClient } from '@/api/komga-client' import { useMutation, useQueryCache } from '@pinia/colada' import { useRules } from 'vuetify/labs/rules' +import { useMessagesStore } from '@/stores/messages' +import { useIntl } from 'vue-intl' +import { commonMessages } from '@/utils/i18n/common-messages' const rules = useRules() +const messagesStore = useMessagesStore() +const intl = useIntl() const formValid = ref(false) const username = ref('') const password = ref('') const rememberMe = ref(false) -const showError = ref(false) -const showErrorText = ref('') +const loginError = ref('') const router = useRouter() const route = useRoute() @@ -139,8 +155,17 @@ const { mutate: performLogin, isLoading } = useMutation({ else void router.push('/') }, onError: (error) => { - showErrorText.value = (error.cause as ErrorCause).message || 'Invalid authentication' - showError.value = true + if ((error.cause as ErrorCause).status === 401) + loginError.value = intl.formatMessage({ + description: 'Login screen: error message displayed when login failed', + defaultMessage: 'Invalid login or password', + id: 'AjWlka', + }) + else + messagesStore.messages.push({ + text: + (error.cause as ErrorCause).message || intl.formatMessage(commonMessages.networkError), + }) }, }) diff --git a/next-ui/src/stores/messages.ts b/next-ui/src/stores/messages.ts new file mode 100644 index 000000000..69ca869f5 --- /dev/null +++ b/next-ui/src/stores/messages.ts @@ -0,0 +1,8 @@ +// Utilities +import { defineStore } from 'pinia' + +export const useMessagesStore = defineStore('messages', { + state: () => ({ + messages: [] as object[], + }), +}) diff --git a/next-ui/src/utils/i18n/common-messages.ts b/next-ui/src/utils/i18n/common-messages.ts index 29eb789db..2698daf6a 100644 --- a/next-ui/src/utils/i18n/common-messages.ts +++ b/next-ui/src/utils/i18n/common-messages.ts @@ -11,4 +11,9 @@ export const commonMessages = { defaultMessage: 'There might be a problem with your connection or your server.', id: 'hYO2n6', }), + networkError: defineMessage({ + description: 'Common message: a network error happened when communicating with the server', + defaultMessage: 'Network error', + id: 'Z/EY89', + }), }