From 25e030a7074f8e00cfbdaf85e491b9b22886991d Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 11 Sep 2023 14:55:18 +0900 Subject: [PATCH] enhance(frontend): improve some caches --- packages/frontend/src/cache.ts | 9 ++-- packages/frontend/src/local-storage.ts | 2 - .../frontend/src/pages/my-antennas/index.vue | 7 ++- .../frontend/src/pages/my-lists/index.vue | 2 +- packages/frontend/src/pages/timeline.vue | 17 ++----- packages/frontend/src/scripts/cache.ts | 46 +++---------------- .../frontend/src/scripts/get-note-menu.ts | 2 +- .../frontend/src/scripts/get-user-menu.ts | 6 +-- 8 files changed, 23 insertions(+), 68 deletions(-) diff --git a/packages/frontend/src/cache.ts b/packages/frontend/src/cache.ts index 3523d4406..1f3d28ba5 100644 --- a/packages/frontend/src/cache.ts +++ b/packages/frontend/src/cache.ts @@ -5,8 +5,9 @@ import * as Misskey from 'misskey-js'; import { Cache } from '@/scripts/cache'; +import { api } from '@/os'; -export const clipsCache = new Cache(Infinity); -export const rolesCache = new Cache(Infinity); -export const userListsCache = new Cache(Infinity); -export const antennasCache = new Cache(Infinity); +export const clipsCache = new Cache(1000 * 60 * 30, () => api('clips/list')); +export const rolesCache = new Cache(1000 * 60 * 30, () => api('admin/roles/list')); +export const userListsCache = new Cache(1000 * 60 * 30, () => api('users/lists/list')); +export const antennasCache = new Cache(1000 * 60 * 30, () => api('antennas/list')); diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index f4c11d613..0d73885b6 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -30,8 +30,6 @@ type Keys = 'message_drafts' | 'scratchpad' | 'debug' | - 'userListsCache' | - 'antennasCache' | `miux:${string}` | `ui:folder:${string}` | `themes:${string}` | diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue index bd7af2213..18ba28964 100644 --- a/packages/frontend/src/pages/my-antennas/index.vue +++ b/packages/frontend/src/pages/my-antennas/index.vue @@ -28,18 +28,17 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue index edfff8b05..a779e4a94 100644 --- a/packages/frontend/src/pages/my-lists/index.vue +++ b/packages/frontend/src/pages/my-lists/index.vue @@ -42,7 +42,7 @@ import { $i } from '@/account'; const items = $computed(() => userListsCache.value.value ?? []); function fetch() { - userListsCache.fetch(() => os.api('users/lists/list')); + userListsCache.fetch(); } fetch(); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 3ec4a6788..f0ef2d10a 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -39,6 +39,7 @@ import { instance } from '@/instance'; import { $i } from '@/account'; import { definePageMetadata } from '@/scripts/page-metadata'; import { miLocalStorage } from '@/local-storage'; +import { antennasCache, userListsCache } from '@/cache'; provide('shouldOmitHeaderTitle', true); @@ -68,24 +69,17 @@ function top(): void { } async function chooseList(ev: MouseEvent): Promise { - const cachedLists = miLocalStorage.getItemAsJson('userListsCache'); - const lists = cachedLists ?? await os.api('users/lists/list'); + const lists = await userListsCache.fetch(); const items = lists.map(list => ({ type: 'link' as const, text: list.name, to: `/timeline/list/${list.id}`, })); os.popupMenu(items, ev.currentTarget ?? ev.target); - if (cachedLists == null) { - miLocalStorage.setItemAsJson('userListsCache', lists); - } else { - miLocalStorage.setItemAsJson('userListsCache', await os.api('users/lists/list')); - } } async function chooseAntenna(ev: MouseEvent): Promise { - const cachedAntennas = miLocalStorage.getItemAsJson('antennasCache'); - const antennas = cachedAntennas ?? await os.api('antennas/list'); + const antennas = await antennasCache.fetch(); const items = antennas.map(antenna => ({ type: 'link' as const, text: antenna.name, @@ -93,11 +87,6 @@ async function chooseAntenna(ev: MouseEvent): Promise { to: `/timeline/antenna/${antenna.id}`, })); os.popupMenu(items, ev.currentTarget ?? ev.target); - if (cachedAntennas == null) { - miLocalStorage.setItemAsJson('antennasCache', antennas); - } else { - miLocalStorage.setItemAsJson('antennasCache', await os.api('antennas/list')); - } } async function chooseChannel(ev: MouseEvent): Promise { diff --git a/packages/frontend/src/scripts/cache.ts b/packages/frontend/src/scripts/cache.ts index 0dda203ef..12347cf4b 100644 --- a/packages/frontend/src/scripts/cache.ts +++ b/packages/frontend/src/scripts/cache.ts @@ -9,9 +9,11 @@ export class Cache { private cachedAt: number | null = null; public value = ref(); private lifetime: number; + private fetcher: () => Promise; - constructor(lifetime: Cache['lifetime']) { + constructor(lifetime: Cache['lifetime'], fetcher: () => Promise) { this.lifetime = lifetime; + this.fetcher = fetcher; } public set(value: T): void { @@ -35,51 +37,17 @@ export class Cache { /** * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します - * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします */ - public async fetch(fetcher: () => Promise, validator?: (cachedValue: T) => boolean): Promise { + public async fetch(): Promise { const cachedValue = this.get(); if (cachedValue !== undefined) { - if (validator) { - if (validator(cachedValue)) { - // Cache HIT - return cachedValue; - } - } else { - // Cache HIT - return cachedValue; - } + // Cache HIT + return cachedValue; } // Cache MISS - const value = await fetcher(); + const value = await this.fetcher(); this.set(value); return value; } - - /** - * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します - * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします - */ - public async fetchMaybe(fetcher: () => Promise, validator?: (cachedValue: T) => boolean): Promise { - const cachedValue = this.get(); - if (cachedValue !== undefined) { - if (validator) { - if (validator(cachedValue)) { - // Cache HIT - return cachedValue; - } - } else { - // Cache HIT - return cachedValue; - } - } - - // Cache MISS - const value = await fetcher(); - if (value !== undefined) { - this.set(value); - } - return value; - } } diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index c8b1cb8df..5bda993ff 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -32,7 +32,7 @@ export async function getNoteClipMenu(props: { const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note; - const clips = await clipsCache.fetch(() => os.api('clips/list')); + const clips = await clipsCache.fetch(); return [...clips.map(clip => ({ text: clip.name, action: () => { diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 3b125edfc..2ab21e6c2 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -170,7 +170,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router icon: 'ti ti-list', text: i18n.ts.addToList, children: async () => { - const lists = await userListsCache.fetch(() => os.api('users/lists/list')); + const lists = await userListsCache.fetch(); return lists.map(list => { const isListed = ref(list.userIds.includes(user.id)); cleanups.push(watch(isListed, () => { @@ -203,7 +203,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router icon: 'ti ti-antenna', text: i18n.ts.addToAntenna, children: async () => { - const antennas = await antennasCache.fetch(() => os.api('antennas/list')); + const antennas = await antennasCache.fetch(); const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}`; return antennas.filter((a) => a.src === 'users').map(antenna => ({ text: antenna.name, @@ -234,7 +234,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router icon: 'ti ti-badges', text: i18n.ts.roles, children: async () => { - const roles = await rolesCache.fetch(() => os.api('admin/roles/list')); + const roles = await rolesCache.fetch(); return roles.filter(r => r.target === 'manual').map(r => ({ text: r.name,