diff --git a/public/locales/en.json b/public/locales/en.json index 706d15e0..e2424b04 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -369,11 +369,12 @@ "no_direct_messages_desc": "You do not have any direct messages yet.", "direct_message": "Direct Message", "create_chat": "Create Chat", - "create_chat_subtitle": "Start a private, encrypted chat by entering a user ID.", + "create_chat_subtitle": "Start a private, encrypted chat by entering a username.", "chats": "Chats", - "user_id": "User ID", - "user_id_placeholder": "@username:server", - "invalid_user_id": "Please enter a valid User ID.", + "username": "Username", + "username_placeholder": "username", + "server": "Server", + "invalid_user_id": "Please enter a valid username and server.", "options": "Options", "e2e_encryption": "End-to-End Encryption", "e2e_encryption_desc": "Once this feature is enabled, it can't be disabled after the room is created.", diff --git a/public/locales/ru.json b/public/locales/ru.json index f378479f..92436e1e 100644 --- a/public/locales/ru.json +++ b/public/locales/ru.json @@ -369,11 +369,12 @@ "no_direct_messages_desc": "У вас ещё нет личных сообщений.", "direct_message": "Новый чат", "create_chat": "Новый чат", - "create_chat_subtitle": "Начните приватный зашифрованный чат, указав ID пользователя.", + "create_chat_subtitle": "Начните приватный зашифрованный чат, указав имя пользователя.", "chats": "Чаты", - "user_id": "ID пользователя", - "user_id_placeholder": "@username:server", - "invalid_user_id": "Введите корректный ID пользователя.", + "username": "Имя пользователя", + "username_placeholder": "username", + "server": "Сервер", + "invalid_user_id": "Введите корректные имя пользователя и сервер.", "options": "Параметры", "e2e_encryption": "Сквозное шифрование", "e2e_encryption_desc": "После включения эту функцию нельзя отключить после создания комнаты.", diff --git a/src/app/features/create-chat/CreateChat.tsx b/src/app/features/create-chat/CreateChat.tsx index 377e2bba..a98b2a8f 100644 --- a/src/app/features/create-chat/CreateChat.tsx +++ b/src/app/features/create-chat/CreateChat.tsx @@ -1,11 +1,30 @@ -import { Box, Button, color, config, Icon, Icons, Input, Spinner, Switch, Text } from 'folds'; +import { + Box, + Button, + color, + config, + Icon, + Icons, + Input, + Spinner, + Switch, + Text, + toRem, +} from 'folds'; import React, { FormEventHandler, useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ICreateRoomStateEvent, MatrixError, Preset, Visibility } from 'matrix-js-sdk'; import { useNavigate } from 'react-router-dom'; import { SettingTile } from '../../components/setting-tile'; import { SequenceCard } from '../../components/sequence-card'; -import { addRoomIdToMDirect, getDMRoomFor, isUserId } from '../../utils/matrix'; +import { + addRoomIdToMDirect, + getDMRoomFor, + getMxIdLocalPart, + getMxIdServer, + isServerName, + isUserId, +} from '../../utils/matrix'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; import { ErrorCode } from '../../cs-errorcode'; @@ -14,6 +33,8 @@ import { createRoomEncryptionState } from '../../components/create-room'; import { useAlive } from '../../hooks/useAlive'; import { getDirectRoomPath } from '../../pages/pathUtils'; +const FALLBACK_SERVER = 'vojo.chat'; + type CreateChatProps = { defaultUserId?: string; }; @@ -23,6 +44,10 @@ export function CreateChat({ defaultUserId }: CreateChatProps) { const alive = useAlive(); const navigate = useNavigate(); + const userServer = getMxIdServer(mx.getSafeUserId()) ?? FALLBACK_SERVER; + const defaultUsername = defaultUserId ? getMxIdLocalPart(defaultUserId) : undefined; + const defaultServer = defaultUserId ? getMxIdServer(defaultUserId) : undefined; + const [encryption, setEncryption] = useState(false); const [invalidUserId, setInvalidUserId] = useState(false); @@ -57,28 +82,42 @@ export function CreateChat({ defaultUserId }: CreateChatProps) { setInvalidUserId(false); const target = evt.target as HTMLFormElement | undefined; - const userIdInput = target?.userIdInput as HTMLInputElement | undefined; - const userId = userIdInput?.value.trim(); + const usernameInput = target?.usernameInput as HTMLInputElement | undefined; + const serverInput = target?.serverInput as HTMLInputElement | undefined; + let rawUsername = usernameInput?.value.trim() ?? ''; + const server = serverInput?.value.trim() || userServer; - if (!userIdInput || !userId) return; - if (!isUserId(userId)) { + if (!usernameInput || !rawUsername) return; + + // If user pasted a full MXID like @alice:matrix.org, split it into fields + if (isUserId(rawUsername)) { + const parsedLocal = getMxIdLocalPart(rawUsername); + const parsedServer = getMxIdServer(rawUsername); + if (parsedLocal && parsedServer) { + rawUsername = parsedLocal; + usernameInput.value = parsedLocal; + if (serverInput) serverInput.value = parsedServer; + } + } + + const username = rawUsername.replace(/^@/, ''); + if (!username || /[@:\s]/.test(username) || !isServerName(server)) { setInvalidUserId(true); return; } - // Route to an existing DM (encrypted or not) before creating a new room; - // otherwise every submit from the card's "Message" button creates a new - // duplicate room even when a perfectly fine DM already exists. + const userId = `@${username}:${server}`; + const existing = getDMRoomFor(mx, userId); if (existing) { - userIdInput.value = ''; + usernameInput.value = ''; navigate(getDirectRoomPath(existing.roomId)); return; } create(userId, encryption).then((roomId) => { if (alive()) { - userIdInput.value = ''; + usernameInput.value = ''; navigate(getDirectRoomPath(roomId)); } }); @@ -87,19 +126,36 @@ export function CreateChat({ defaultUserId }: CreateChatProps) { return ( - {t('Direct.user_id')} - + + + {t('Direct.username')} + + + + {t('Direct.server')} + + + {invalidUserId && ( diff --git a/src/app/pages/client/direct/DirectCreate.tsx b/src/app/pages/client/direct/DirectCreate.tsx index ea09bbfd..0e119a22 100644 --- a/src/app/pages/client/direct/DirectCreate.tsx +++ b/src/app/pages/client/direct/DirectCreate.tsx @@ -61,9 +61,7 @@ export function DirectCreate() { } title={t('Direct.create_chat')} - subTitle={t('Direct.create_chat_subtitle')} />