forked from Mirrors/elk
feat: wip
This commit is contained in:
parent
1816530f84
commit
f2d125ed4e
14 changed files with 64 additions and 40 deletions
2
app.vue
2
app.vue
|
@ -3,7 +3,7 @@ setupPageHeader()
|
|||
provideGlobalCommands()
|
||||
|
||||
// We want to trigger rerendering the page when account changes
|
||||
const key = computed(() => `${currentUser.value?.server ?? currentServer.value}:${currentUser.value?.account.id || ''}`)
|
||||
const key = computed(() => `${currentServer.value}:${isGuest.value ? '[anonymous]' : currentUser.value!.account!.id || ''}`)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -41,8 +41,8 @@ onMastoInit(async () => {
|
|||
|
||||
// Optimize rendering for the common case of being logged in, only show visual feedback for disabled user-only items
|
||||
// when we know there is no user.
|
||||
const noUserDisable = computed(() => !isMastoInitialised.value || (props.userOnly && !currentUser.value))
|
||||
const noUserVisual = computed(() => isMastoInitialised.value && props.userOnly && !currentUser.value)
|
||||
const noUserDisable = computed(() => !isMastoInitialised.value || (props.userOnly && isGuest.value))
|
||||
const noUserVisual = computed(() => isMastoInitialised.value && props.userOnly && isGuest.value)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -2,12 +2,16 @@
|
|||
<VDropdown v-if="isMastoInitialised && currentUser" sm:hidden>
|
||||
<div style="-webkit-touch-callout: none;">
|
||||
<AccountAvatar
|
||||
v-if="!currentUser.guest"
|
||||
ref="avatar"
|
||||
:account="currentUser.account"
|
||||
h-8
|
||||
w-8
|
||||
:draggable="false"
|
||||
/>
|
||||
<div v-else>
|
||||
TODO: Guest
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #popper="{ hide }">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
const disabled = computed(() => !isMastoInitialised.value || !currentUser.value)
|
||||
const disabledVisual = computed(() => isMastoInitialised.value && !currentUser.value)
|
||||
const disabled = computed(() => !isMastoInitialised.value || isGuest.value)
|
||||
const disabledVisual = computed(() => isMastoInitialised.value && isGuest.value)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -181,7 +181,7 @@ defineExpose({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isMastoInitialised && currentUser" flex="~ col gap-4" py3 px2 sm:px4>
|
||||
<div v-if="isMastoInitialised && currentUser && currentUser.account && !isGuest" flex="~ col gap-4" py3 px2 sm:px4>
|
||||
<template v-if="draft.editingStatus">
|
||||
<div flex="~ col gap-1">
|
||||
<div id="state-editing" text-secondary self-center>
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
// workaround: cannot use TSNonNull `!` in template
|
||||
const account = $computed(() => currentUser.value!.account!)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDropdown :distance="0" placement="top-start">
|
||||
<button btn-action-icon :aria-label="$t('action.switch_account')">
|
||||
<div :class="{ 'hidden xl:block': currentUser }" i-ri:more-2-line />
|
||||
<AccountAvatar v-if="currentUser" xl:hidden :account="currentUser.account" w-9 h-9 />
|
||||
<AccountAvatar v-if="!isGuest" xl:hidden :account="account" w-9 h-9 />
|
||||
<span v-else>TODO: Guest</span>
|
||||
</button>
|
||||
<template #popper="{ hide }">
|
||||
<UserSwitcher @click="hide" />
|
||||
|
|
|
@ -6,7 +6,7 @@ const all = useUsers()
|
|||
const router = useRouter()
|
||||
const masto = useMasto()
|
||||
const switchUser = (user: UserLogin) => {
|
||||
if (!user.guest && !currentUser.value?.guest && user.account.id === currentUser.value?.account.id)
|
||||
if (!user.guest && !isGuest.value && user.account.id === currentUser.value!.account!.id)
|
||||
router.push(getAccountRoute(user.account))
|
||||
else
|
||||
masto.loginTo(user)
|
||||
|
@ -25,7 +25,8 @@ const switchUser = (user: UserLogin) => {
|
|||
hover="filter-none op100"
|
||||
@click="switchUser(user)"
|
||||
>
|
||||
<AccountAvatar w-13 h-13 :account="user.account" />
|
||||
<AccountAvatar v-if="!user.guest" w-13 h-13 :account="user.account" />
|
||||
<span v-else>TODO: Guest</span>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,7 @@ export interface PushManagerSubscriptionInfo {
|
|||
subscription: PushSubscription | null
|
||||
}
|
||||
|
||||
export interface RequiredUserLogin extends Required<Omit<UserLogin, 'account' | 'pushSubscription'>> {
|
||||
export interface RequiredUserLogin extends Required<Pick<UserLogin, 'server' | 'token' | 'vapidKey'>> {
|
||||
pushSubscription?: MastoPushSubscription
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import type {
|
|||
SubscriptionResult,
|
||||
} from '~/composables/push-notifications/types'
|
||||
import { STORAGE_KEY_NOTIFICATION, STORAGE_KEY_NOTIFICATION_POLICY } from '~/constants'
|
||||
import type { UserLogin } from '~/types'
|
||||
|
||||
const supportsPushNotifications = typeof window !== 'undefined'
|
||||
&& 'serviceWorker' in navigator
|
||||
|
@ -68,10 +69,10 @@ export const usePushManager = () => {
|
|||
if (!isSupported)
|
||||
return 'not-supported'
|
||||
|
||||
if (!currentUser.value)
|
||||
if (isGuest.value)
|
||||
return 'no-user'
|
||||
|
||||
const { pushSubscription, server, token, vapidKey, account: { acct } } = currentUser.value
|
||||
const { pushSubscription, server, token, vapidKey, account: { acct } } = currentUser.value as UserLogin<true>
|
||||
|
||||
if (!token || !server || !vapidKey)
|
||||
return 'invalid-vapid-key'
|
||||
|
@ -86,10 +87,8 @@ export const usePushManager = () => {
|
|||
return 'notification-denied'
|
||||
}
|
||||
|
||||
currentUser.value.pushSubscription = await createPushSubscription(
|
||||
{
|
||||
pushSubscription, server, token, vapidKey,
|
||||
},
|
||||
currentUser.value!.pushSubscription = await createPushSubscription(
|
||||
{ pushSubscription, server, token, vapidKey },
|
||||
notificationData ?? {
|
||||
alerts: {
|
||||
follow: true,
|
||||
|
@ -110,7 +109,7 @@ export const usePushManager = () => {
|
|||
}
|
||||
|
||||
const unsubscribe = async () => {
|
||||
if (!isSupported || !isSubscribed || !currentUser.value)
|
||||
if (!isSupported || !isSubscribed || !checkUser(currentUser.value))
|
||||
return false
|
||||
|
||||
await removePushNotifications(currentUser.value)
|
||||
|
|
|
@ -43,28 +43,30 @@ const initializeUsers = async (): Promise<Ref<UserLogin[]> | RemovableRef<UserLo
|
|||
const users = await initializeUsers()
|
||||
const instances = useLocalStorage<Record<string, Instance>>(STORAGE_KEY_SERVERS, mock ? mock.server : {}, { deep: true })
|
||||
const currentUserId = useLocalStorage<string>(STORAGE_KEY_CURRENT_USER, mock ? mock.user.account.id : '')
|
||||
const isGuestId = computed(() => currentUserId.value.startsWith('[anonymous]@'))
|
||||
|
||||
export const currentUser = computed<UserLogin | undefined>(() => {
|
||||
if (!currentUserId.value)
|
||||
// Fallback to the first account
|
||||
return users.value[0]
|
||||
|
||||
const user = users.value.find(user => user.account?.id === currentUserId.value)
|
||||
if (user)
|
||||
return user
|
||||
})
|
||||
if (isGuestId.value) {
|
||||
const server = currentUserId.value.replace('[anonymous]@', '')
|
||||
return users.value.find(user => user.guest && user.server === server)
|
||||
}
|
||||
|
||||
export const isGuest = computed(
|
||||
() => currentUserId.value.startsWith('[anonymous]@') || !currentUser.value?.account?.acct,
|
||||
)
|
||||
return users.value.find(user => user.account?.id === currentUserId.value)
|
||||
})
|
||||
|
||||
export const currentServer = computed<string>(() => currentUser.value?.server || DEFAULT_SERVER)
|
||||
export const currentInstance = computed<null | Instance>(() => {
|
||||
return instances.value[currentServer.value] ?? null
|
||||
})
|
||||
export const checkUser = (val: UserLogin | undefined): val is UserLogin<true> => !!(val && !val.guest)
|
||||
export const isGuest = computed(() => !checkUser(currentUser.value))
|
||||
|
||||
export const currentUserHandle = computed(() =>
|
||||
isGuest.value ? '[anonymous]' : currentUser.value!.account!.acct
|
||||
isGuestId.value ? '[anonymous]' : currentUser.value!.account!.acct
|
||||
,
|
||||
)
|
||||
|
||||
|
@ -75,7 +77,7 @@ export const characterLimit = computed(() => currentInstance.value?.configuratio
|
|||
async function loginTo(user?: UserLogin) {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const server = user?.server || (route.params.server as string)
|
||||
const server = user?.server || (route.params.server as string) || DEFAULT_SERVER
|
||||
const masto = await loginMasto({
|
||||
url: `https://${server}`,
|
||||
accessToken: user?.token,
|
||||
|
@ -87,9 +89,11 @@ async function loginTo(user?: UserLogin) {
|
|||
if (!user?.token) {
|
||||
const instance = await masto.instances.fetch()
|
||||
|
||||
currentUserId.value = `[anonymous]@${server}`
|
||||
instances.value[server] = instance
|
||||
if (!users.value.some(u => u.server === server && u.guest))
|
||||
users.value.push({ server, guest: true })
|
||||
|
||||
currentUserId.value = `[anonymous]@${server}`
|
||||
}
|
||||
|
||||
else {
|
||||
|
@ -183,7 +187,7 @@ export async function removePushNotificationData(user: UserLogin, fromSWPushMana
|
|||
}
|
||||
}
|
||||
|
||||
export async function removePushNotifications(user: UserLogin) {
|
||||
export async function removePushNotifications(user: UserLogin<true>) {
|
||||
if (!user.pushSubscription)
|
||||
return
|
||||
|
||||
|
@ -213,9 +217,10 @@ export async function signout() {
|
|||
if (!users.value.some((u, i) => u.server === currentUser.value!.server && i !== index))
|
||||
delete instances.value[currentUser.value.server]
|
||||
|
||||
if (checkUser(currentUser.value)) {
|
||||
await removePushNotifications(currentUser.value)
|
||||
|
||||
await removePushNotificationData(currentUser.value)
|
||||
}
|
||||
|
||||
currentUserId.value = ''
|
||||
// Remove the current user from the users
|
||||
|
@ -234,7 +239,7 @@ export async function signout() {
|
|||
const notifications = reactive<Record<string, undefined | [Promise<WsEvents>, number]>>({})
|
||||
|
||||
export const useNotifications = () => {
|
||||
const id = currentUser.value?.account!.id
|
||||
const id = $computed(() => currentUser.value?.account?.id)
|
||||
const masto = useMasto()
|
||||
|
||||
const clearNotifications = () => {
|
||||
|
@ -292,7 +297,7 @@ export function useUserLocalStorage<T extends object>(key: string, initial: () =
|
|||
const all = storages.get(key) as Ref<Record<string, T>>
|
||||
|
||||
return computed(() => {
|
||||
const id = isGuest.value
|
||||
const id = isGuestId.value
|
||||
? '[anonymous]'
|
||||
: currentUser.value!.account!.acct
|
||||
all.value[id] = Object.assign(initial(), all.value[id] || {})
|
||||
|
|
|
@ -5,7 +5,7 @@ export default defineNuxtRouteMiddleware((to) => {
|
|||
return
|
||||
|
||||
onMastoInit(() => {
|
||||
if (!currentUser.value)
|
||||
if (isGuest.value)
|
||||
return navigateTo(`/${currentServer.value}/public`)
|
||||
if (to.path === '/')
|
||||
return navigateTo('/home')
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<script setup lang="ts">
|
||||
import { invoke } from '@vueuse/shared'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const tabs = $computed(() => [
|
||||
|
@ -20,7 +18,7 @@ const tabs = $computed(() => [
|
|||
{
|
||||
to: `/${currentServer.value}/explore/users`,
|
||||
display: t('tab.for_you'),
|
||||
disabled: !isMastoInitialised.value || !currentUser.value,
|
||||
disabled: !isMastoInitialised.value || isGuest.value,
|
||||
},
|
||||
] as const)
|
||||
</script>
|
||||
|
|
|
@ -8,6 +8,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|||
server: query.server,
|
||||
token: query.token,
|
||||
vapidKey: typeof query.vapid_key === 'string' ? query.vapid_key : undefined,
|
||||
guest: false,
|
||||
}
|
||||
: currentUser.value
|
||||
|
||||
|
|
|
@ -12,12 +12,22 @@ export interface AppInfo {
|
|||
vapid_key: string
|
||||
}
|
||||
|
||||
export type UserLogin = {
|
||||
interface UserLoginWithToken {
|
||||
account: AccountCredentials
|
||||
guest: false
|
||||
}
|
||||
|
||||
interface UserLoginGuest {
|
||||
account?: undefined
|
||||
guest: true
|
||||
}
|
||||
|
||||
export type UserLogin<WithToken extends boolean = boolean> = {
|
||||
server: string
|
||||
token?: string
|
||||
vapidKey?: string
|
||||
pushSubscription?: PushSubscription
|
||||
} & ({ account: AccountCredentials; guest: false } | { account?: undefined; guest: true })
|
||||
} & ((WithToken extends false ? UserLoginGuest : never) | (WithToken extends true ? UserLoginWithToken : never))
|
||||
|
||||
export interface ElkMasto extends MastoClient {
|
||||
loginTo(user?: UserLogin): Promise<MastoClient>
|
||||
|
|
Loading…
Reference in a new issue