diff --git a/frontend/src/components/misc/NoAuthWrapper.vue b/frontend/src/components/misc/NoAuthWrapper.vue index 1698f0f22..5db7cc19c 100644 --- a/frontend/src/components/misc/NoAuthWrapper.vue +++ b/frontend/src/components/misc/NoAuthWrapper.vue @@ -25,7 +25,7 @@ > {{ title }} - + (), @@ -61,6 +62,11 @@ withDefaults( showApiConfig: false, }, ) + +const isDesktop = isDesktopApp() +const hasStoredApiUrl = isDesktop && localStorage.getItem('API_URL') !== null +const shouldShowApiConfig = computed(() => props.showApiConfig && (!isDesktop || hasStoredApiUrl)) + const configStore = useConfigStore() const motd = computed(() => configStore.motd) diff --git a/frontend/src/i18n/lang/en.json b/frontend/src/i18n/lang/en.json index 4bd8c723e..3dc16ad5d 100644 --- a/frontend/src/i18n/lang/en.json +++ b/frontend/src/i18n/lang/en.json @@ -55,6 +55,12 @@ "openIdStateError": "State does not match, refusing to continue!", "openIdGeneralError": "An error occurred while authenticating against the third party.", "oauthMissingParams": "Missing required OAuth parameters: {params}", + "oauthRedirectedToApp": "You have been redirected to the app. You can close this tab now.", + "desktopTryDemo": "Try the Demo", + "desktopCustomServer": "Custom Server URL", + "desktopCustomServerDescription": "Enter the URL of your Vikunja server to get started.", + "desktopWaitingForAuth": "Waiting for authentication…", + "desktopOAuthError": "Authentication failed: {error}", "logout": "Logout", "emailInvalid": "Please enter a valid email address.", "usernameRequired": "Please provide a username.", @@ -156,8 +162,7 @@ "tokenCreated": "Here is your new token: {token}", "wontSeeItAgain": "Write it down or save it securely — you will not be able to see it again.", "mustUseToken": "You need to create a CalDAV token to use CalDAV with any third-party client. Enter the token in the password field of your client.", - "usernameIs": "Your username for CalDAV is: {0}", - "apiTokenHint": "You can also use an API token with CalDAV permission. Create one in {link}." + "usernameIs": "Your username for CalDAV is: {0}" }, "avatar": { "title": "Avatar", diff --git a/frontend/src/stores/base.ts b/frontend/src/stores/base.ts index d90ec8449..115950ce5 100644 --- a/frontend/src/stores/base.ts +++ b/frontend/src/stores/base.ts @@ -7,6 +7,7 @@ import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash' import ProjectModel from '@/models/project' import ProjectService from '@/services/project' import {checkAndSetApiUrl, ERROR_NO_API_URL, InvalidApiUrlProvidedError, NoApiUrlProvidedError} from '@/helpers/checkAndSetApiUrl' +import {isDesktopApp} from '@/helpers/desktopAuth' import {useMenuActive} from '@/composables/useMenuActive' @@ -146,6 +147,19 @@ export const useBaseStore = defineStore('base', () => { async function loadApp() { try { + if (isDesktopApp()) { + // On desktop, ignore the default window.API_URL (set by index.html) + // and only use a previously stored API URL from localStorage. + const storedApiUrl = localStorage.getItem('API_URL') + if (storedApiUrl) { + window.API_URL = storedApiUrl + await authStore.checkAuth() + } + await router.isReady() + ready.value = true + return + } + await checkAndSetApiUrl(window.API_URL) await authStore.checkAuth() await router.isReady() diff --git a/frontend/src/views/user/DesktopLogin.vue b/frontend/src/views/user/DesktopLogin.vue new file mode 100644 index 000000000..de20efc1e --- /dev/null +++ b/frontend/src/views/user/DesktopLogin.vue @@ -0,0 +1,119 @@ + + + diff --git a/frontend/src/views/user/Login.vue b/frontend/src/views/user/Login.vue index 29ee5c90f..503dd46e5 100644 --- a/frontend/src/views/user/Login.vue +++ b/frontend/src/views/user/Login.vue @@ -15,8 +15,11 @@ > {{ errorMessage }} + + +
@@ -106,7 +109,7 @@
configStore.auth.openidConnect) const hasOpenIdProviders = computed(() => openidConnect.value.enabled && openidConnect.value.providers?.length > 0) const isLoading = computed(() => authStore.isLoading) +const isDesktop = isDesktopApp() const confirmedEmailSuccess = ref(false) const errorMessage = ref('') @@ -189,6 +195,7 @@ const validateUsernameField = useDebounceFn(() => { usernameValid.value = usernameRef.value?.value !== '' }, 100) + const needsTotpPasscode = computed(() => authStore.needsTotpPasscode) const totpPasscode = ref(null)