diff --git a/keyserver/src/updaters/thread-updaters.js b/keyserver/src/updaters/thread-updaters.js --- a/keyserver/src/updaters/thread-updaters.js +++ b/keyserver/src/updaters/thread-updaters.js @@ -6,6 +6,7 @@ roleIsAdminRole, viewerIsMember, getThreadTypeParentRequirement, + validChatNameRegex, } from 'lib/shared/thread-utils.js'; import { hasMinCodeVersion } from 'lib/shared/version-utils.js'; import type { Shape } from 'lib/types/core.js'; @@ -333,6 +334,9 @@ const untrimmedName = request.changes.name; if (untrimmedName !== undefined && untrimmedName !== null) { const name = firstLine(untrimmedName); + if (name.search(validChatNameRegex) === -1) { + throw new ServerError('invalid_chat_name'); + } changedFields.name = name; sqlUpdate.name = name ?? null; } 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 @@ -90,6 +90,12 @@ import { firstLine } from '../utils/string-utils.js'; import { trimText } from '../utils/text-utils.js'; +const chatNameMaxLength = 191; +const chatNameMinLength = 0; +const secondCharRange = `{${chatNameMinLength},${chatNameMaxLength}}`; +const validChatNameRegexString = `^.${secondCharRange}$`; +const validChatNameRegex: RegExp = new RegExp(validChatNameRegexString); + function colorIsDark(color: string): boolean { return tinycolor(`#${color}`).isDark(); } @@ -1530,4 +1536,6 @@ getAvailableThreadMemberActions, selectedThreadColors, threadMembersWithoutAddedAshoat, + validChatNameRegex, + chatNameMaxLength, }; diff --git a/native/chat/settings/thread-settings-name.react.js b/native/chat/settings/thread-settings-name.react.js --- a/native/chat/settings/thread-settings-name.react.js +++ b/native/chat/settings/thread-settings-name.react.js @@ -15,6 +15,7 @@ changeThreadSettings, } from 'lib/actions/thread-actions.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; +import { chatNameMaxLength } from 'lib/shared/thread-utils.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; import { type ResolvedThreadInfo, @@ -108,6 +109,7 @@ editable={this.props.loadingStatus !== 'loading'} ref={this.textInputRef} selectionColor={`#${this.props.threadInfo.color}`} + maxLength={chatNameMaxLength} /> {button} diff --git a/web/modals/input.react.js b/web/modals/input.react.js --- a/web/modals/input.react.js +++ b/web/modals/input.react.js @@ -19,6 +19,7 @@ ...BaseInputProps, +type: string, +placeholder: string, + +maxLength?: number, }; function Input(props: InputProps, ref): React.Node { diff --git a/web/modals/threads/settings/thread-settings-general-tab.react.js b/web/modals/threads/settings/thread-settings-general-tab.react.js --- a/web/modals/threads/settings/thread-settings-general-tab.react.js +++ b/web/modals/threads/settings/thread-settings-general-tab.react.js @@ -7,7 +7,10 @@ changeThreadSettingsActionTypes, changeThreadSettings, } from 'lib/actions/thread-actions.js'; -import { threadHasPermission } from 'lib/shared/thread-utils.js'; +import { + threadHasPermission, + chatNameMaxLength, +} from 'lib/shared/thread-utils.js'; import { type SetState } from 'lib/types/hook-types.js'; import { type ThreadInfo, @@ -153,6 +156,7 @@