feat: wip

This commit is contained in:
三咲智子 2023-01-03 09:23:16 +08:00
parent 1816530f84
commit f2d125ed4e
No known key found for this signature in database
GPG key ID: 69992F2250DFD93E
14 changed files with 64 additions and 40 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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 }">

View file

@ -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>

View file

@ -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>

View file

@ -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" />

View file

@ -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>

View file

@ -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
}

View file

@ -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)

View file

@ -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
users.value.push({ server, guest: true })
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]
await removePushNotifications(currentUser.value)
await removePushNotificationData(currentUser.value)
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] || {})

View file

@ -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')

View file

@ -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>

View file

@ -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

View file

@ -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>