From 9a7c37db2454c8b671e263e2c29bf4be21ec44c8 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 20 Dec 2022 00:16:15 +0000 Subject: [PATCH] fix: key current instance on user server, not user id (#466) --- .env.mock | 2 +- composables/masto.ts | 8 +++++--- composables/users.ts | 29 +++++++++++++++-------------- plugins/masto.ts | 9 +++++---- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/.env.mock b/.env.mock index e45acc79..9e2d8b22 100644 --- a/.env.mock +++ b/.env.mock @@ -1 +1 @@ -MOCK_USER='{"user":{"server":"universeodon.com","token":"yZcpj0FmnsEkUvBiXSCb_KQnccl2IU0kx9TfDbcxPJY","vapidKey":"BJwtUVlyCabpMnLI6HOyu-qMfJswxEq_c8pgRymxjTN_vCzMWfGrRHrwNczj9LIokAHtxh6Ziw1Kq7_ERDoriz0=","account":{"id":"109424142224653388","username":"elkdev","acct":"elkdev@universeodon.com","displayName":"Elk Dev Team","locked":false,"bot":false,"discoverable":null,"group":false,"createdAt":"2022-11-28T00:00:00.000Z","note":"","url":"https://universeodon.com/@elkdev","avatar":"https://universeodon.com/avatars/original/missing.png","avatarStatic":"https://universeodon.com/avatars/original/missing.png","header":"https://universeodon.com/headers/original/missing.png","headerStatic":"https://universeodon.com/headers/original/missing.png","followersCount":3,"followingCount":4,"statusesCount":20,"lastStatusAt":"2022-12-13","noindex":false,"source":{"privacy":"public","sensitive":false,"language":null,"note":"","fields":[],"followRequestsCount":0},"emojis":[],"fields":[],"role":{"id":"-99","name":"","permissions":"65536","color":"","highlighted":false}}},"server":{"109424142224653388":{"uri":"universeodon.com","title":"Universeodon","shortDescription":"Be one with the fediverse.","description":"","email":"novae@universeodon.com","version":"4.0.2","urls":{"streamingApi":"wss://universeodon.com"},"stats":{"userCount":57026,"statusCount":283364,"domainCount":11515},"thumbnail":"https://media.universeodon.com/site_uploads/files/000/000/003/@1x/9de6fc1bbd150b05.png","languages":["en"],"registrations":true,"approvalRequired":false,"invitesEnabled":true,"configuration":{"accounts":{"maxFeaturedTags":10},"statuses":{"maxCharacters":500,"maxMediaAttachments":4,"charactersReservedPerUrl":23},"mediaAttachments":{"supportedMimeTypes":["image/jpeg","image/png","image/gif","image/heic","image/heif","image/webp","image/avif","video/webm","video/mp4","video/quicktime","video/ogg","audio/wave","audio/wav","audio/x-wav","audio/x-pn-wave","audio/vnd.wave","audio/ogg","audio/vorbis","audio/mpeg","audio/mp3","audio/webm","audio/flac","audio/aac","audio/m4a","audio/x-m4a","audio/mp4","audio/3gpp","video/x-ms-asf"],"imageSizeLimit":10485760,"imageMatrixLimit":16777216,"videoSizeLimit":41943040,"videoFrameRateLimit":60,"videoMatrixLimit":2304000},"polls":{"maxOptions":4,"maxCharactersPerOption":50,"minExpiration":300,"maxExpiration":2629746}},"contactAccount":{"id":"109287809647205395","username":"supernovae","acct":"supernovae","displayName":"Supernovae","locked":false,"bot":false,"discoverable":true,"group":false,"createdAt":"2022-11-04T00:00:00.000Z","note":"","url":"https://universeodon.com/@supernovae","avatar":"https://media.universeodon.com/accounts/avatars/109/287/809/647/205/395/original/551eafba585d19e5.jpg","avatarStatic":"https://media.universeodon.com/accounts/avatars/109/287/809/647/205/395/original/551eafba585d19e5.jpg","header":"https://media.universeodon.com/accounts/headers/109/287/809/647/205/395/original/5de388c5945925c5.jpg","headerStatic":"https://media.universeodon.com/accounts/headers/109/287/809/647/205/395/original/5de388c5945925c5.jpg","followersCount":6387,"followingCount":305,"statusesCount":1753,"lastStatusAt":"2022-11-28","noindex":false,"emojis":[],"fields":[]},"rules":[]}}}' +MOCK_USER='{"user":{"server":"universeodon.com","token":"yZcpj0FmnsEkUvBiXSCb_KQnccl2IU0kx9TfDbcxPJY","vapidKey":"BJwtUVlyCabpMnLI6HOyu-qMfJswxEq_c8pgRymxjTN_vCzMWfGrRHrwNczj9LIokAHtxh6Ziw1Kq7_ERDoriz0=","account":{"id":"109424142224653388","username":"elkdev","acct":"elkdev@universeodon.com","displayName":"Elk Dev Team","locked":false,"bot":false,"discoverable":null,"group":false,"createdAt":"2022-11-28T00:00:00.000Z","note":"","url":"https://universeodon.com/@elkdev","avatar":"https://universeodon.com/avatars/original/missing.png","avatarStatic":"https://universeodon.com/avatars/original/missing.png","header":"https://universeodon.com/headers/original/missing.png","headerStatic":"https://universeodon.com/headers/original/missing.png","followersCount":3,"followingCount":4,"statusesCount":20,"lastStatusAt":"2022-12-13","noindex":false,"source":{"privacy":"public","sensitive":false,"language":null,"note":"","fields":[],"followRequestsCount":0},"emojis":[],"fields":[],"role":{"id":"-99","name":"","permissions":"65536","color":"","highlighted":false}}},"server":{"universeodon.com":{"uri":"universeodon.com","title":"Universeodon","shortDescription":"Be one with the fediverse.","description":"","email":"novae@universeodon.com","version":"4.0.2","urls":{"streamingApi":"wss://universeodon.com"},"stats":{"userCount":57026,"statusCount":283364,"domainCount":11515},"thumbnail":"https://media.universeodon.com/site_uploads/files/000/000/003/@1x/9de6fc1bbd150b05.png","languages":["en"],"registrations":true,"approvalRequired":false,"invitesEnabled":true,"configuration":{"accounts":{"maxFeaturedTags":10},"statuses":{"maxCharacters":500,"maxMediaAttachments":4,"charactersReservedPerUrl":23},"mediaAttachments":{"supportedMimeTypes":["image/jpeg","image/png","image/gif","image/heic","image/heif","image/webp","image/avif","video/webm","video/mp4","video/quicktime","video/ogg","audio/wave","audio/wav","audio/x-wav","audio/x-pn-wave","audio/vnd.wave","audio/ogg","audio/vorbis","audio/mpeg","audio/mp3","audio/webm","audio/flac","audio/aac","audio/m4a","audio/x-m4a","audio/mp4","audio/3gpp","video/x-ms-asf"],"imageSizeLimit":10485760,"imageMatrixLimit":16777216,"videoSizeLimit":41943040,"videoFrameRateLimit":60,"videoMatrixLimit":2304000},"polls":{"maxOptions":4,"maxCharactersPerOption":50,"minExpiration":300,"maxExpiration":2629746}},"contactAccount":{"id":"109287809647205395","username":"supernovae","acct":"supernovae","displayName":"Supernovae","locked":false,"bot":false,"discoverable":true,"group":false,"createdAt":"2022-11-04T00:00:00.000Z","note":"","url":"https://universeodon.com/@supernovae","avatar":"https://media.universeodon.com/accounts/avatars/109/287/809/647/205/395/original/551eafba585d19e5.jpg","avatarStatic":"https://media.universeodon.com/accounts/avatars/109/287/809/647/205/395/original/551eafba585d19e5.jpg","header":"https://media.universeodon.com/accounts/headers/109/287/809/647/205/395/original/5de388c5945925c5.jpg","headerStatic":"https://media.universeodon.com/accounts/headers/109/287/809/647/205/395/original/5de388c5945925c5.jpg","followersCount":6387,"followingCount":305,"statusesCount":1753,"lastStatusAt":"2022-11-28","noindex":false,"emojis":[],"fields":[]},"rules":[]}}}' diff --git a/composables/masto.ts b/composables/masto.ts index 985560ac..105b80be 100644 --- a/composables/masto.ts +++ b/composables/masto.ts @@ -43,7 +43,8 @@ export function getShortHandle({ acct }: Account) { export function getServerName(account: Account) { if (account.acct.includes('@')) return account.acct.split('@')[1] - return account.url.match(UserLinkRE)?.[1] || currentUser.value?.server || '' + // We should only lack the server name if we're on the same server as the account + return currentInstance.value?.uri || '' } export function getFullHandle(account: Account) { @@ -64,8 +65,9 @@ export function toShortHandle(fullHandle: string) { export function getAccountRoute(account: Account) { let handle = getFullHandle(account).slice(1) - if (handle.endsWith(`@${currentServer.value}`)) - handle = handle.slice(0, -currentServer.value.length - 1) + const uri = currentInstance.value?.uri ?? currentServer.value + if (currentInstance.value && handle.endsWith(`@${uri}`)) + handle = handle.slice(0, -uri.length - 1) return useRouter().resolve({ name: 'account-index', diff --git a/composables/users.ts b/composables/users.ts index d06647d9..5ec06be4 100644 --- a/composables/users.ts +++ b/composables/users.ts @@ -15,7 +15,7 @@ import type { PushNotificationPolicy, PushNotificationRequest } from '~/composab const mock = process.mock const users = useLocalStorage(STORAGE_KEY_USERS, mock ? [mock.user] : [], { deep: true }) -const servers = useLocalStorage>(STORAGE_KEY_SERVERS, mock ? mock.server : {}, { deep: true }) +const instances = useLocalStorage>(STORAGE_KEY_SERVERS, mock ? mock.server : {}, { deep: true }) const currentUserId = useLocalStorage(STORAGE_KEY_CURRENT_USER, mock ? mock.user.account.id : '') export const currentUser = computed(() => { @@ -29,19 +29,19 @@ export const currentUser = computed(() => { return users.value[0] }) +const publicInstance = ref(null) +export const currentInstance = computed(() => currentUser.value ? instances.value[currentUser.value.server] ?? null : publicInstance.value) + +export const publicServer = ref(DEFAULT_SERVER) +export const currentServer = computed(() => currentUser.value?.server || publicServer.value) + export const currentUserHandle = computed(() => currentUser.value?.account.id - ? `${currentUser.value.account.acct}@${currentUser.value.server}` + ? `${currentUser.value.account.acct}@${currentInstance.value?.uri || currentServer.value}` : '[anonymous]', ) -export const publicServer = ref(DEFAULT_SERVER) -const publicInstance = ref(null) -export const currentServer = computed(() => currentUser.value?.server || publicServer.value) - export const useUsers = () => users -export const currentInstance = computed(() => currentUserId.value ? servers.value[currentUserId.value] ?? null : publicInstance.value) - export const characterLimit = computed(() => currentInstance.value?.configuration.statuses.maxCharacters ?? DEFAULT_POST_CHARS_LIMIT) export async function loginTo(user?: Omit & { account?: AccountCredentials }) { @@ -62,7 +62,7 @@ export async function loginTo(user?: Omit & { account?: Ac else { try { - const [me, server, pushSubscription] = await Promise.all([ + const [me, instance, pushSubscription] = await Promise.all([ masto.accounts.verifyCredentials(), masto.instances.fetch(), // we get 404 response instead empty data @@ -72,10 +72,10 @@ export async function loginTo(user?: Omit & { account?: Ac user.account = me user.pushSubscription = pushSubscription currentUserId.value = me.id - servers.value[me.id] = server + instances.value[server] = instance if (!user.account.acct.includes('@')) - user.account.acct = `${user.account.acct}@${server.uri}` + user.account.acct = `${user.account.acct}@${instance.uri}` if (!users.value.some(u => u.server === user.server && u.token === user.token)) users.value.push(user as UserLogin) @@ -141,7 +141,8 @@ export async function signout() { if (index !== -1) { // Clear stale data clearUserLocalStorage() - delete servers.value[_currentUserId] + if (!users.value.some((u, i) => u.server === currentUser.value!.server && i !== index)) + delete instances.value[currentUser.value.server] await removePushNotifications(currentUser.value) @@ -221,7 +222,7 @@ export function useUserLocalStorage(key: string, initial: () = return computed(() => { const id = currentUser.value?.account.id - ? `${currentUser.value.account.acct}@${currentUser.value.server}` + ? `${currentUser.value.account.acct}@${currentInstance.value?.uri || currentServer.value}` : '[anonymous]' all.value[id] = Object.assign(initial(), all.value[id] || {}) return all.value[id] @@ -237,7 +238,7 @@ export function clearUserLocalStorage(account?: Account) { if (!account) return - const id = `${account.acct}@${currentUser.value?.server}` + const id = `${account.acct}@${currentInstance.value?.uri || currentServer.value}` // @ts-expect-error bind value to the function ;(useUserLocalStorage._ as Map>>).forEach((storage) => { if (storage.value[id]) diff --git a/plugins/masto.ts b/plugins/masto.ts index 3d33b925..0865330a 100644 --- a/plugins/masto.ts +++ b/plugins/masto.ts @@ -12,8 +12,8 @@ export default defineNuxtPlugin(async (nuxtApp) => { return initialised if (key === 'loginTo') { - return (...args: any[]) => { - apiPromise.value = loginTo(...args).then((r) => { + return (...args: any[]): Promise => { + return apiPromise.value = loginTo(...args).then((r) => { api.value = r return masto }).catch(() => { @@ -23,20 +23,21 @@ export default defineNuxtPlugin(async (nuxtApp) => { statusMessage: 'Could not log into account.', }) }) - return apiPromise } } if (api.value && key in api.value) return api.value[key as keyof MastoClient] - if (!api) { + if (!api.value) { return new Proxy({}, { get(_, subkey) { return (...args: any[]) => apiPromise.value?.then((r: any) => r[key][subkey](...args)) }, }) } + + return undefined }, })