diff --git a/lib/types/dm-ops.js b/lib/types/dm-ops.js --- a/lib/types/dm-ops.js +++ b/lib/types/dm-ops.js @@ -25,7 +25,15 @@ } from './thread-types.js'; import type { ClientUpdateInfo } from './update-types.js'; import { values } from '../utils/objects.js'; -import { tColor, tShape, tString, tUserID } from '../utils/validation-utils.js'; +import { + tColor, + thickIDRegex, + tRegex, + tShape, + tString, + tUserID, + uuidRegex, +} from '../utils/validation-utils.js'; export const dmOperationTypes = Object.freeze({ CREATE_THREAD: 'create_thread', @@ -48,6 +56,14 @@ }); export type DMOperationType = $Values; +const tThickID = tRegex(thickIDRegex); + +// In CHANGE_THREAD_SETTINGS operation we're generating message IDs +// based on the prefix that is tThickID. A message with the generated ID +// can be used as a target message of the edit, reaction, or a sidebar. +const thickTargetMessageIDRegex = new RegExp(`^${uuidRegex}(?:/\\w+)?$`); +const tThickTargetMessageID = tRegex(thickTargetMessageIDRegex); + type MemberIDWithSubscription = { +id: string, +subscription: ThreadSubscription, @@ -78,20 +94,20 @@ }; export const createThickRawThreadInfoInputValidator: TInterface = tShape({ - threadID: t.String, + threadID: tThickID, threadType: thickThreadTypeValidator, creationTime: t.Number, - parentThreadID: t.maybe(t.String), + parentThreadID: t.maybe(tThickID), allMemberIDsWithSubscriptions: t.list(memberIDWithSubscriptionValidator), - roleID: t.String, + roleID: tThickID, unread: t.Boolean, timestamps: threadTimestampsValidator, name: t.maybe(t.String), avatar: t.maybe(clientAvatarValidator), description: t.maybe(t.String), color: t.maybe(t.String), - containingThreadID: t.maybe(t.String), - sourceMessageID: t.maybe(t.String), + containingThreadID: t.maybe(tThickID), + sourceMessageID: t.maybe(tThickTargetMessageID), repliesCount: t.maybe(t.Number), pinnedCount: t.maybe(t.Number), }); @@ -109,13 +125,13 @@ export const dmCreateThreadOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.CREATE_THREAD), - threadID: t.String, + threadID: tThickID, creatorID: tUserID, time: t.Number, threadType: t.enums.of(values(nonSidebarThickThreadTypes)), memberIDs: t.list(tUserID), - roleID: t.String, - newMessageID: t.String, + roleID: tThickID, + newMessageID: tThickID, }); export type DMCreateSidebarOperation = { @@ -133,15 +149,15 @@ export const dmCreateSidebarOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.CREATE_SIDEBAR), - threadID: t.String, + threadID: tThickID, creatorID: tUserID, time: t.Number, - parentThreadID: t.String, + parentThreadID: tThickID, memberIDs: t.list(tUserID), - sourceMessageID: t.String, - roleID: t.String, - newSidebarSourceMessageID: t.String, - newCreateSidebarMessageID: t.String, + sourceMessageID: tThickTargetMessageID, + roleID: tThickID, + newSidebarSourceMessageID: tThickID, + newCreateSidebarMessageID: tThickID, }); export type DMSendTextMessageOperation = { @@ -155,10 +171,10 @@ export const dmSendTextMessageOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.SEND_TEXT_MESSAGE), - threadID: t.String, + threadID: tThickID, creatorID: tUserID, time: t.Number, - messageID: t.String, + messageID: tThickID, text: t.String, }); @@ -173,10 +189,10 @@ export const dmSendMultimediaMessageOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.SEND_MULTIMEDIA_MESSAGE), - threadID: t.String, + threadID: tThickID, creatorID: tUserID, time: t.Number, - messageID: t.String, + messageID: tThickID, media: t.list(mediaValidator), }); @@ -193,11 +209,11 @@ export const dmSendReactionMessageOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.SEND_REACTION_MESSAGE), - threadID: t.String, + threadID: tThickID, creatorID: tUserID, time: t.Number, - messageID: t.String, - targetMessageID: t.String, + messageID: tThickID, + targetMessageID: tThickTargetMessageID, reaction: t.String, action: t.enums.of(['add_reaction', 'remove_reaction']), }); @@ -214,11 +230,11 @@ export const dmSendEditMessageOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.SEND_EDIT_MESSAGE), - threadID: t.String, + threadID: tThickID, creatorID: tUserID, time: t.Number, - messageID: t.String, - targetMessageID: t.String, + messageID: tThickID, + targetMessageID: tThickTargetMessageID, text: t.String, }); @@ -242,8 +258,8 @@ export const dmAddMembersOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.ADD_MEMBERS), - threadID: t.String, - messageID: t.String, + threadID: tThickID, + messageID: tThickID, ...dmAddMembersBaseValidatorShape, }); @@ -256,7 +272,7 @@ export const dmAddViewerToThreadMembersValidator: TInterface = tShape({ type: tString(dmOperationTypes.ADD_VIEWER_TO_THREAD_MEMBERS), - messageID: t.maybe(t.String), + messageID: t.maybe(tThickID), existingThreadDetails: createThickRawThreadInfoInputValidator, ...dmAddMembersBaseValidatorShape, }); @@ -273,7 +289,7 @@ type: tString(dmOperationTypes.JOIN_THREAD), joinerID: tUserID, time: t.Number, - messageID: t.String, + messageID: tThickID, existingThreadDetails: createThickRawThreadInfoInputValidator, }); @@ -289,8 +305,8 @@ type: tString(dmOperationTypes.LEAVE_THREAD), editorID: tUserID, time: t.Number, - messageID: t.String, - threadID: t.String, + messageID: tThickID, + threadID: tThickID, }); export type DMThreadSettingsChanges = { @@ -311,7 +327,7 @@ export const dmChangeThreadSettingsOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.CHANGE_THREAD_SETTINGS), - threadID: t.String, + threadID: tThickID, editorID: tUserID, time: t.Number, changes: tShape({ @@ -320,7 +336,7 @@ color: t.maybe(tColor), avatar: t.maybe(clientAvatarValidator), }), - messageIDsPrefix: t.String, + messageIDsPrefix: tThickID, }); export type DMChangeThreadSubscriptionOperation = { @@ -334,7 +350,7 @@ tShape({ type: tString(dmOperationTypes.CHANGE_THREAD_SUBSCRIPTION), time: t.Number, - threadID: t.String, + threadID: tThickID, creatorID: tUserID, subscription: threadSubscriptionValidator, }); @@ -350,7 +366,7 @@ tShape({ type: tString(dmOperationTypes.CHANGE_THREAD_READ_STATUS), time: t.Number, - threadID: t.String, + threadID: tThickID, creatorID: tUserID, unread: t.Boolean, }); @@ -372,13 +388,13 @@ export const dmCreateEntryOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.CREATE_ENTRY), - threadID: t.String, + threadID: tThickID, creatorID: tUserID, time: t.Number, - entryID: t.String, + entryID: tThickID, entryDate: t.String, text: t.String, - messageID: t.String, + messageID: tThickID, }); export type DMDeleteEntryOperation = { @@ -395,14 +411,14 @@ export const dmDeleteEntryOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.DELETE_ENTRY), - threadID: t.String, + threadID: tThickID, creatorID: tUserID, time: t.Number, creationTime: t.Number, - entryID: t.String, + entryID: tThickID, entryDate: t.String, prevText: t.String, - messageID: t.String, + messageID: tThickID, }); export type DMEditEntryOperation = { @@ -419,14 +435,14 @@ export const dmEditEntryOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.EDIT_ENTRY), - threadID: t.String, + threadID: tThickID, creatorID: tUserID, creationTime: t.Number, time: t.Number, - entryID: t.String, + entryID: tThickID, entryDate: t.String, text: t.String, - messageID: t.String, + messageID: tThickID, }); export type DMUpdateRelationshipOperation = { @@ -441,7 +457,7 @@ export const dmUpdateRelationshipOperationValidator: TInterface = tShape({ type: tString(dmOperationTypes.UPDATE_RELATIONSHIP), - threadID: t.String, + threadID: tThickID, creatorID: tUserID, time: t.Number, operation: t.enums.of([ @@ -450,7 +466,7 @@ 'farcaster_mutual', ]), targetUserID: tUserID, - messageID: t.String, + messageID: tThickID, }); export type DMOperation = diff --git a/lib/utils/validation-utils.js b/lib/utils/validation-utils.js --- a/lib/utils/validation-utils.js +++ b/lib/utils/validation-utils.js @@ -115,7 +115,7 @@ const pendingSidebarURLPrefix = 'sidebar'; const pendingThickSidebarURLPrefix = 'dm_sidebar'; const pendingThreadIDRegex = `pending/(type[0-9]+/[0-9]+(\\+[0-9]+)*|(${pendingSidebarURLPrefix}|${pendingThickSidebarURLPrefix})/${idSchemaRegex})`; -const thickThreadIDRegex: RegExp = new RegExp(`^${uuidRegex}$`); +const thickIDRegex: RegExp = new RegExp(`^${uuidRegex}$`); const chatNameMaxLength = 191; const chatNameMinLength = 0; @@ -147,11 +147,12 @@ tMediaMessageMedia, assertWithValidator, ashoatKeyserverID, + uuidRegex, idSchemaRegex, pendingSidebarURLPrefix, pendingThickSidebarURLPrefix, pendingThreadIDRegex, - thickThreadIDRegex, + thickIDRegex, validChatNameRegex, validChatNameRegexString, chatNameMaxLength, diff --git a/web/redux/initial-state-gate.js b/web/redux/initial-state-gate.js --- a/web/redux/initial-state-gate.js +++ b/web/redux/initial-state-gate.js @@ -17,7 +17,7 @@ import { entries, values } from 'lib/utils/objects.js'; import { useDispatch } from 'lib/utils/redux-utils.js'; import { infoFromURL } from 'lib/utils/url-utils.js'; -import { thickThreadIDRegex } from 'lib/utils/validation-utils.js'; +import { thickIDRegex } from 'lib/utils/validation-utils.js'; import { setInitialReduxState, @@ -58,7 +58,7 @@ try { let urlInfo = infoFromURL(decodeURI(window.location.href)); const isThickThreadOpen = - urlInfo.thread && thickThreadIDRegex.test(urlInfo.thread); + urlInfo.thread && thickIDRegex.test(urlInfo.thread); // Handle older links if (urlInfo.thread && !isThickThreadOpen) { urlInfo = {