Skip to content

Commit e7ff1f9

Browse files
userquinmaybeanerd
authored andcommitted
fix: change page reload and account switch logic (elk-zone#2975)
1 parent 2894762 commit e7ff1f9

File tree

9 files changed

+328
-253
lines changed

9 files changed

+328
-253
lines changed

components/nav/NavSide.vue

+27-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,31 @@ const { notifications } = useNotifications()
88
const useStarFavoriteIcon = usePreferences('useStarFavoriteIcon')
99
const lastAccessedNotificationRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_NOTIFICATION_ROUTE, '')
1010
const lastAccessedExploreRoute = useLocalStorage(STORAGE_KEY_LAST_ACCESSED_EXPLORE_ROUTE, '')
11+
12+
const notificationsLink = computed(() => {
13+
const hydrated = isHydrated.value
14+
const user = currentUser.value
15+
const lastRoute = lastAccessedNotificationRoute.value
16+
if (!hydrated || !user || !lastRoute) {
17+
return '/notifications'
18+
}
19+
20+
return `/notifications/${lastRoute}`
21+
})
22+
const exploreLink = computed(() => {
23+
const hydrated = isHydrated.value
24+
const server = currentServer.value
25+
let lastRoute = lastAccessedExploreRoute.value
26+
if (!hydrated) {
27+
return '/explore'
28+
}
29+
30+
if (lastRoute.length) {
31+
lastRoute = `/${lastRoute}`
32+
}
33+
34+
return server ? `/${server}/explore${lastRoute}` : `/explore${lastRoute}`
35+
})
1136
</script>
1237

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

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

3661
<div class="spacer" shrink hidden sm:block />
37-
<NavSideItem :text="$t('nav.explore')" :to="isHydrated ? `/${currentServer}/explore/${lastAccessedExploreRoute}` : `/explore/${lastAccessedExploreRoute}`" icon="i-ri:compass-3-line" :command="command" />
62+
<NavSideItem :text="$t('nav.explore')" :to="exploreLink" icon="i-ri:compass-3-line" :command="command" />
3863
<NavSideItem :text="$t('nav.local')" :to="isHydrated ? `/${currentServer}/public/local` : '/public/local'" icon="i-ri:group-2-line " :command="command" />
3964
<NavSideItem :text="$t('nav.federated')" :to="isHydrated ? `/${currentServer}/public` : '/public'" icon="i-ri:earth-line" :command="command" />
4065
<NavSideItem :text="$t('nav.lists')" :to="isHydrated ? `/${currentServer}/lists` : '/lists'" icon="i-ri:list-check" user-only :command="command" />

composables/idb/index.ts

+38-35
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,75 @@
11
import type { MaybeRefOrGetter, RemovableRef } from '@vueuse/core'
2-
import type { Ref } from 'vue'
32
import type { UseIDBOptions } from '@vueuse/integrations/useIDBKeyval'
43
import { del, get, set, update } from '~/utils/elk-idb'
54

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

