feat: prevent mixing data on account switch (#384)

This commit is contained in:
Joaquín Sánchez 2022-12-12 00:30:26 +01:00 committed by GitHub
parent 8c82c91056
commit de160c219a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 26 deletions

View file

@ -15,7 +15,8 @@ export function setCached(key: string, value: any, override = false) {
} }
export function fetchStatus(id: string, force = false): Promise<Status> { export function fetchStatus(id: string, force = false): Promise<Status> {
const key = `status:${id}` const server = currentServer.value
const key = `${server}:status:${id}`
const cached = cache.get(key) const cached = cache.get(key)
if (cached && !force) if (cached && !force)
return cached return cached
@ -32,30 +33,37 @@ export function fetchAccountById(id?: string | null): Promise<Account | null> {
if (!id) if (!id)
return Promise.resolve(null) return Promise.resolve(null)
const key = `account:${id}` const server = currentServer.value
const key = `${server}:account:${id}`
const cached = cache.get(key) const cached = cache.get(key)
if (cached) if (cached)
return cached return cached
const uri = currentInstance.value?.uri
const promise = useMasto().accounts.fetch(id) const promise = useMasto().accounts.fetch(id)
.then((account) => { .then((r) => {
cacheAccount(account, true) if (!r.acct.includes('@') && uri)
return account r.acct = `${r.acct}@${uri}`
cacheAccount(r, server, true)
return r
}) })
cache.set(key, promise) cache.set(key, promise)
return promise return promise
} }
export async function fetchAccountByHandle(acct: string): Promise<Account> { export async function fetchAccountByHandle(acct: string): Promise<Account> {
const key = `account:${acct}` const server = currentServer.value
const key = `${server}:account:${acct}`
const cached = cache.get(key) const cached = cache.get(key)
if (cached) if (cached)
return cached return cached
const uri = currentInstance.value?.uri
const account = useMasto().accounts.lookup({ acct }) const account = useMasto().accounts.lookup({ acct })
.then((r) => { .then((r) => {
if (!r.acct.includes('@') && currentInstance.value) if (!r.acct.includes('@') && uri)
r.acct = `${r.acct}@${currentInstance.value.uri}` r.acct = `${r.acct}@${uri}`
cacheAccount(r, true) cacheAccount(r, server, true)
return r return r
}) })
cache.set(key, account) cache.set(key, account)
@ -70,11 +78,11 @@ export function useAccountById(id?: string | null) {
return useAsyncState(() => fetchAccountById(id), null).state return useAsyncState(() => fetchAccountById(id), null).state
} }
export function cacheStatus(status: Status, override?: boolean) { export function cacheStatus(status: Status, server = currentServer.value, override?: boolean) {
setCached(`status:${status.id}`, status, override) setCached(`${server}:status:${status.id}`, status, override)
} }
export function cacheAccount(account: Account, override?: boolean) { export function cacheAccount(account: Account, server = currentServer.value, override?: boolean) {
setCached(`account:${account.id}`, account, override) setCached(`${server}:account:${account.id}`, account, override)
setCached(`account:${account.acct}`, account, override) setCached(`${server}:account:${account.acct}`, account, override)
} }

View file

@ -31,12 +31,6 @@ export const currentInstance = computed<null | Instance>(() => currentUserId.val
export const characterLimit = computed(() => currentInstance.value?.configuration.statuses.maxCharacters ?? DEFAULT_POST_CHARS_LIMIT) export const characterLimit = computed(() => currentInstance.value?.configuration.statuses.maxCharacters ?? DEFAULT_POST_CHARS_LIMIT)
export async function loginTo(user?: Omit<UserLogin, 'account'> & { account?: AccountCredentials }) { export async function loginTo(user?: Omit<UserLogin, 'account'> & { account?: AccountCredentials }) {
if (user) {
const existing = users.value.find(u => u.server === user.server && u.token === user.token)
if (existing && currentUserId.value !== user.account?.id)
currentUserId.value = user.account?.id
}
const config = useRuntimeConfig() const config = useRuntimeConfig()
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
@ -76,7 +70,7 @@ export async function loginTo(user?: Omit<UserLogin, 'account'> & { account?: Ac
setMasto(masto) setMasto(masto)
if ('server' in route.params) { if ('server' in route.params && user?.token) {
await router.push({ await router.push({
...route, ...route,
force: true, force: true,
@ -110,7 +104,7 @@ export async function signout() {
currentUserId.value = users.value[0]?.account?.id currentUserId.value = users.value[0]?.account?.id
if (!currentUserId.value) if (!currentUserId.value)
await useRouter().push(`/${currentServer.value}/public`) await useRouter().push('/')
await loginTo(currentUser.value) await loginTo(currentUser.value)
} }
@ -127,7 +121,7 @@ export const useNotifications = () => {
} }
async function connect(): Promise<void> { async function connect(): Promise<void> {
if (!id || notifications[id]) if (!id || notifications[id] || !currentUser.value?.token)
return return
const masto = useMasto() const masto = useMasto()

View file

@ -6,7 +6,9 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
if (!('server' in to.params)) if (!('server' in to.params))
return return
if (!currentUser.value) { const user = currentUser.value
if (!user) {
if (from.params.server !== to.params.server) { if (from.params.server !== to.params.server) {
await loginTo({ await loginTo({
server: to.params.server as string, server: to.params.server as string,
@ -16,7 +18,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
} }
// 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 (currentUser.value.server === to.params.server) if (user.server === to.params.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
@ -29,12 +31,19 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
...to, ...to,
params: { params: {
...to.params, ...to.params,
server: currentUser.value.server, server: user.server,
}, },
} }
} }
try { try {
// If we're already on an account page, we can search for this on the new instance
if (to.params.account) {
const account = await fetchAccountByHandle(to.params.account as string)
if (account)
return getAccountRoute(account)
}
// If we're logged in, search for the local id the account or status corresponds to // If we're logged in, search for the local id the account or status corresponds to
const { value } = await useMasto().search({ q: `https:/${to.fullPath}`, resolve: true, limit: 1 }).next() const { value } = await useMasto().search({ q: `https:/${to.fullPath}`, resolve: true, limit: 1 }).next()

View file

@ -4,6 +4,7 @@ import type { ComponentPublicInstance } from 'vue'
definePageMeta({ definePageMeta({
name: 'status', name: 'status',
key: route => route.path,
}) })
const route = useRoute() const route = useRoute()

View file

@ -1,4 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
definePageMeta({
key: route => `${route.params.server}:${route.params.account}`,
})
const params = useRoute().params const params = useRoute().params
const accountName = $(computedEager(() => toShortHandle(params.account as string))) const accountName = $(computedEager(() => toShortHandle(params.account as string)))