feat: allow running elk with a single server (#1606)
This commit is contained in:
parent
61428cd9cd
commit
53d0812efd
|
@ -5,6 +5,7 @@ import {
|
||||||
isCommandPanelOpen,
|
isCommandPanelOpen,
|
||||||
isConfirmDialogOpen,
|
isConfirmDialogOpen,
|
||||||
isEditHistoryDialogOpen,
|
isEditHistoryDialogOpen,
|
||||||
|
isErrorDialogOpen,
|
||||||
isFavouritedBoostedByDialogOpen,
|
isFavouritedBoostedByDialogOpen,
|
||||||
isMediaPreviewOpen,
|
isMediaPreviewOpen,
|
||||||
isPreviewHelpOpen,
|
isPreviewHelpOpen,
|
||||||
|
@ -87,6 +88,9 @@ const handleFavouritedBoostedByClose = () => {
|
||||||
<ModalDialog v-model="isConfirmDialogOpen" py-4 px-8 max-w-125>
|
<ModalDialog v-model="isConfirmDialogOpen" py-4 px-8 max-w-125>
|
||||||
<ModalConfirm v-if="confirmDialogLabel" v-bind="confirmDialogLabel" @choice="handleConfirmChoice" />
|
<ModalConfirm v-if="confirmDialogLabel" v-bind="confirmDialogLabel" @choice="handleConfirmChoice" />
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
|
<ModalDialog v-model="isErrorDialogOpen" py-4 px-8 max-w-125>
|
||||||
|
<ModalError v-if="errorDialogData" v-bind="errorDialogData" />
|
||||||
|
</ModalDialog>
|
||||||
<ModalDialog
|
<ModalDialog
|
||||||
v-model="isFavouritedBoostedByDialogOpen"
|
v-model="isFavouritedBoostedByDialogOpen"
|
||||||
max-w-180
|
max-w-180
|
||||||
|
|
31
components/modal/ModalError.vue
Normal file
31
components/modal/ModalError.vue
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ErrorDialogData } from '~/types'
|
||||||
|
|
||||||
|
defineProps<ErrorDialogData>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div flex="~ col" gap-6>
|
||||||
|
<div font-bold text-lg text-center>
|
||||||
|
{{ title }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
flex="~ col"
|
||||||
|
gap-1 text-sm
|
||||||
|
pt-1 ps-2 pe-1 pb-2
|
||||||
|
text-red-600 dark:text-red-400
|
||||||
|
border="~ base rounded red-600 dark:red-400"
|
||||||
|
>
|
||||||
|
<ol ps-2 sm:ps-1>
|
||||||
|
<li v-for="(message, i) in messages" :key="i" flex="~ col sm:row" gap-y-1 sm:gap-x-2>
|
||||||
|
{{ message }}
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<div flex justify-end gap-2>
|
||||||
|
<button btn-text @click="closeErrorDialog()">
|
||||||
|
{{ close }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -1,3 +1,7 @@
|
||||||
|
<script setup>
|
||||||
|
const { busy, oauth, singleInstanceServer } = useSignIn()
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDropdown v-if="isHydrated && currentUser" sm:hidden>
|
<VDropdown v-if="isHydrated && currentUser" sm:hidden>
|
||||||
<div style="-webkit-touch-callout: none;">
|
<div style="-webkit-touch-callout: none;">
|
||||||
|
@ -15,7 +19,24 @@
|
||||||
<UserSwitcher ref="switcher" @click="hide()" />
|
<UserSwitcher ref="switcher" @click="hide()" />
|
||||||
</template>
|
</template>
|
||||||
</VDropdown>
|
</VDropdown>
|
||||||
<button v-else btn-solid text-sm px-2 py-1 text-center xl:hidden @click="openSigninDialog()">
|
<template v-else>
|
||||||
{{ $t('action.sign_in') }}
|
<button
|
||||||
</button>
|
v-if="singleInstanceServer"
|
||||||
|
flex="~ row"
|
||||||
|
gap-x-1 items-center justify-center btn-solid text-sm px-2 py-1 xl:hidden
|
||||||
|
:disabled="busy"
|
||||||
|
@click="oauth()"
|
||||||
|
>
|
||||||
|
<span v-if="busy" aria-hidden="true" block animate animate-spin preserve-3d class="rtl-flip">
|
||||||
|
<span block i-ri:loader-2-fill aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
<span v-else aria-hidden="true" block i-ri:login-circle-line class="rtl-flip" />
|
||||||
|
<i18n-t keypath="action.sign_in_to">
|
||||||
|
<strong>{{ currentServer }}</strong>
|
||||||
|
</i18n-t>
|
||||||
|
</button>
|
||||||
|
<button v-else btn-solid text-sm px-2 py-1 text-center xl:hidden @click="openSigninDialog()">
|
||||||
|
{{ $t('action.sign_in') }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,66 +1,21 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
import { $fetch } from 'ofetch'
|
|
||||||
|
|
||||||
const input = $ref<HTMLInputElement>()
|
const input = ref<HTMLInputElement | undefined>()
|
||||||
let server = $ref<string>('')
|
|
||||||
let busy = $ref<boolean>(false)
|
|
||||||
let error = $ref<boolean>(false)
|
|
||||||
let displayError = $ref<boolean>(false)
|
|
||||||
let knownServers = $ref<string[]>([])
|
let knownServers = $ref<string[]>([])
|
||||||
let autocompleteIndex = $ref(0)
|
let autocompleteIndex = $ref(0)
|
||||||
let autocompleteShow = $ref(false)
|
let autocompleteShow = $ref(false)
|
||||||
|
|
||||||
const users = useUsers()
|
const { busy, error, displayError, server, oauth } = useSignIn(input)
|
||||||
const userSettings = useUserSettings()
|
|
||||||
|
|
||||||
async function oauth() {
|
|
||||||
if (busy)
|
|
||||||
return
|
|
||||||
|
|
||||||
busy = true
|
|
||||||
error = false
|
|
||||||
displayError = false
|
|
||||||
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
if (server)
|
|
||||||
server = server.split('/')[0]
|
|
||||||
|
|
||||||
try {
|
|
||||||
const url = await (globalThis.$fetch as any)(`/api/${server || publicServer.value}/login`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: {
|
|
||||||
force_login: users.value.some(u => u.server === server),
|
|
||||||
origin: location.origin,
|
|
||||||
lang: userSettings.value.language,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
location.href = url
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
|
|
||||||
displayError = true
|
|
||||||
error = true
|
|
||||||
await nextTick()
|
|
||||||
input?.focus()
|
|
||||||
await nextTick()
|
|
||||||
setTimeout(() => {
|
|
||||||
busy = false
|
|
||||||
error = false
|
|
||||||
}, 512)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let fuse = $shallowRef(new Fuse([] as string[]))
|
let fuse = $shallowRef(new Fuse([] as string[]))
|
||||||
|
|
||||||
const filteredServers = $computed(() => {
|
const filteredServers = $computed(() => {
|
||||||
if (!server)
|
if (!server.value)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
const results = fuse.search(server, { limit: 6 }).map(result => result.item)
|
const results = fuse.search(server.value, { limit: 6 }).map(result => result.item)
|
||||||
if (results[0] === server)
|
if (results[0] === server.value)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
@ -78,12 +33,12 @@ function isValidUrl(str: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleInput() {
|
async function handleInput() {
|
||||||
const input = server.trim()
|
const input = server.value.trim()
|
||||||
if (input.startsWith('https://'))
|
if (input.startsWith('https://'))
|
||||||
server = input.replace('https://', '')
|
server.value = input.replace('https://', '')
|
||||||
|
|
||||||
if (input.length)
|
if (input.length)
|
||||||
displayError = false
|
displayError.value = false
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isValidUrl(`https://${input}`)
|
isValidUrl(`https://${input}`)
|
||||||
|
@ -110,7 +65,7 @@ function move(delta: number) {
|
||||||
|
|
||||||
function onEnter(e: KeyboardEvent) {
|
function onEnter(e: KeyboardEvent) {
|
||||||
if (autocompleteShow === true && filteredServers[autocompleteIndex]) {
|
if (autocompleteShow === true && filteredServers[autocompleteIndex]) {
|
||||||
server = filteredServers[autocompleteIndex]
|
server.value = filteredServers[autocompleteIndex]
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
autocompleteShow = false
|
autocompleteShow = false
|
||||||
}
|
}
|
||||||
|
@ -124,16 +79,16 @@ function escapeAutocomplete(evt: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function select(index: number) {
|
function select(index: number) {
|
||||||
server = filteredServers[index]
|
server.value = filteredServers[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
input?.focus()
|
input?.value?.focus()
|
||||||
knownServers = await (globalThis.$fetch as any)('/api/list-servers')
|
knownServers = await (globalThis.$fetch as any)('/api/list-servers')
|
||||||
fuse = new Fuse(knownServers, { shouldSort: true })
|
fuse = new Fuse(knownServers, { shouldSort: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
onClickOutside($$(input), () => {
|
onClickOutside(input, () => {
|
||||||
autocompleteShow = false
|
autocompleteShow = false
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { busy, oauth, singleInstanceServer } = useSignIn()
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div p8 lg:flex="~ col gap2" hidden>
|
<div p8 lg:flex="~ col gap2" hidden>
|
||||||
<p v-if="isHydrated" text-sm>
|
<p v-if="isHydrated" text-sm>
|
||||||
|
@ -8,7 +12,19 @@
|
||||||
<p text-sm text-secondary>
|
<p text-sm text-secondary>
|
||||||
{{ $t('user.sign_in_desc') }}
|
{{ $t('user.sign_in_desc') }}
|
||||||
</p>
|
</p>
|
||||||
<button btn-solid rounded-3 text-center mt-2 select-none @click="openSigninDialog()">
|
<button
|
||||||
|
v-if="singleInstanceServer"
|
||||||
|
flex="~ row" gap-x-2 items-center justify-center btn-solid text-center rounded-3
|
||||||
|
:disabled="busy"
|
||||||
|
@click="oauth()"
|
||||||
|
>
|
||||||
|
<span v-if="busy" aria-hidden="true" block animate animate-spin preserve-3d class="rtl-flip">
|
||||||
|
<span block i-ri:loader-2-fill aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
<span v-else aria-hidden="true" block i-ri:login-circle-line class="rtl-flip" />
|
||||||
|
{{ $t('action.sign_in') }}
|
||||||
|
</button>
|
||||||
|
<button v-else btn-solid rounded-3 text-center mt-2 select-none @click="openSigninDialog()">
|
||||||
{{ $t('action.sign_in') }}
|
{{ $t('action.sign_in') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,6 +6,7 @@ const emit = defineEmits<{
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const all = useUsers()
|
const all = useUsers()
|
||||||
|
const { busy, singleInstanceServer, oauth } = useSignIn()
|
||||||
|
|
||||||
const sorted = computed(() => {
|
const sorted = computed(() => {
|
||||||
return [
|
return [
|
||||||
|
@ -21,6 +22,12 @@ const clickUser = (user: UserLogin) => {
|
||||||
else
|
else
|
||||||
switchUser(user)
|
switchUser(user)
|
||||||
}
|
}
|
||||||
|
const processSignIn = () => {
|
||||||
|
if (singleInstanceServer)
|
||||||
|
oauth()
|
||||||
|
else
|
||||||
|
openSigninDialog()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -43,7 +50,7 @@ const clickUser = (user: UserLogin) => {
|
||||||
:text="$t('user.add_existing')"
|
:text="$t('user.add_existing')"
|
||||||
icon="i-ri:user-add-line"
|
icon="i-ri:user-add-line"
|
||||||
w-full
|
w-full
|
||||||
@click="openSigninDialog"
|
@click="processSignIn"
|
||||||
/>
|
/>
|
||||||
<CommonDropdownItem
|
<CommonDropdownItem
|
||||||
is="button"
|
is="button"
|
||||||
|
|
|
@ -59,7 +59,8 @@ export function fetchAccountById(id?: string | null): Promise<mastodon.v1.Accoun
|
||||||
export async function fetchAccountByHandle(acct: string): Promise<mastodon.v1.Account> {
|
export async function fetchAccountByHandle(acct: string): Promise<mastodon.v1.Account> {
|
||||||
const server = currentServer.value
|
const server = currentServer.value
|
||||||
const userId = currentUser.value?.account.id
|
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)
|
const cached = cache.get(key)
|
||||||
if (cached)
|
if (cached)
|
||||||
return cached
|
return cached
|
||||||
|
@ -69,9 +70,9 @@ export async function fetchAccountByHandle(acct: string): Promise<mastodon.v1.Ac
|
||||||
const client = useMastoClient()
|
const client = useMastoClient()
|
||||||
let account: mastodon.v1.Account
|
let account: mastodon.v1.Account
|
||||||
if (!isGotoSocial.value)
|
if (!isGotoSocial.value)
|
||||||
account = await client.v1.accounts.lookup({ acct })
|
account = await client.v1.accounts.lookup({ acct: userAcct })
|
||||||
else
|
else
|
||||||
account = (await client.v1.search({ q: `@${acct}`, type: 'accounts' })).accounts[0]
|
account = (await client.v1.search({ q: `@${userAcct}`, type: 'accounts' })).accounts[0]
|
||||||
|
|
||||||
if (account.acct && !account.acct.includes('@') && domain)
|
if (account.acct && !account.acct.includes('@') && domain)
|
||||||
account.acct = `${account.acct}@${domain}`
|
account.acct = `${account.acct}@${domain}`
|
||||||
|
@ -107,6 +108,7 @@ export function removeCachedStatus(id: string, server = currentServer.value) {
|
||||||
|
|
||||||
export function cacheAccount(account: mastodon.v1.Account, server = currentServer.value, override?: boolean) {
|
export function cacheAccount(account: mastodon.v1.Account, server = currentServer.value, override?: boolean) {
|
||||||
const userId = currentUser.value?.account.id
|
const userId = currentUser.value?.account.id
|
||||||
|
const userAcct = account.acct.endsWith(`@${server}`) ? account.acct.slice(0, -server.length - 1) : account.acct
|
||||||
setCached(`${server}:${userId}:account:${account.id}`, account, override)
|
setCached(`${server}:${userId}:account:${account.id}`, account, override)
|
||||||
setCached(`${server}:${userId}:account:${account.acct}`, account, override)
|
setCached(`${server}:${userId}:account:${userAcct}`, account, override)
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,6 +245,7 @@ export const provideGlobalCommands = () => {
|
||||||
const masto = useMasto()
|
const masto = useMasto()
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
const userSettings = useUserSettings()
|
const userSettings = useUserSettings()
|
||||||
|
const { singleInstanceServer, oauth } = useSignIn()
|
||||||
|
|
||||||
useCommand({
|
useCommand({
|
||||||
scope: 'Navigation',
|
scope: 'Navigation',
|
||||||
|
@ -310,7 +311,10 @@ export const provideGlobalCommands = () => {
|
||||||
icon: 'i-ri:user-add-line',
|
icon: 'i-ri:user-add-line',
|
||||||
|
|
||||||
onActivate() {
|
onActivate() {
|
||||||
openSigninDialog()
|
if (singleInstanceServer)
|
||||||
|
oauth()
|
||||||
|
else
|
||||||
|
openSigninDialog()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
useCommand({
|
useCommand({
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import type { mastodon } from 'masto'
|
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'
|
import { STORAGE_KEY_FIRST_VISIT } from '~/constants'
|
||||||
|
|
||||||
export const confirmDialogChoice = ref<ConfirmDialogChoice>()
|
export const confirmDialogChoice = ref<ConfirmDialogChoice>()
|
||||||
export const confirmDialogLabel = ref<ConfirmDialogLabel>()
|
export const confirmDialogLabel = ref<ConfirmDialogLabel>()
|
||||||
|
export const errorDialogData = ref<ErrorDialogData>()
|
||||||
|
|
||||||
export const mediaPreviewList = ref<mastodon.v1.MediaAttachment[]>([])
|
export const mediaPreviewList = ref<mastodon.v1.MediaAttachment[]>([])
|
||||||
export const mediaPreviewIndex = ref(0)
|
export const mediaPreviewIndex = ref(0)
|
||||||
|
@ -22,6 +23,7 @@ export const isEditHistoryDialogOpen = ref(false)
|
||||||
export const isPreviewHelpOpen = ref(isFirstVisit.value)
|
export const isPreviewHelpOpen = ref(isFirstVisit.value)
|
||||||
export const isCommandPanelOpen = ref(false)
|
export const isCommandPanelOpen = ref(false)
|
||||||
export const isConfirmDialogOpen = ref(false)
|
export const isConfirmDialogOpen = ref(false)
|
||||||
|
export const isErrorDialogOpen = ref(false)
|
||||||
export const isFavouritedBoostedByDialogOpen = ref(false)
|
export const isFavouritedBoostedByDialogOpen = ref(false)
|
||||||
|
|
||||||
export const lastPublishDialogStatus = ref<mastodon.v1.Status | null>(null)
|
export const lastPublishDialogStatus = ref<mastodon.v1.Status | null>(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() {
|
export function closeMediaPreview() {
|
||||||
history.back()
|
history.back()
|
||||||
}
|
}
|
||||||
|
|
77
composables/sign-in.ts
Normal file
77
composables/sign-in.ts
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
|
||||||
|
export const useSignIn = (input?: Ref<HTMLInputElement | undefined>) => {
|
||||||
|
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 }
|
||||||
|
}
|
|
@ -263,7 +263,7 @@ export async function signOut() {
|
||||||
if (!currentUserHandle.value)
|
if (!currentUserHandle.value)
|
||||||
await useRouter().push('/')
|
await useRouter().push('/')
|
||||||
|
|
||||||
loginTo(masto, currentUser.value)
|
loginTo(masto, currentUser.value || { server: publicServer.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkLogin() {
|
export function checkLogin() {
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"save_changes": "Save changes",
|
"save_changes": "Save changes",
|
||||||
"sign_in": "Sign in",
|
"sign_in": "Sign in",
|
||||||
|
"sign_in_to": "Sign in to {0}",
|
||||||
"switch_account": "Switch account",
|
"switch_account": "Switch account",
|
||||||
"vote": "Vote"
|
"vote": "Vote"
|
||||||
},
|
},
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"save_changes": "Guardar cambios",
|
"save_changes": "Guardar cambios",
|
||||||
"sign_in": "Iniciar sesión",
|
"sign_in": "Iniciar sesión",
|
||||||
|
"sign_in_to": "Iniciar sesión en {0}",
|
||||||
"switch_account": "Cambiar cuenta",
|
"switch_account": "Cambiar cuenta",
|
||||||
"vote": "Votar"
|
"vote": "Votar"
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,16 +5,18 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||||
if (!('server' in to.params))
|
if (!('server' in to.params))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
const server = to.params.server as string || currentServer.value
|
||||||
const user = currentUser.value
|
const user = currentUser.value
|
||||||
const masto = useMasto()
|
const masto = useMasto()
|
||||||
if (!user) {
|
if (!user) {
|
||||||
if (from.params.server !== to.params.server)
|
const fromServer = from.params.server || currentServer.value
|
||||||
loginTo(masto, { server: to.params.server as string })
|
if (fromServer !== server)
|
||||||
|
loginTo(masto, { server })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to additionally resolve an id if we're already logged in
|
// No need to additionally resolve an id if we're already logged in
|
||||||
if (user.server === to.params.server)
|
if (user.server === server)
|
||||||
return
|
return
|
||||||
|
|
||||||
// Tags don't need to be redirected to a local id
|
// Tags don't need to be redirected to a local id
|
||||||
|
@ -22,7 +24,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||||
return
|
return
|
||||||
|
|
||||||
// Handle redirecting to new permalink structure for users with old links
|
// Handle redirecting to new permalink structure for users with old links
|
||||||
if (!to.params.server) {
|
if (!useAppConfig().singleInstanceServer && !to.params.server) {
|
||||||
return {
|
return {
|
||||||
...to,
|
...to,
|
||||||
params: {
|
params: {
|
10
middleware/2.single-instance.global.ts
Normal file
10
middleware/2.single-instance.global.ts
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
})
|
|
@ -1,6 +1,7 @@
|
||||||
export default defineNuxtRouteMiddleware((to) => {
|
export default defineNuxtRouteMiddleware((to) => {
|
||||||
if (process.server)
|
if (process.server)
|
||||||
return
|
return
|
||||||
|
|
||||||
if (to.path === '/signin/callback')
|
if (to.path === '/signin/callback')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,7 @@ export default defineNuxtConfig({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appConfig: {
|
appConfig: {
|
||||||
|
singleInstanceServer: process.env.SINGLE_INSTANCE_SERVER === 'true',
|
||||||
storage: {
|
storage: {
|
||||||
driver: process.env.NUXT_STORAGE_DRIVER ?? (isCI ? 'cloudflare' : 'fs'),
|
driver: process.env.NUXT_STORAGE_DRIVER ?? (isCI ? 'cloudflare' : 'fs'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
key: route => `${route.params.server}:${route.params.account}`,
|
key: route => `${route.params.server ?? currentServer.value}:${route.params.account}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
const params = useRoute().params
|
const params = useRoute().params
|
||||||
|
|
|
@ -5,11 +5,11 @@ definePageMeta({
|
||||||
middleware: 'auth',
|
middleware: 'auth',
|
||||||
})
|
})
|
||||||
|
|
||||||
const list = $computed(() => useRoute().params.list as string)
|
|
||||||
const server = $computed(() => useRoute().params.server as string)
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const list = $computed(() => route.params.list as string)
|
||||||
|
const server = $computed(() => (route.params.server ?? currentServer.value) as string)
|
||||||
|
|
||||||
const tabs = $computed<CommonRouteTabOption[]>(() => [
|
const tabs = $computed<CommonRouteTabOption[]>(() => [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin(() => {
|
||||||
const { params, query } = useRoute()
|
const { params, query } = useRoute()
|
||||||
|
|
||||||
publicServer.value = params.server as string || useRuntimeConfig().public.defaultServer
|
publicServer.value = params.server as string || useRuntimeConfig().public.defaultServer
|
||||||
|
|
||||||
const masto = createMasto()
|
const masto = createMasto()
|
||||||
|
|
|
@ -4,7 +4,7 @@ const BOT_RE = /bot\b|index|spider|facebookexternalhit|crawl|wget|slurp|mediapar
|
||||||
|
|
||||||
export default defineNuxtPlugin(async (nuxtApp) => {
|
export default defineNuxtPlugin(async (nuxtApp) => {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
if (!route.params.server)
|
if (!('server' in route.params))
|
||||||
return
|
return
|
||||||
|
|
||||||
const userAgent = useRequestHeaders()['user-agent']
|
const userAgent = useRequestHeaders()['user-agent']
|
||||||
|
|
|
@ -63,6 +63,12 @@ export interface ConfirmDialogLabel {
|
||||||
}
|
}
|
||||||
export type ConfirmDialogChoice = 'confirm' | 'cancel'
|
export type ConfirmDialogChoice = 'confirm' | 'cancel'
|
||||||
|
|
||||||
|
export interface ErrorDialogData {
|
||||||
|
title: string
|
||||||
|
messages: string[]
|
||||||
|
close: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface BuildInfo {
|
export interface BuildInfo {
|
||||||
version: string
|
version: string
|
||||||
commit: string
|
commit: string
|
||||||
|
|
Loading…
Reference in a new issue