From 45a3064153ad299f6a4b6b377a9b281d5f92bf0d Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sat, 12 Jun 2021 17:20:19 +0300 Subject: [PATCH] More RTL support (#1071) --- src/api/gramjs/apiBuilders/chats.ts | 20 ++--- src/components/common/Audio.scss | 24 ++++++ src/components/common/Audio.tsx | 6 +- src/components/common/DeleteChatModal.tsx | 2 +- src/components/common/EmbeddedMessage.scss | 18 ++++- src/components/common/EmbeddedMessage.tsx | 2 +- src/components/common/File.scss | 10 ++- src/components/common/File.tsx | 2 +- src/components/common/GroupChatInfo.tsx | 4 +- src/components/common/PasswordForm.tsx | 8 +- src/components/common/Picker.tsx | 2 +- src/components/common/PickerSelectedItem.scss | 25 ++++++ src/components/common/PickerSelectedItem.tsx | 1 + src/components/common/PrivateChatInfo.tsx | 4 +- src/components/common/SafeLink.tsx | 3 + src/components/common/TypingStatus.scss | 13 ++- src/components/common/TypingStatus.tsx | 7 +- .../common/UnpinAllMessagesModal.tsx | 1 + src/components/common/WebLink.scss | 15 ++++ src/components/common/WebLink.tsx | 11 ++- .../common/helpers/renderMessageText.tsx | 1 + src/components/left/ConnectionState.scss | 4 +- src/components/left/ConnectionState.tsx | 2 +- src/components/left/main/Chat.scss | 35 +++++++- src/components/left/main/Chat.tsx | 5 +- src/components/left/main/ChatFolders.tsx | 2 +- src/components/left/main/LeftMainHeader.scss | 5 ++ src/components/left/search/AudioResults.tsx | 4 +- src/components/left/search/ChatMessage.scss | 8 +- src/components/left/search/ChatResults.tsx | 8 +- src/components/left/search/LeftSearch.scss | 47 +++++++++++ src/components/left/search/LeftSearch.tsx | 8 +- src/components/left/search/LinkResults.tsx | 5 +- src/components/left/search/MediaResults.tsx | 3 +- .../left/search/RecentContacts.scss | 9 ++- src/components/left/search/RecentContacts.tsx | 7 +- src/components/left/settings/Settings.scss | 27 +++++++ .../left/settings/SettingsEditProfile.tsx | 8 +- .../left/settings/SettingsGeneral.tsx | 16 ++-- .../SettingsGeneralBackgroundColor.scss | 6 ++ .../left/settings/SettingsHeader.tsx | 4 +- .../left/settings/SettingsLanguage.tsx | 1 + src/components/left/settings/SettingsMain.tsx | 2 +- .../left/settings/SettingsNotifications.tsx | 10 ++- .../left/settings/SettingsPrivacy.tsx | 2 +- .../SettingsPrivacyActiveSessions.tsx | 18 +++-- .../settings/SettingsPrivacyBlockedUsers.tsx | 4 +- .../settings/SettingsPrivacyVisibility.tsx | 10 +-- .../left/settings/SettingsStickerSet.scss | 7 ++ .../left/settings/SettingsStickerSet.tsx | 1 + .../settings/folders/SettingsFolders.scss | 13 +++ .../folders/SettingsFoldersChatsPicker.scss | 9 +++ .../folders/SettingsFoldersChatsPicker.tsx | 8 +- .../settings/folders/SettingsFoldersEdit.tsx | 8 +- .../settings/folders/SettingsFoldersMain.tsx | 10 ++- .../twoFa/SettingsTwoFaCongratulations.tsx | 4 +- .../settings/twoFa/SettingsTwoFaEnabled.tsx | 4 +- src/components/main/ForwardPicker.tsx | 2 +- src/components/mediaViewer/MediaViewer.scss | 12 +++ src/components/mediaViewer/MediaViewer.tsx | 4 +- .../mediaViewer/MediaViewerActions.scss | 6 +- src/components/mediaViewer/SenderInfo.scss | 2 +- src/components/middle/AudioPlayer.tsx | 2 +- src/components/middle/HeaderPinnedMessage.tsx | 2 +- src/components/middle/MiddleColumn.scss | 4 +- src/components/middle/MiddleColumn.tsx | 2 +- src/components/middle/MiddleHeader.scss | 10 ++- src/components/middle/MiddleHeader.tsx | 2 + src/components/middle/ScrollDownButton.tsx | 4 +- src/components/middle/composer/AttachMenu.tsx | 6 +- .../middle/composer/AttachmentModal.tsx | 2 +- src/components/middle/composer/Composer.scss | 5 ++ src/components/middle/composer/Composer.tsx | 8 +- .../middle/composer/EmojiCategory.tsx | 1 + .../middle/composer/EmojiPicker.tsx | 2 +- .../middle/composer/MentionTooltip.scss | 11 ++- .../middle/composer/MessageInput.tsx | 6 +- .../middle/composer/SymbolMenuFooter.tsx | 6 +- .../middle/message/CommentButton.scss | 23 ++++-- .../middle/message/CommentButton.tsx | 3 +- src/components/middle/message/Message.tsx | 8 +- .../middle/message/MessageContextMenu.tsx | 8 +- src/components/middle/message/MessageMeta.tsx | 2 +- src/components/middle/message/Poll.tsx | 20 ++--- src/components/middle/message/PollOption.scss | 2 +- src/components/middle/message/PollOption.tsx | 4 +- src/components/middle/message/WebPage.scss | 15 ++++ .../middle/message/_message-content.scss | 33 +++++--- src/components/payment/PaymentModal.scss | 2 + src/components/payment/PaymentModal.tsx | 2 +- src/components/payment/ReceiptModal.tsx | 2 +- src/components/right/GifSearch.tsx | 2 +- src/components/right/PollAnswerResults.scss | 25 +++++- src/components/right/PollAnswerResults.tsx | 8 +- src/components/right/PollResults.tsx | 4 +- src/components/right/Profile.scss | 6 ++ src/components/right/Profile.tsx | 10 ++- src/components/right/ProfileInfo.scss | 28 +++++++ src/components/right/ProfileInfo.tsx | 8 +- src/components/right/RightSearch.tsx | 5 +- src/components/right/StickerSearch.scss | 11 +++ src/components/right/StickerSearch.tsx | 7 +- src/components/right/StickerSetResult.tsx | 5 +- .../management/ManageChatAdministrators.tsx | 2 +- .../management/ManageChatPrivacyType.tsx | 8 +- .../right/management/ManageGroup.tsx | 12 ++- .../management/ManageGroupAdminRights.tsx | 4 +- .../management/ManageGroupPermissions.tsx | 4 +- .../management/ManageGroupRecentActions.tsx | 8 +- .../management/ManageGroupRemovedUsers.tsx | 2 +- .../management/ManageGroupUserPermissions.tsx | 2 +- .../right/management/ManageUser.tsx | 4 +- .../right/management/Management.scss | 12 +++ src/components/ui/Button.scss | 10 +++ src/components/ui/Button.tsx | 6 +- src/components/ui/Checkbox.scss | 25 ++++++ src/components/ui/Checkbox.tsx | 4 +- src/components/ui/InputText.tsx | 4 +- src/components/ui/Link.tsx | 7 +- src/components/ui/ListItem.scss | 68 +++++++++++++++- src/components/ui/ListItem.tsx | 4 + src/components/ui/MenuItem.scss | 17 ++++ src/components/ui/MenuItem.tsx | 4 + src/components/ui/Modal.scss | 8 +- src/components/ui/Radio.scss | 30 +++++++ src/components/ui/Radio.tsx | 9 ++- src/components/ui/RadioGroup.tsx | 1 + src/components/ui/RangeSlider.scss | 7 ++ src/components/ui/RangeSlider.tsx | 8 +- src/components/ui/SearchInput.scss | 28 +++++++ src/components/ui/SearchInput.tsx | 5 +- src/components/ui/ShowMoreButton.tsx | 5 ++ src/components/ui/Tab.scss | 2 +- src/components/ui/TabList.tsx | 6 +- src/components/ui/Transition.scss | 33 ++++++++ src/components/ui/Transition.tsx | 5 +- src/config.ts | 2 +- src/modules/actions/api/initial.ts | 5 +- src/styles/_forms.scss | 44 ++++++++++ src/styles/index.scss | 3 +- src/types/index.ts | 5 +- src/util/langProvider.ts | 80 +++++++++++-------- 142 files changed, 1075 insertions(+), 263 deletions(-) diff --git a/src/api/gramjs/apiBuilders/chats.ts b/src/api/gramjs/apiBuilders/chats.ts index 6b74ec08a..5c5f389d4 100644 --- a/src/api/gramjs/apiBuilders/chats.ts +++ b/src/api/gramjs/apiBuilders/chats.ts @@ -319,29 +319,29 @@ export function buildChatTypingStatus( if (update.action instanceof GramJs.SendMessageCancelAction) { return undefined; } else if (update.action instanceof GramJs.SendMessageTypingAction) { - action = 'typing'; + action = 'lng_user_typing'; } else if (update.action instanceof GramJs.SendMessageRecordVideoAction) { - action = 'recording a video'; + action = 'lng_send_action_record_video'; } else if (update.action instanceof GramJs.SendMessageUploadVideoAction) { - action = 'uploading a video'; + action = 'lng_send_action_upload_video'; } else if (update.action instanceof GramJs.SendMessageRecordAudioAction) { - action = 'recording a voice message'; + action = 'lng_send_action_record_audio'; } else if (update.action instanceof GramJs.SendMessageUploadAudioAction) { - action = 'uploading a voice message'; + action = 'lng_send_action_upload_audio'; } else if (update.action instanceof GramJs.SendMessageUploadPhotoAction) { - action = 'uploading a photo'; + action = 'lng_send_action_upload_photo'; } else if (update.action instanceof GramJs.SendMessageUploadDocumentAction) { - action = 'uploading a file'; + action = 'lng_send_action_upload_file'; } else if (update.action instanceof GramJs.SendMessageGeoLocationAction) { action = 'selecting a location to share'; } else if (update.action instanceof GramJs.SendMessageChooseContactAction) { action = 'selecting a contact to share'; } else if (update.action instanceof GramJs.SendMessageGamePlayAction) { - action = 'playing a game'; + action = 'lng_playing_game'; } else if (update.action instanceof GramJs.SendMessageRecordRoundAction) { - action = 'recording a round video'; + action = 'lng_send_action_record_round'; } else if (update.action instanceof GramJs.SendMessageUploadRoundAction) { - action = 'uploading a round video'; + action = 'lng_send_action_upload_round'; } return { diff --git a/src/components/common/Audio.scss b/src/components/common/Audio.scss index 009bd4216..1ce020c73 100644 --- a/src/components/common/Audio.scss +++ b/src/components/common/Audio.scss @@ -232,10 +232,34 @@ top: 0.1875rem; left: 0.1875rem; } + + &[dir=rtl] { + .media-loading { + left: auto !important; + right: 0; + } + } } .ProgressSpinner.size-s svg { width: 2.25rem; height: 2.25rem; } + + &[dir=rtl] { + .toggle-play { + margin-left: .5rem; + margin-right: 0; + + &.smaller { + margin-left: .75rem; + margin-right: 0; + } + } + + .content, + .duration { + text-align: right; + } + } } diff --git a/src/components/common/Audio.tsx b/src/components/common/Audio.tsx index 1337c8d6f..c18874008 100644 --- a/src/components/common/Audio.tsx +++ b/src/components/common/Audio.tsx @@ -86,6 +86,7 @@ const Audio: FC = ({ const { content: { audio, voice }, isMediaUnread } = message; const isVoice = Boolean(voice); const isSeeking = useRef(false); + const lang = useLang(); const [isActivated, setIsActivated] = useState(false); const shouldDownload = (isActivated || PRELOAD) && lastSyncTime; @@ -177,8 +178,6 @@ const Audio: FC = ({ onDateClick!(message.id, message.chatId); }, [onDateClick, message.id, message.chatId]); - const lang = useLang(); - function getFirstLine() { if (isVoice) { return senderTitle || 'Voice'; @@ -264,7 +263,7 @@ const Audio: FC = ({ } return ( -
+
{isSelectable && (
{isSelected && } @@ -277,6 +276,7 @@ const Audio: FC = ({ className={buttonClassNames.join(' ')} ariaLabel={isPlaying ? 'Pause audio' : 'Play audio'} onClick={handleButtonClick} + isRtl={lang.isRtl} > diff --git a/src/components/common/DeleteChatModal.tsx b/src/components/common/DeleteChatModal.tsx index ecc03e11f..b343781de 100644 --- a/src/components/common/DeleteChatModal.tsx +++ b/src/components/common/DeleteChatModal.tsx @@ -90,7 +90,7 @@ const DeleteChatModal: FC = ({ function renderHeader() { return ( -
+
= ({ > {mediaThumbnail && renderPictogram(pictogramId, mediaThumbnail, mediaBlobUrl, isRoundVideo)}
-
{renderText(senderTitle || title || NBSP)}

{!message ? ( customText || NBSP @@ -73,6 +72,7 @@ const EmbeddedMessage: FC = ({ renderText(getMessageSummaryText(lang, message, Boolean(mediaThumbnail))) )}

+
{renderText(senderTitle || title || NBSP)}
); diff --git a/src/components/common/File.scss b/src/components/common/File.scss index adeefee33..6756fcb66 100644 --- a/src/components/common/File.scss +++ b/src/components/common/File.scss @@ -190,7 +190,15 @@ } } - &:dir(rtl) { + &:dir(rtl), + &[dir=rtl] { + .file-progress, + .file-icon, + .file-preview { + margin-left: .75rem; + margin-right: 0; + } + .file-info { text-align: right; diff --git a/src/components/common/File.tsx b/src/components/common/File.tsx index 7abaa4f94..7979f5174 100644 --- a/src/components/common/File.tsx +++ b/src/components/common/File.tsx @@ -82,7 +82,7 @@ const File: FC = ({ ); return ( -
+
{isSelectable && (
{isSelected && } diff --git a/src/components/common/GroupChatInfo.tsx b/src/components/common/GroupChatInfo.tsx index bc862762e..7c623e340 100644 --- a/src/components/common/GroupChatInfo.tsx +++ b/src/components/common/GroupChatInfo.tsx @@ -31,6 +31,7 @@ type OwnProps = { withFullInfo?: boolean; withUpdatingStatus?: boolean; withChatType?: boolean; + noRtl?: boolean; }; type StateProps = { @@ -49,6 +50,7 @@ const GroupChatInfo: FC = ({ withFullInfo, withUpdatingStatus, withChatType, + noRtl, chat, onlineCount, areMessagesLoaded, @@ -116,7 +118,7 @@ const GroupChatInfo: FC = ({ } return ( -
+
= ({ }) => { // eslint-disable-next-line no-null/no-null const inputRef = useRef(null); + const lang = useLang(); const [password, setPassword] = useState(''); const [canSubmit, setCanSubmit] = useState(false); @@ -90,7 +92,10 @@ const PasswordForm: FC = ({ return (
-
+
= ({ value={password || ''} autoComplete="current-password" onChange={onPasswordChange} + dir="auto" />
= ({ return (
-
+
{selectedIds.map((id, i) => ( = ({ className={fullClassName} onClick={() => onClick(clickArg)} title={isMinimized ? titleText : undefined} + dir={lang.isRtl ? 'rtl' : undefined} > {iconElement} {!isMinimized && ( diff --git a/src/components/common/PrivateChatInfo.tsx b/src/components/common/PrivateChatInfo.tsx index a0febe639..f8d7da3e2 100644 --- a/src/components/common/PrivateChatInfo.tsx +++ b/src/components/common/PrivateChatInfo.tsx @@ -29,6 +29,7 @@ type OwnProps = { withFullInfo?: boolean; withUpdatingStatus?: boolean; noStatusOrTyping?: boolean; + noRtl?: boolean; }; type StateProps = { @@ -48,6 +49,7 @@ const PrivateChatInfo: FC = ({ withFullInfo, withUpdatingStatus, noStatusOrTyping, + noRtl, user, isSavedMessages, areMessagesLoaded, @@ -110,7 +112,7 @@ const PrivateChatInfo: FC = ({ } return ( -
+
; @@ -21,6 +22,7 @@ const SafeLink: FC = ({ text, className, children, + isRtl, toggleSafeLinkModal, openTelegramLink, }) => { @@ -65,6 +67,7 @@ const SafeLink: FC = ({ rel="noopener noreferrer" className={classNames} onClick={handleClick} + dir={isRtl ? 'rtl' : 'auto'} > {content} diff --git a/src/components/common/TypingStatus.scss b/src/components/common/TypingStatus.scss index 317647420..4ec1cec77 100644 --- a/src/components/common/TypingStatus.scss +++ b/src/components/common/TypingStatus.scss @@ -4,7 +4,7 @@ .sender-name { &::after { - content: '\00a0is\00a0'; + content: '\00a0'; color: var(--color-text-secondary); } } @@ -17,6 +17,11 @@ &::after { content: '...'; animation: typing-animation 1s steps(4, start) infinite; + + html[lang=ar] &, + html[lang=fa] & { + animation-name: typing-animation-rtl; + } } } } @@ -26,3 +31,9 @@ transform: translateX(-1rem); } } + +@keyframes typing-animation-rtl { + from { + transform: translateX(1rem); + } +} diff --git a/src/components/common/TypingStatus.tsx b/src/components/common/TypingStatus.tsx index a2d31bc2a..915eb5322 100644 --- a/src/components/common/TypingStatus.tsx +++ b/src/components/common/TypingStatus.tsx @@ -6,6 +6,7 @@ import { ApiUser, ApiTypingStatus } from '../../api/types'; import { selectUser } from '../../modules/selectors'; import { getUserFirstOrLastName } from '../../modules/helpers'; import renderText from './helpers/renderText'; +import useLang from '../../hooks/useLang'; import './TypingStatus.scss'; @@ -18,14 +19,16 @@ type StateProps = { }; const TypingStatus: FC = ({ typingStatus, typingUser }) => { + const lang = useLang(); const typingUserName = typingUser && !typingUser.isSelf && getUserFirstOrLastName(typingUser); return ( -

+

{typingUserName && ( {renderText(typingUserName)} )} - {typingStatus.action} + {/* fix for translation "username _is_ typing" */} + {lang(typingStatus.action).replace('{user}', '').trim()}

); diff --git a/src/components/common/UnpinAllMessagesModal.tsx b/src/components/common/UnpinAllMessagesModal.tsx index 5f497ad26..cc983fca7 100644 --- a/src/components/common/UnpinAllMessagesModal.tsx +++ b/src/components/common/UnpinAllMessagesModal.tsx @@ -20,6 +20,7 @@ const UnpinAllMessagesModal: FC = ({ onUnpin, }) => { const lang = useLang(); + return ( = ({ message, senderTitle, onMessageClick }) => {
{photo && ( )}
- {renderText(title || siteName || displayUrl)} + + {renderText(title || siteName || displayUrl)} + {truncatedDescription && ( - {renderText(truncatedDescription)} + + {renderText(truncatedDescription)} + )} {url.replace('mailto:', '') || displayUrl} @@ -93,6 +99,7 @@ const WebLink: FC = ({ message, senderTitle, onMessageClick }) => { {formatPastTimeShort(lang, message.date * 1000)} diff --git a/src/components/common/helpers/renderMessageText.tsx b/src/components/common/helpers/renderMessageText.tsx index 4923ed5d9..f6d85bd37 100644 --- a/src/components/common/helpers/renderMessageText.tsx +++ b/src/components/common/helpers/renderMessageText.tsx @@ -287,6 +287,7 @@ function processEntity( {renderMessagePart(renderedContent)} diff --git a/src/components/left/ConnectionState.scss b/src/components/left/ConnectionState.scss index 195e3d773..86dfd6d7b 100644 --- a/src/components/left/ConnectionState.scss +++ b/src/components/left/ConnectionState.scss @@ -15,13 +15,13 @@ color: var(--color-text-lighter); font-weight: 500; line-height: 2rem; - margin-left: 1.9rem; + margin-inline-start: 1.875rem; white-space: nowrap; } @media (max-width: 950px) { > .state-text { - margin-left: 1.2rem; + margin-inline-start: 1.25rem; } } } diff --git a/src/components/left/ConnectionState.tsx b/src/components/left/ConnectionState.tsx index 0c348ddf6..2bdd7ecd4 100644 --- a/src/components/left/ConnectionState.tsx +++ b/src/components/left/ConnectionState.tsx @@ -18,7 +18,7 @@ const ConnectionState: FC = ({ connectionState }) => { const isConnecting = connectionState === 'connectionStateConnecting'; return isConnecting && ( -
+
{lang('WaitingForNetwork')}
diff --git a/src/components/left/main/Chat.scss b/src/components/left/main/Chat.scss index cc622197e..a9a404cff 100644 --- a/src/components/left/main/Chat.scss +++ b/src/components/left/main/Chat.scss @@ -94,11 +94,14 @@ } .last-message { - .sender-name, .draft { + .draft { &::after { content: ': '; } } + .colon { + margin-inline-end: .25rem; + } .media-preview { position: relative; @@ -110,7 +113,7 @@ object-fit: cover; border-radius: .125rem; vertical-align: -.25rem; - margin-right: .25rem; + margin-inline-end: .25rem; &.round { border-radius: .625rem; @@ -127,8 +130,34 @@ font-size: .75rem; color: #fff; position: absolute; - left: .25rem; top: .1875rem; + margin-inline-start: -1.25rem; + } + } + } + + &[dir=rtl] { + .info { + .LastMessageMeta { + margin-left: 0; + margin-right: auto; + } + + .title, .subtitle { + padding-left: .15rem; + padding-right: 0; + } + + .icon-muted-chat { + margin-left: 0; + margin-right: 0.25rem; + } + + .last-message, .typing-status { + padding-left: 0.5rem; + padding-right: 0; + text-align: right; + unicode-bidi: plaintext; } } } diff --git a/src/components/left/main/Chat.tsx b/src/components/left/main/Chat.tsx index 5bb3e279f..c1e46f5ce 100644 --- a/src/components/left/main/Chat.tsx +++ b/src/components/left/main/Chat.tsx @@ -228,7 +228,10 @@ const Chat: FC = ({ return (

{senderName && ( - {renderText(senderName)} + <> + {renderText(senderName)} + : + )} {renderMessageSummary(lang, lastMessage!, mediaBlobUrl || mediaThumbnail, isRoundVideo)}

diff --git a/src/components/left/main/ChatFolders.tsx b/src/components/left/main/ChatFolders.tsx index 949fd103f..0a46ca111 100644 --- a/src/components/left/main/ChatFolders.tsx +++ b/src/components/left/main/ChatFolders.tsx @@ -163,7 +163,7 @@ const ChatFolders: FC = ({ ) : undefined} diff --git a/src/components/left/main/LeftMainHeader.scss b/src/components/left/main/LeftMainHeader.scss index 734882088..c1b8da106 100644 --- a/src/components/left/main/LeftMainHeader.scss +++ b/src/components/left/main/LeftMainHeader.scss @@ -57,6 +57,11 @@ flex-shrink: 0; } + [dir=rtl] .archived-badge { + margin-left: 0; + margin-right: auto; + } + .Menu .bubble { min-width: 17rem; } diff --git a/src/components/left/search/AudioResults.tsx b/src/components/left/search/AudioResults.tsx index aa60b9980..c4e64875f 100644 --- a/src/components/left/search/AudioResults.tsx +++ b/src/components/left/search/AudioResults.tsx @@ -88,7 +88,9 @@ const AudioResults: FC = ({ key={message.id} > {shouldDrawDateDivider && ( -

{formatMonthAndYear(lang, new Date(message.date * 1000))}

+

+ {formatMonthAndYear(lang, new Date(message.date * 1000))} +

)}