Skip to content

Commit

Permalink
fix: change page reload and account switch logic (#2975)
Browse files Browse the repository at this point in the history
  • Loading branch information
userquin authored Sep 25, 2024
1 parent 54344ac commit 44074ff
Show file tree
Hide file tree
Showing 9 changed files with 328 additions and 253 deletions.
29 changes: 27 additions & 2 deletions components/nav/NavSide.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,31 @@ const { notifications } = useNotifications()
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
const lastAccessedNotificationRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_NOTIFICATION_ROUTE, '')
const lastAccessedExploreRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_EXPLORE_ROUTE, '')
const notificationsLink = computed(() => {
const hydrated = isHydrated.value
const user = currentUser.value
const lastRoute = lastAccessedNotificationRoute.value
if (!hydrated || !user || !lastRoute) {
return '/notifications'
}
return `/notifications/${lastRoute}`
})
const exploreLink = computed(() => {
const hydrated = isHydrated.value
const server = currentServer.value
let lastRoute = lastAccessedExploreRoute.value
if (!hydrated) {
return '/explore'
}
if (lastRoute.length) {
lastRoute = `/${lastRoute}`
}
return server ? `/${server}/explore${lastRoute}` : `/explore${lastRoute}`
})
</script>

<template>
Expand All @@ -16,7 +41,7 @@ const lastAccessedExploreRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_EXPLO

<div class="spacer" shrink xl:hidden />
<NavSideItem :text="$t('nav.home')" to="/home" icon="i-ri:home-5-line" user-only :command="command" />
<NavSideItem :text="$t('nav.notifications')" :to="`/notifications/${lastAccessedNotificationRoute}`" icon="i-ri:notification-4-line" user-only :command="command">
<NavSideItem :text="$t('nav.notifications')" :to="notificationsLink" icon="i-ri:notification-4-line" user-only :command="command">
<template #icon>
<div flex relative>
<div class="i-ri:notification-4-line" text-xl />
Expand All @@ -34,7 +59,7 @@ const lastAccessedExploreRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_EXPLO
<NavSideItem :text="$t('action.compose')" to="/compose" icon="i-ri:quill-pen-line" user-only :command="command" />

<div class="spacer" shrink hidden sm:block />
<NavSideItem :text="$t('nav.explore')" :to="isHydrated ? `/${currentServer}/explore/${lastAccessedExploreRoute}` : `/explore/${lastAccessedExploreRoute}`" icon="i-ri:compass-3-line" :command="command" />
<NavSideItem :text="$t('nav.explore')" :to="exploreLink" icon="i-ri:compass-3-line" :command="command" />
<NavSideItem :text="$t('nav.local')" :to="isHydrated ? `/${currentServer}/public/local` : '/public/local'" icon="i-ri:group-2-line " :command="command" />
<NavSideItem :text="$t('nav.federated')" :to="isHydrated ? `/${currentServer}/public` : '/public'" icon="i-ri:earth-line" :command="command" />
<NavSideItem :text="$t('nav.lists')" :to="isHydrated ? `/${currentServer}/lists` : '/lists'" icon="i-ri:list-check" user-only :command="command" />
Expand Down
73 changes: 38 additions & 35 deletions composables/idb/index.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,75 @@
import type { MaybeRefOrGetter, RemovableRef } from '@vueuse/core'
import type { Ref } from 'vue'
import type { UseIDBOptions } from '@vueuse/integrations/useIDBKeyval'
import { del, get, set, update } from '~/utils/elk-idb'

const isIDBSupported = !process.test && typeof indexedDB !== 'undefined'
export interface UseAsyncIDBKeyvalReturn<T> {
set: (value: T) => Promise<void>
readIDB: () => Promise<T | undefined>
}

