diff --git a/packages/frontend/src/scripts/note-drafts.ts b/packages/frontend/src/scripts/note-drafts.ts index 0b886f912c..c5f1376deb 100644 --- a/packages/frontend/src/scripts/note-drafts.ts +++ b/packages/frontend/src/scripts/note-drafts.ts @@ -2,9 +2,13 @@ import * as Misskey from 'misskey-js'; import type { PollEditorModelValue } from '@/components/MkPollEditor.vue'; import type { DeleteScheduleEditorModelValue } from '@/components/MkDeleteScheduleEditor.vue'; import { miLocalStorage } from '@/local-storage.js'; +import { get as idbGet, set as idbSet } from '@/scripts/idb-proxy.js'; export type NoteDraft = { updatedAt: Date; + type: keyof NoteKeys; + uniqueId: string; + auxId: string | null; data: { text: string; useCw: boolean; @@ -17,28 +21,83 @@ export type NoteDraft = { }; }; -export function getAll() { - const drafts = miLocalStorage.getItem('drafts'); +type NoteKeys = { + note: () => unknown, + reply: (replyId: string) => unknown, + quote: (renoteId: string) => unknown, + channel: (channelId: string) => unknown, +} + +export async function migrate(userId: string) { + const raw = miLocalStorage.getItem('drafts'); + if (!raw) return; + + const drafts = JSON.parse(raw) as Record; + const newDrafts: Record = {}; + + for (let i = 0; i < Object.keys(drafts).length; i++) { + const key = Object.keys(drafts)[i]; + const [type, id] = key.split(':'); + if (type === 'note' && id !== userId) continue; + const keyType = type === 'renote' ? 'quote' : type as keyof NoteKeys; + const keyId = type === 'note' ? null : id; + const uniqueId = Date.now().toString() + i.toString(); + const newKey = getKey(keyType, uniqueId, false, keyId as string); + newDrafts[newKey] = { + ...drafts[key], + uniqueId, + type: keyType, + auxId: keyId, + }; + delete drafts[key]; + } + + await idbSet(`drafts::${userId}`, JSON.stringify(newDrafts)); + miLocalStorage.setItem('drafts', JSON.stringify(drafts)); +} + +function getKey(type: T, uniqueId: string | null, withUniqueId: U, ...args: Parameters): U extends true ? { uniqueId: string, key: string } : string { + const id = uniqueId ?? Date.now(); + let key = `${type}:${id}`; + for (const arg of args) { + if (arg != null) key += `:${arg}`; + } + + if (withUniqueId) { + return { uniqueId: id, key } as any; + } else { + return key as any; + } +} + +export async function getAll(userId: string) { + const drafts = await idbGet(`drafts::${userId}`); if (!drafts) return {}; return JSON.parse(drafts) as Record; } -export function get(key: string) { - const draft = getAll()[key]; +export async function get(type: T, userId: string, uniqueId: string | null, ...args: Parameters) { + const key = getKey(type, uniqueId, false, ...args); + const draft = await getAll(userId)[key]; return draft ?? null; } -export function set(key: string, draft: NoteDraft['data']) { - const drafts = getAll(); - drafts[key] = { +export async function set(type: T, userId: string, uniqueId: string | null, draft: NoteDraft['data'], ...args: Parameters) { + const drafts = await getAll(userId); + const keys = getKey(type, uniqueId, true, ...args); + drafts[keys.key] = { updatedAt: new Date(), + type, + uniqueId: uniqueId ?? keys.uniqueId, + auxId: args[0] ?? null, data: draft, }; - miLocalStorage.setItem('drafts', JSON.stringify(drafts)); + await idbSet(`drafts::${userId}`, JSON.stringify(drafts)); } -export function remove(key: string) { - const drafts = getAll(); +export async function remove(type: T, userId: string, uniqueId: string | null, ...args: Parameters) { + const drafts = await getAll(userId); + const key = getKey(type, uniqueId, false, ...args); delete drafts[key]; - miLocalStorage.setItem('drafts', JSON.stringify(drafts)); + await idbSet(`drafts::${userId}`, JSON.stringify(drafts)); }