@@ -15,7 +19,24 @@
-
+
+
+
+
diff --git a/components/user/UserSignIn.vue b/components/user/UserSignIn.vue
index eccf641c..97621bc2 100644
--- a/components/user/UserSignIn.vue
+++ b/components/user/UserSignIn.vue
@@ -1,66 +1,21 @@
diff --git a/components/user/UserSignInEntry.vue b/components/user/UserSignInEntry.vue
index d6274495..0d78abc2 100644
--- a/components/user/UserSignInEntry.vue
+++ b/components/user/UserSignInEntry.vue
@@ -1,3 +1,7 @@
+
+
@@ -8,7 +12,19 @@
{{ $t('user.sign_in_desc') }}
-
diff --git a/components/user/UserSwitcher.vue b/components/user/UserSwitcher.vue
index e8a766f1..d082abb1 100644
--- a/components/user/UserSwitcher.vue
+++ b/components/user/UserSwitcher.vue
@@ -6,6 +6,7 @@ const emit = defineEmits<{
}>()
const all = useUsers()
+const { busy, singleInstanceServer, oauth } = useSignIn()
const sorted = computed(() => {
return [
@@ -21,6 +22,12 @@ const clickUser = (user: UserLogin) => {
else
switchUser(user)
}
+const processSignIn = () => {
+ if (singleInstanceServer)
+ oauth()
+ else
+ openSigninDialog()
+}
@@ -43,7 +50,7 @@ const clickUser = (user: UserLogin) => {
:text="$t('user.add_existing')"
icon="i-ri:user-add-line"
w-full
- @click="openSigninDialog"
+ @click="processSignIn"
/>
{
const server = currentServer.value
const userId = currentUser.value?.account.id
- const key = `${server}:${userId}:account:${acct}`
+ const userAcct = acct.endsWith(`@${server}`) ? acct.slice(0, -server.length - 1) : acct
+ const key = `${server}:${userId}:account:${userAcct}`
const cached = cache.get(key)
if (cached)
return cached
@@ -69,9 +70,9 @@ export async function fetchAccountByHandle(acct: string): Promise {
const masto = useMasto()
const colorMode = useColorMode()
const userSettings = useUserSettings()
+ const { singleInstanceServer, oauth } = useSignIn()
useCommand({
scope: 'Navigation',
@@ -310,7 +311,10 @@ export const provideGlobalCommands = () => {
icon: 'i-ri:user-add-line',
onActivate() {
- openSigninDialog()
+ if (singleInstanceServer)
+ oauth()
+ else
+ openSigninDialog()
},
})
useCommand({
diff --git a/composables/dialog.ts b/composables/dialog.ts
index ac8f1018..9835d9be 100644
--- a/composables/dialog.ts
+++ b/composables/dialog.ts
@@ -1,9 +1,10 @@
import type { mastodon } from 'masto'
-import type { ConfirmDialogChoice, ConfirmDialogLabel, Draft } from '~/types'
+import type { ConfirmDialogChoice, ConfirmDialogLabel, Draft, ErrorDialogData } from '~/types'
import { STORAGE_KEY_FIRST_VISIT } from '~/constants'
export const confirmDialogChoice = ref()
export const confirmDialogLabel = ref()
+export const errorDialogData = ref()
export const mediaPreviewList = ref([])
export const mediaPreviewIndex = ref(0)
@@ -22,6 +23,7 @@ export const isEditHistoryDialogOpen = ref(false)
export const isPreviewHelpOpen = ref(isFirstVisit.value)
export const isCommandPanelOpen = ref(false)
export const isConfirmDialogOpen = ref(false)
+export const isErrorDialogOpen = ref(false)
export const isFavouritedBoostedByDialogOpen = ref(false)
export const lastPublishDialogStatus = ref(null)
@@ -101,6 +103,17 @@ export function openMediaPreview(attachments: mastodon.v1.MediaAttachment[], ind
}, '')
}
+export async function openErrorDialog(data: ErrorDialogData) {
+ errorDialogData.value = data
+ isErrorDialogOpen.value = true
+
+ await until(isErrorDialogOpen).toBe(false)
+}
+
+export function closeErrorDialog() {
+ isErrorDialogOpen.value = false
+}
+
export function closeMediaPreview() {
history.back()
}
diff --git a/composables/sign-in.ts b/composables/sign-in.ts
new file mode 100644
index 00000000..139d792f
--- /dev/null
+++ b/composables/sign-in.ts
@@ -0,0 +1,77 @@
+import type { Ref } from 'vue'
+
+export const useSignIn = (input?: Ref) => {
+ const singleInstanceServer = useAppConfig().singleInstanceServer
+ const userSettings = useUserSettings()
+ const users = useUsers()
+ const { t } = useI18n()
+
+ const busy = ref(false)
+ const error = ref(false)
+ const server = ref('')
+ const displayError = ref(false)
+
+ async function oauth() {
+ if (busy.value)
+ return
+
+ busy.value = true
+ error.value = false
+ displayError.value = false
+
+ await nextTick()
+
+ if (!singleInstanceServer && server.value)
+ server.value = server.value.split('/')[0]
+
+ try {
+ let href: string
+ if (singleInstanceServer) {
+ href = await (globalThis.$fetch as any)(`/api/${publicServer.value}/login`, {
+ method: 'POST',
+ body: {
+ force_login: users.value.length > 0,
+ origin: location.origin,
+ lang: userSettings.value.language,
+ },
+ })
+ busy.value = false
+ }
+ else {
+ href = await (globalThis.$fetch as any)(`/api/${server.value || publicServer.value}/login`, {
+ method: 'POST',
+ body: {
+ force_login: users.value.some(u => u.server === server.value),
+ origin: location.origin,
+ lang: userSettings.value.language,
+ },
+ })
+ }
+ location.href = href
+ }
+ catch (err) {
+ if (singleInstanceServer) {
+ console.error(err)
+ busy.value = false
+ await openErrorDialog({
+ title: t('common.error'),
+ messages: [t('error.sign_in_error')],
+ close: t('action.close'),
+ })
+ }
+ else {
+ displayError.value = true
+ error.value = true
+ await nextTick()
+ input?.value?.focus()
+ await nextTick()
+ setTimeout(() => {
+ busy.value = false
+ error.value = false
+ }, 512)
+ }
+ }
+ }
+
+ return { busy, displayError, error, server, singleInstanceServer, oauth }
+}
diff --git a/composables/users.ts b/composables/users.ts
index 062fa401..ab69cf5f 100644
--- a/composables/users.ts
+++ b/composables/users.ts
@@ -263,7 +263,7 @@ export async function signOut() {
if (!currentUserHandle.value)
await useRouter().push('/')
- loginTo(masto, currentUser.value)
+ loginTo(masto, currentUser.value || { server: publicServer.value })
}
export function checkLogin() {
diff --git a/locales/en.json b/locales/en.json
index 20714989..823339f8 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -69,6 +69,7 @@
"save": "Save",
"save_changes": "Save changes",
"sign_in": "Sign in",
+ "sign_in_to": "Sign in to {0}",
"switch_account": "Switch account",
"vote": "Vote"
},
diff --git a/locales/es.json b/locales/es.json
index 50cf46bb..648afb62 100644
--- a/locales/es.json
+++ b/locales/es.json
@@ -68,6 +68,7 @@
"save": "Guardar",
"save_changes": "Guardar cambios",
"sign_in": "Iniciar sesión",
+ "sign_in_to": "Iniciar sesión en {0}",
"switch_account": "Cambiar cuenta",
"vote": "Votar"
},
diff --git a/middleware/permalink.global.ts b/middleware/1.permalink.global.ts
similarity index 81%
rename from middleware/permalink.global.ts
rename to middleware/1.permalink.global.ts
index 6aa555cf..64c789fa 100644
--- a/middleware/permalink.global.ts
+++ b/middleware/1.permalink.global.ts
@@ -5,16 +5,18 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
if (!('server' in to.params))
return
+ const server = to.params.server as string || currentServer.value
const user = currentUser.value
const masto = useMasto()
if (!user) {
- if (from.params.server !== to.params.server)
- loginTo(masto, { server: to.params.server as string })
+ const fromServer = from.params.server || currentServer.value
+ if (fromServer !== server)
+ loginTo(masto, { server })
return
}
// No need to additionally resolve an id if we're already logged in
- if (user.server === to.params.server)
+ if (user.server === server)
return
// Tags don't need to be redirected to a local id
@@ -22,7 +24,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
return
// Handle redirecting to new permalink structure for users with old links
- if (!to.params.server) {
+ if (!useAppConfig().singleInstanceServer && !to.params.server) {
return {
...to,
params: {
diff --git a/middleware/2.single-instance.global.ts b/middleware/2.single-instance.global.ts
new file mode 100644
index 00000000..4b12cddd
--- /dev/null
+++ b/middleware/2.single-instance.global.ts
@@ -0,0 +1,10 @@
+export default defineNuxtRouteMiddleware(async (to) => {
+ if (process.server || !useAppConfig().singleInstanceServer)
+ return
+
+ if (to.params.server) {
+ const newTo = { ...to }
+ delete newTo.params.server
+ return newTo
+ }
+})
diff --git a/middleware/auth.ts b/middleware/auth.ts
index 05bddb0e..40eafd80 100644
--- a/middleware/auth.ts
+++ b/middleware/auth.ts
@@ -1,6 +1,7 @@
export default defineNuxtRouteMiddleware((to) => {
if (process.server)
return
+
if (to.path === '/signin/callback')
return
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 79211532..369631a9 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -96,6 +96,7 @@ export default defineNuxtConfig({
},
},
appConfig: {
+ singleInstanceServer: process.env.SINGLE_INSTANCE_SERVER === 'true',
storage: {
driver: process.env.NUXT_STORAGE_DRIVER ?? (isCI ? 'cloudflare' : 'fs'),
},
diff --git a/pages/[[server]]/@[account]/index.vue b/pages/[[server]]/@[account]/index.vue
index faba9dca..bce15883 100644
--- a/pages/[[server]]/@[account]/index.vue
+++ b/pages/[[server]]/@[account]/index.vue
@@ -1,6 +1,6 @@