diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js --- a/lib/shared/thread-utils.js +++ b/lib/shared/thread-utils.js @@ -1344,8 +1344,10 @@ return !!defaultRole.permissions[threadPermissions.VOICED]; } +const draftKeySuffix = '/message_composer'; + function draftKeyFromThreadID(threadID: string): string { - return `${threadID}/message_composer`; + return `${threadID}${draftKeySuffix}`; } function getContainingThreadID( @@ -1636,6 +1638,7 @@ useCanCreateSidebarFromMessage, useSidebarExistsOrCanBeCreated, checkIfDefaultMembersAreVoiced, + draftKeySuffix, draftKeyFromThreadID, threadTypeCanBePending, getContainingThreadID, diff --git a/lib/utils/migration-utils.js b/lib/utils/migration-utils.js new file mode 100644 --- /dev/null +++ b/lib/utils/migration-utils.js @@ -0,0 +1,68 @@ +// @flow + +import { entries } from './objects.js'; +import { keyserverPrefixID } from './validation-utils.js'; +import { + parsePendingThreadID, + getPendingThreadID, + draftKeySuffix, +} from '../shared/thread-utils.js'; +import type { + ClientDBDraftInfo, + ClientDBDraftStoreOperation, + DraftStore, +} from '../types/draft-types'; + +function convertDraftKeyToNewIDSchema(key: string): string { + const threadID = key.slice(0, -draftKeySuffix.length); + + const pendingIDContents = parsePendingThreadID(threadID); + + if (!pendingIDContents) { + return `${keyserverPrefixID}|${key}`; + } + + const { threadType, sourceMessageID, memberIDs } = pendingIDContents; + + if (!sourceMessageID) { + return key; + } + + const convertedThreadID = getPendingThreadID( + threadType, + memberIDs, + `${keyserverPrefixID}|${sourceMessageID}`, + ); + + return `${convertedThreadID}${draftKeySuffix}`; +} + +function convertDraftStoreToNewIDSchema(store: DraftStore): DraftStore { + return { + drafts: Object.fromEntries( + entries(store.drafts).map(([key, value]) => [ + convertDraftKeyToNewIDSchema(key), + value, + ]), + ), + }; +} + +function generateIDSchemaMigrationOpsForDrafts( + drafts: $ReadOnlyArray, +): $ReadOnlyArray { + const operations = drafts.map(draft => ({ + type: 'update', + payload: { + key: convertDraftKeyToNewIDSchema(draft.key), + text: draft.text, + }, + })); + return [{ type: 'remove_all' }, ...operations]; +} + +export { + convertDraftKeyToNewIDSchema, + convertDraftStoreToNewIDSchema, + generateIDSchemaMigrationOpsForDrafts, +}; diff --git a/lib/utils/migration-utils.test.js b/lib/utils/migration-utils.test.js new file mode 100644 --- /dev/null +++ b/lib/utils/migration-utils.test.js @@ -0,0 +1,87 @@ +// @flow + +import { + convertDraftKeyToNewIDSchema, + convertDraftStoreToNewIDSchema, + generateIDSchemaMigrationOpsForDrafts, +} from './migration-utils.js'; +import type { + ClientDBDraftInfo, + ClientDBDraftStoreOperation, + DraftStore, +} from '../types/draft-types.js'; + +describe('id schema migrations', () => { + it('should convert draft keys', () => { + const examples = [ + ['123/message_composer', '256|123/message_composer'], + [ + 'pending/sidebar/83874/message_composer', + 'pending/sidebar/256|83874/message_composer', + ], + [ + 'pending/type6/83811+84104/message_composer', + 'pending/type6/83811+84104/message_composer', + ], + ]; + + for (const [before, after] of examples) { + expect(convertDraftKeyToNewIDSchema(before)).toEqual(after); + } + }); + + it('should convert draft store', () => { + const exampleDraftStore: DraftStore = { + drafts: { + '83818/message_composer': 'draft', + 'pending/sidebar/83874/message_composer': 'draft', + 'pending/type6/83811+84104/message_composer': 'draft', + }, + }; + const convertedDraftStore: DraftStore = { + drafts: { + '256|83818/message_composer': 'draft', + 'pending/sidebar/256|83874/message_composer': 'draft', + 'pending/type6/83811+84104/message_composer': 'draft', + }, + }; + + expect(convertDraftStoreToNewIDSchema(exampleDraftStore)).toEqual( + convertedDraftStore, + ); + }); + + it('should generate migration ops for drafts', () => { + const drafts: $ReadOnlyArray = [ + { key: '83818/message_composer', text: 'draft' }, + { key: 'pending/sidebar/83874/message_composer', text: 'draft' }, + { key: 'pending/type6/83811+84104/message_composer', text: 'draft' }, + ]; + const operations: $ReadOnlyArray = [ + { type: 'remove_all' }, + { + payload: { + key: '256|83818/message_composer', + text: 'draft', + }, + type: 'update', + }, + { + payload: { + key: 'pending/sidebar/256|83874/message_composer', + text: 'draft', + }, + type: 'update', + }, + { + payload: { + key: 'pending/type6/83811+84104/message_composer', + text: 'draft', + }, + type: 'update', + }, + ]; + + expect(generateIDSchemaMigrationOpsForDrafts(drafts)).toEqual(operations); + }); +});