810
export async function useAsyncIDBKeyval<T>(
911
key: IDBValidKey,
1012
initialValue: MaybeRefOrGetter<T>,
11-
options: UseIDBOptions = {},
12-
source?: Ref<T>,
13-
): Promise<RemovableRef<T>> {
13+
source: RemovableRef<T>,
14+
options: Omit<UseIDBOptions, 'shallow'> = {},
15+
): Promise<UseAsyncIDBKeyvalReturn<T>> {
1416
const {
1517
flush = 'pre',
1618
deep = true,
17-
shallow,
19+
writeDefaults = true,
1820
onError = (e: unknown) => {
1921
console.error(e)
2022
},
2123
} = options
2224

23-
const data = source ?? (shallow ? shallowRef : ref)(initialValue) as Ref<T>
24-
2525
const rawInit: T = toValue<T>(initialValue)
2626

27-
async function read() {
28-
if (!isIDBSupported)
29-
return
30-
try {
31-
const rawValue = await get<T>(key)
32-
if (rawValue === undefined) {
33-
if (rawInit !== undefined && rawInit !== null)
34-
await set(key, rawInit)
35-
}
36-
else {
37-
data.value = rawValue
27+
try {
28+
const rawValue = await get<T>(key)
29+
if (rawValue === undefined) {
30+
if (rawInit !== undefined && rawInit !== null && writeDefaults) {
31+
await set(key, rawInit)
32+
source.value = rawInit
3833
}
3934
}
40-
catch (e) {
41-
onError(e)
35+
else {
36+
source.value = rawValue
4237
}
4338
}
39+
catch (e) {
40+
onError(e)
41+
}
4442

45-
await read()
46-
47-
async function write() {
48-
if (!isIDBSupported)
49-
return
43+
async function write(data: T) {
5044
try {
51-
if (data.value == null) {
45+
if (data == null) {
5246
await del(key)
5347
}
5448
else {
5549
// IndexedDB does not support saving proxies, convert from proxy before saving
56-
if (Array.isArray(data.value))
57-
await update(key, () => (JSON.parse(JSON.stringify(data.value))))
58-
else if (typeof data.value === 'object')
59-
await update(key, () => ({ ...data.value }))
60-
else
61-
await update(key, () => (data.value))
50+
await update(key, () => toRaw(data))
6251
}
6352
}
6453
catch (e) {
6554
onError(e)
6655
}
6756
}
6857

69-
watch(data, () => write(), { flush, deep })
58+
const {
59+
pause: pauseWatch,
60+
resume: resumeWatch,
61+
} = watchPausable(source, data => write(data), { flush, deep })
62+
63+
async function setData(value: T): Promise<void> {
64+
pauseWatch()
65+
try {
66+
await write(value)
67+
source.value = value
68+
}
69+
finally {
70+
resumeWatch()
71+
}
72+
}
7073

71-
return data as RemovableRef<T>
74+
return { set: setData, readIDB: () => get<T>(key) }
7275
}

composables/users.ts

+17-45
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const mock = process.mock
1919

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

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

3434
export const currentUser = computed<UserLogin | undefined>(() => {
35-
if (currentUserHandle.value) {
36-
const user = users.value.find(user => user.account?.acct === currentUserHandle.value)
35+
const handle = currentUserHandle.value
36+
const currentUsers = users.value
37+
if (handle) {
38+
const user = currentUsers.find(user => user.account?.acct === handle)
3739
if (user)
3840
return user
3941
}
4042
// Fallback to the first account
41-
return users.value[0]
43+
return currentUsers.length ? currentUsers[0] : undefined
4244
})
4345

4446
const publicInstance = ref<ElkInstance | null>(null)
45-
export const currentInstance = computed<null | ElkInstance>(() => currentUser.value ? instanceStorage.value[currentUser.value.server] ?? null : publicInstance.value)
47+
export const currentInstance = computed<null | ElkInstance>(() => {
48+
const user = currentUser.value
49+
const storage = instanceStorage.value
50+
const instance = publicInstance.value
51+
return user ? storage[user.server] ?? null : instance
52+
})
4653

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

58-
// when multiple tabs: we need to reload window when sign in, switch account or sign out
59-
if (import.meta.client) {
60-
// fix #2972: now users loaded from idb, we need to wait for it
61-
const initialLoad = ref(true)
62-
watchOnce(users, () => {
63-
initialLoad.value = false
64-
}, { immediate: true, flush: 'sync' })
65-
66-
const windowReload = () => {
67-
if (document.visibilityState === 'visible' && !initialLoad.value)
68-
window.location.reload()
69-
}
70-
watch(currentUserHandle, async (handle, oldHandle) => {
71-
// when sign in or switch account
72-
if (handle) {
73-
if (handle === currentUser.value?.account?.acct) {
74-
// when sign in, the other tab will not have the user, idb is not reactive
75-
const newUser = users.value.find(user => user.account?.acct === handle)
76-
// if the user is there, then we are switching account
77-
if (newUser) {
78-
// check if the change is on current tab: if so, don't reload
79-
if (document.hasFocus() || document.visibilityState === 'visible')
80-
return
81-
}
82-
}
83-
84-
window.addEventListener('visibilitychange', windowReload, { capture: true })
85-
}
86-
// when sign out
87-
else if (oldHandle) {
88-
const oldUser = users.value.find(user => user.account?.acct === oldHandle)
89-
// when sign out, the other tab will not have the user, idb is not reactive
90-
if (oldUser)
91-
window.addEventListener('visibilitychange', windowReload, { capture: true })
92-
}
93-
}, { immediate: true, flush: 'post' })
94-
}
95-
9665
export function useUsers() {
9766
return users
9867
}
@@ -102,7 +71,10 @@ export function useSelfAccount(user: MaybeRefOrGetter<mastodon.v1.Account | unde
10271

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

105-
export async function loginTo(masto: ElkMasto, user: Overwrite<UserLogin, { account?: mastodon.v1.AccountCredentials }>) {
74+
export async function loginTo(
75+
masto: ElkMasto,
76+
user: Overwrite<UserLogin, { account?: mastodon.v1.AccountCredentials }>,
77+
) {
10678
const { client } = masto
10779
const instance = mastoLogin(masto, user)
10880

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

306-
loginTo(masto, currentUser.value || { server: publicServer.value })
278+
await loginTo(masto, currentUser.value || { server: publicServer.value })
307279
}
308280

309281
export function checkLogin() {

0 commit comments

Comments
 (0)