export async function useAsyncIDBKeyval<T>(
key: IDBValidKey,
initialValue: MaybeRefOrGetter<T>,
options: UseIDBOptions = {},
source?: Ref<T>,
): Promise<RemovableRef<T>> {
source: RemovableRef<T>,
options: Omit<UseIDBOptions, 'shallow'> = {},
): Promise<UseAsyncIDBKeyvalReturn<T>> {
const {
flush = 'pre',
deep = true,
shallow,
writeDefaults = true,
onError = (e: unknown) => {
console.error(e)
},
} = options

const data = source ?? (shallow ? shallowRef : ref)(initialValue) as Ref<T>

const rawInit: T = toValue<T>(initialValue)

async function read() {
if (!isIDBSupported)
return
try {
const rawValue = await get<T>(key)
if (rawValue === undefined) {
if (rawInit !== undefined && rawInit !== null)
await set(key, rawInit)
}
else {
data.value = rawValue
try {
const rawValue = await get<T>(key)
if (rawValue === undefined) {
if (rawInit !== undefined && rawInit !== null && writeDefaults) {
await set(key, rawInit)
source.value = rawInit
}
}
catch (e) {
onError(e)
else {
source.value = rawValue
}
}
catch (e) {
onError(e)
}

await read()

async function write() {
if (!isIDBSupported)
return
async function write(data: T) {
try {
if (data.value == null) {
if (data == null) {
await del(key)
}
else {
// IndexedDB does not support saving proxies, convert from proxy before saving
if (Array.isArray(data.value))
await update(key, () => (JSON.parse(JSON.stringify(data.value))))
else if (typeof data.value === 'object')
await update(key, () => ({ ...data.value }))
else
await update(key, () => (data.value))
await update(key, () => toRaw(data))
}
}
catch (e) {
onError(e)
}
}

watch(data, () => write(), { flush, deep })
const {
pause: pauseWatch,
resume: resumeWatch,
} = watchPausable(source, data => write(data), { flush, deep })

async function setData(value: T): Promise<void> {
pauseWatch()
try {
await write(value)
source.value = value
}
finally {
resumeWatch()
}
}

return data as RemovableRef<T>
return { set: setData, readIDB: () => get<T>(key) }
}
62 changes: 17 additions & 45 deletions composables/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const mock = process.mock

const users: Ref<UserLogin[]> | RemovableRef<UserLogin[]> = import.meta.server ? ref<UserLogin[]>([]) : ref<UserLogin[]>([]) as RemovableRef<UserLogin[]>
const nodes = useLocalStorage<Record<string, any>>(STORAGE_KEY_NODES, {}, { deep: true })
const currentUserHandle = useLocalStorage<string>(STORAGE_KEY_CURRENT_USER_HANDLE, mock ? mock.user.account.id : '')
export const currentUserHandle = useLocalStorage<string>(STORAGE_KEY_CURRENT_USER_HANDLE, mock ? mock.user.account.id : '')
export const instanceStorage = useLocalStorage<Record<string, mastodon.v1.Instance>>(STORAGE_KEY_SERVERS, mock ? mock.server : {}, { deep: true })

export type ElkInstance = Partial<mastodon.v1.Instance> & {
Expand All @@ -32,17 +32,24 @@ export function getInstanceCache(server: string): mastodon.v1.Instance | undefin
}

export const currentUser = computed<UserLogin | undefined>(() => {
if (currentUserHandle.value) {
const user = users.value.find(user => user.account?.acct === currentUserHandle.value)
const handle = currentUserHandle.value
const currentUsers = users.value
if (handle) {
const user = currentUsers.find(user => user.account?.acct === handle)
if (user)
return user
}
// Fallback to the first account
return users.value[0]
return currentUsers.length ? currentUsers[0] : undefined
})

const publicInstance = ref<ElkInstance | null>(null)
export const currentInstance = computed<null | ElkInstance>(() => currentUser.value ? instanceStorage.value[currentUser.value.server] ?? null : publicInstance.value)
export const currentInstance = computed<null | ElkInstance>(() => {
const user = currentUser.value
const storage = instanceStorage.value
const instance = publicInstance.value
return user ? storage[user.server] ?? null : instance
})

export function getInstanceDomain(instance: ElkInstance) {
return instance.accountDomain || withoutProtocol(instance.uri)
Expand All @@ -55,44 +62,6 @@ export const currentNodeInfo = computed<null | Record<string, any>>(() => nodes.
export const isGotoSocial = computed(() => currentNodeInfo.value?.software?.name === 'gotosocial')
export const isGlitchEdition = computed(() => currentInstance.value?.version?.includes('+glitch'))

// when multiple tabs: we need to reload window when sign in, switch account or sign out
if (import.meta.client) {
// fix #2972: now users loaded from idb, we need to wait for it
const initialLoad = ref(true)
watchOnce(users, () => {
initialLoad.value = false
}, { immediate: true, flush: 'sync' })

const windowReload = () => {
if (document.visibilityState === 'visible' && !initialLoad.value)
window.location.reload()
}
watch(currentUserHandle, async (handle, oldHandle) => {
// when sign in or switch account
if (handle) {
if (handle === currentUser.value?.account?.acct) {
// when sign in, the other tab will not have the user, idb is not reactive
const newUser = users.value.find(user => user.account?.acct === handle)
// if the user is there, then we are switching account
if (newUser) {
// check if the change is on current tab: if so, don't reload
if (document.hasFocus() || document.visibilityState === 'visible')
return
}
}

window.addEventListener('visibilitychange', windowReload, { capture: true })
}
// when sign out
else if (oldHandle) {
const oldUser = users.value.find(user => user.account?.acct === oldHandle)
// when sign out, the other tab will not have the user, idb is not reactive
if (oldUser)
window.addEventListener('visibilitychange', windowReload, { capture: true })
}
}, { immediate: true, flush: 'post' })
}

export function useUsers() {
return users
}
Expand All @@ -102,7 +71,10 @@ export function useSelfAccount(user: MaybeRefOrGetter<mastodon.v1.Account | unde

export const characterLimit = computed(() => currentInstance.value?.configuration?.statuses.maxCharacters ?? DEFAULT_POST_CHARS_LIMIT)

export async function loginTo(masto: ElkMasto, user: Overwrite<UserLogin, { account?: mastodon.v1.AccountCredentials }>) {
export async function loginTo(
masto: ElkMasto,
user: Overwrite<UserLogin, { account?: mastodon.v1.AccountCredentials }>,
) {
const { client } = masto
const instance = mastoLogin(masto, user)

Expand Down Expand Up @@ -303,7 +275,7 @@ export async function signOut() {
if (!currentUserHandle.value)
await useRouter().push('/')

loginTo(masto, currentUser.value || { server: publicServer.value })
await loginTo(masto, currentUser.value || { server: publicServer.value })
}

export function checkLogin() {
Expand Down
Loading

0 comments on commit 44074ff

Please sign in to comment.