Localization: Add many translations (#1103)
This commit is contained in:
parent
d2a10a5b8b
commit
ffa338d667
@ -18,6 +18,7 @@ import { buildCollectionByKey } from '../../../util/iteratees';
|
|||||||
import localDb from '../localDb';
|
import localDb from '../localDb';
|
||||||
|
|
||||||
const MAX_INT_32 = 2 ** 31 - 1;
|
const MAX_INT_32 = 2 ** 31 - 1;
|
||||||
|
const BETA_LANG_CODES = ['ar', 'fa'];
|
||||||
|
|
||||||
export function updateProfile({
|
export function updateProfile({
|
||||||
firstName,
|
firstName,
|
||||||
@ -250,7 +251,10 @@ export async function fetchLanguages(): Promise<ApiLanguage[] | undefined> {
|
|||||||
|
|
||||||
export async function fetchLangPack({ sourceLangPacks, langCode }: { sourceLangPacks: string[]; langCode: string }) {
|
export async function fetchLangPack({ sourceLangPacks, langCode }: { sourceLangPacks: string[]; langCode: string }) {
|
||||||
const results = await Promise.all(sourceLangPacks.map((langPack) => {
|
const results = await Promise.all(sourceLangPacks.map((langPack) => {
|
||||||
return invokeRequest(new GramJs.langpack.GetLangPack({ langPack, langCode }));
|
return invokeRequest(new GramJs.langpack.GetLangPack({
|
||||||
|
langPack,
|
||||||
|
langCode: BETA_LANG_CODES.includes(langCode) ? `${langCode}-raw` : langCode,
|
||||||
|
}));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const collections = results
|
const collections = results
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import {
|
|||||||
} from '../../modules/helpers';
|
} from '../../modules/helpers';
|
||||||
import { pick } from '../../util/iteratees';
|
import { pick } from '../../util/iteratees';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import renderText from './helpers/renderText';
|
||||||
|
|
||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import Modal from '../ui/Modal';
|
import Modal from '../ui/Modal';
|
||||||
@ -96,55 +97,52 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
chat={chat}
|
chat={chat}
|
||||||
isSavedMessages={isChatWithSelf}
|
isSavedMessages={isChatWithSelf}
|
||||||
/>
|
/>
|
||||||
<h3 className="modal-title">{renderTitle()}</h3>
|
<h3 className="modal-title">{lang(renderTitle())}</h3>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTitle() {
|
function renderTitle() {
|
||||||
if (isChannel && !chat.isCreator) {
|
if (isChannel && !chat.isCreator) {
|
||||||
return 'Leave Channel?';
|
return 'LeaveChannel';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isChannel && chat.isCreator) {
|
if (isChannel && chat.isCreator) {
|
||||||
return 'Delete and Leave Channel?';
|
return 'ChannelDelete';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBasicGroup || isSuperGroup) {
|
if (isBasicGroup || isSuperGroup) {
|
||||||
return 'Leave Group?';
|
return 'Group.LeaveGroup';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Delete Chat?';
|
return 'DeleteChatUser';
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMessage() {
|
function renderMessage() {
|
||||||
if (isChannel && !chat.isCreator) {
|
|
||||||
return <p>Are you sure you want to leave channel <strong>{chatTitle}</strong>?</p>;
|
|
||||||
}
|
|
||||||
if (isChannel && chat.isCreator) {
|
if (isChannel && chat.isCreator) {
|
||||||
return <p>Are you sure you want to delete and leave channel <strong>{chatTitle}</strong>?</p>;
|
return <p>{renderText(lang('ChatList.DeleteAndLeaveGroupConfirmation', chatTitle), ['simple_markdown'])}</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBasicGroup || isSuperGroup) {
|
if ((isChannel && !chat.isCreator) || isBasicGroup || isSuperGroup) {
|
||||||
return <p>Are you sure you want to leave group <strong>{chatTitle}</strong>?</p>;
|
return <p>{renderText(lang('ChannelLeaveAlertWithName', chatTitle), ['simple_markdown'])}</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <p>Are you sure you want to delete chat with <strong>{contactName}</strong>?</p>;
|
return <p>{renderText(lang('ChatList.DeleteChatConfirmation', contactName), ['simple_markdown'])}</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderActionText() {
|
function renderActionText() {
|
||||||
if (isChannel && !chat.isCreator) {
|
if (isChannel && !chat.isCreator) {
|
||||||
return 'Leave Channel';
|
return 'LeaveChannel';
|
||||||
}
|
}
|
||||||
if (isChannel && chat.isCreator) {
|
if (isChannel && chat.isCreator) {
|
||||||
return 'Delete and Leave Channel';
|
return 'Chat.Input.Delete';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBasicGroup || isSuperGroup) {
|
if (isBasicGroup || isSuperGroup) {
|
||||||
return 'Leave Group';
|
return 'Group.LeaveGroup';
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Delete${canDeleteForAll ? ' just for me' : ''}`;
|
return canDeleteForAll ? 'ChatList.DeleteForCurrentUser' : 'Delete';
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -157,11 +155,11 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
{renderMessage()}
|
{renderMessage()}
|
||||||
{canDeleteForAll && (
|
{canDeleteForAll && (
|
||||||
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForAll}>
|
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForAll}>
|
||||||
Delete for {contactName ? `me and ${contactName}` : 'Everyone'}
|
{contactName ? lang('ChatList.DeleteForEveryone', contactName) : lang('DeleteForAll')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteChat}>
|
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteChat}>
|
||||||
{renderActionText()}
|
{lang(renderActionText())}
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>
|
<Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@ -91,19 +91,19 @@ const DeleteMessageModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
>
|
>
|
||||||
<p>{lang('AreYouSureDeleteSingleMessage')}</p>
|
<p>{lang('AreYouSureDeleteSingleMessage')}</p>
|
||||||
{willDeleteForCurrentUserOnly && (
|
{willDeleteForCurrentUserOnly && (
|
||||||
<p>This will delete it just for you, not for other participants in the chat.</p>
|
<p>{lang('lng_delete_for_me_chat_hint')}</p>
|
||||||
)}
|
)}
|
||||||
{willDeleteForAll && (
|
{willDeleteForAll && (
|
||||||
<p>This will delete it for everyone in this chat.</p>
|
<p>{lang('lng_delete_for_everyone_hint')}</p>
|
||||||
)}
|
)}
|
||||||
{canDeleteForAll && (
|
{canDeleteForAll && (
|
||||||
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForAll}>
|
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForAll}>
|
||||||
Delete for {contactName ? 'me and ' : 'Everyone'}
|
{contactName && lang('Conversation.DeleteMessagesFor', renderText(contactName))}
|
||||||
{contactName && renderText(contactName)}
|
{!contactName && lang('Conversation.DeleteMessagesForEveryone')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForSelf}>
|
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForSelf}>
|
||||||
Delete{canDeleteForAll ? ' just for me' : ''}
|
{lang(canDeleteForAll ? 'ChatList.DeleteForCurrentUser' : 'Delete')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>
|
<Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@ -49,7 +49,7 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
const pictogramId = message && `sticker-reply-thumb${message.id}`;
|
const pictogramId = message && `sticker-reply-thumb${message.id}`;
|
||||||
const mediaThumbnail = useWebpThumbnail(message);
|
const mediaThumbnail = useWebpThumbnail(message);
|
||||||
|
|
||||||
useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
const senderTitle = sender && getSenderTitle(sender);
|
const senderTitle = sender && getSenderTitle(sender);
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
) : isActionMessage(message) ? (
|
) : isActionMessage(message) ? (
|
||||||
<ActionMessage message={message} isEmbedded />
|
<ActionMessage message={message} isEmbedded />
|
||||||
) : (
|
) : (
|
||||||
renderText(getMessageSummaryText(message, Boolean(mediaThumbnail)))
|
renderText(getMessageSummaryText(lang, message, Boolean(mediaThumbnail)))
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,7 +4,19 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--color-text-meta);
|
color: var(--color-text-meta);
|
||||||
|
|
||||||
|
&.with-description {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.AnimatedSticker {
|
.AnimatedSticker {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: .875rem;
|
||||||
|
text-align: center;
|
||||||
|
margin: 1rem 0 0;
|
||||||
|
unicode-bidi: plaintext;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,21 +2,26 @@ import React, { FC, memo } from '../../lib/teact/teact';
|
|||||||
|
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import useShowTransition from '../../hooks/useShowTransition';
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
|
import renderText from './helpers/renderText';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
import './NothingFound.scss';
|
import './NothingFound.scss';
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
text?: string;
|
text?: string;
|
||||||
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_TEXT = 'Nothing found.';
|
const DEFAULT_TEXT = 'Nothing found.';
|
||||||
|
|
||||||
const NothingFound: FC<OwnProps> = ({ text = DEFAULT_TEXT }) => {
|
const NothingFound: FC<OwnProps> = ({ text = DEFAULT_TEXT, description }) => {
|
||||||
|
const lang = useLang();
|
||||||
const { transitionClassNames } = useShowTransition(true);
|
const { transitionClassNames } = useShowTransition(true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName('NothingFound', transitionClassNames)}>
|
<div className={buildClassName('NothingFound', transitionClassNames, description && 'with-description')}>
|
||||||
{text}
|
{text}
|
||||||
|
{description && <p className="description">{renderText(lang(description), ['br'])}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { STICKER_SIZE_MODAL } from '../../config';
|
|||||||
import { pick } from '../../util/iteratees';
|
import { pick } from '../../util/iteratees';
|
||||||
import { selectStickerSet } from '../../modules/selectors';
|
import { selectStickerSet } from '../../modules/selectors';
|
||||||
import { useIntersectionObserver } from '../../hooks/useIntersectionObserver';
|
import { useIntersectionObserver } from '../../hooks/useIntersectionObserver';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
import Modal from '../ui/Modal';
|
import Modal from '../ui/Modal';
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
@ -43,6 +44,7 @@ const StickerSetModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
observe: observeIntersection,
|
observe: observeIntersection,
|
||||||
@ -98,7 +100,11 @@ const StickerSetModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
color={stickerSet.installedDate ? 'danger' : 'primary'}
|
color={stickerSet.installedDate ? 'danger' : 'primary'}
|
||||||
onClick={handleButtonClick}
|
onClick={handleButtonClick}
|
||||||
>
|
>
|
||||||
{`${stickerSet.installedDate ? 'Remove' : 'Add'} ${stickerSet.count} stickers`}
|
{lang(
|
||||||
|
stickerSet.installedDate ? 'StickerPack.RemoveStickerCount' : 'StickerPack.AddStickerCount',
|
||||||
|
stickerSet.count,
|
||||||
|
'i',
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import buildClassName from '../../util/buildClassName';
|
|||||||
import trimText from '../../util/trimText';
|
import trimText from '../../util/trimText';
|
||||||
import renderText from './helpers/renderText';
|
import renderText from './helpers/renderText';
|
||||||
import { formatPastTimeShort } from '../../util/dateFormat';
|
import { formatPastTimeShort } from '../../util/dateFormat';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
import Media from './Media';
|
import Media from './Media';
|
||||||
import Link from '../ui/Link';
|
import Link from '../ui/Link';
|
||||||
@ -23,13 +24,15 @@ type OwnProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const WebLink: FC<OwnProps> = ({ message, senderTitle, onMessageClick }) => {
|
const WebLink: FC<OwnProps> = ({ message, senderTitle, onMessageClick }) => {
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
let linkData: ApiWebPage | undefined = getMessageWebPage(message);
|
let linkData: ApiWebPage | undefined = getMessageWebPage(message);
|
||||||
|
|
||||||
if (!linkData) {
|
if (!linkData) {
|
||||||
const link = getFirstLinkInMessage(message);
|
const link = getFirstLinkInMessage(message);
|
||||||
if (link) {
|
if (link) {
|
||||||
const { url, domain } = link;
|
const { url, domain } = link;
|
||||||
const messageText = getMessageSummaryText(message);
|
const messageText = getMessageSummaryText(lang, message);
|
||||||
|
|
||||||
linkData = {
|
linkData = {
|
||||||
siteName: domain.replace(/^www./, ''),
|
siteName: domain.replace(/^www./, ''),
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React from '../../../lib/teact/teact';
|
import React from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import { ApiChat, ApiMessage, ApiUser } from '../../../api/types';
|
import { ApiChat, ApiMessage, ApiUser } from '../../../api/types';
|
||||||
|
import { LangFn } from '../../../hooks/useLang';
|
||||||
import {
|
import {
|
||||||
getChatTitle,
|
getChatTitle,
|
||||||
getMessageContent,
|
getMessageContent,
|
||||||
@ -25,6 +26,7 @@ interface ActionMessageTextOptions {
|
|||||||
const NBSP = '\u00A0';
|
const NBSP = '\u00A0';
|
||||||
|
|
||||||
export function renderActionMessageText(
|
export function renderActionMessageText(
|
||||||
|
lang: LangFn,
|
||||||
message: ApiMessage,
|
message: ApiMessage,
|
||||||
actionOrigin?: ApiUser | ApiChat,
|
actionOrigin?: ApiUser | ApiChat,
|
||||||
targetUser?: ApiUser,
|
targetUser?: ApiUser,
|
||||||
@ -66,7 +68,7 @@ export function renderActionMessageText(
|
|||||||
unprocessed,
|
unprocessed,
|
||||||
'%message%',
|
'%message%',
|
||||||
targetMessage
|
targetMessage
|
||||||
? renderMessageContent(targetMessage, textOptions)
|
? renderMessageContent(lang, targetMessage, textOptions)
|
||||||
: 'a message',
|
: 'a message',
|
||||||
);
|
);
|
||||||
unprocessed = processed.pop() as string;
|
unprocessed = processed.pop() as string;
|
||||||
@ -104,8 +106,8 @@ function renderProductContent(message: ApiMessage) {
|
|||||||
: 'a product';
|
: 'a product';
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMessageContent(message: ApiMessage, options: ActionMessageTextOptions = {}) {
|
function renderMessageContent(lang: LangFn, message: ApiMessage, options: ActionMessageTextOptions = {}) {
|
||||||
const text = getMessageSummaryText(message);
|
const text = getMessageSummaryText(lang, message);
|
||||||
const {
|
const {
|
||||||
photo, video, document, sticker,
|
photo, video, document, sticker,
|
||||||
} = getMessageContent(message);
|
} = getMessageContent(message);
|
||||||
|
|||||||
@ -80,7 +80,7 @@ const NewChatButton: FC<OwnProps> = ({
|
|||||||
color="primary"
|
color="primary"
|
||||||
className={isMenuOpen ? 'active' : ''}
|
className={isMenuOpen ? 'active' : ''}
|
||||||
onClick={toggleIsMenuOpen}
|
onClick={toggleIsMenuOpen}
|
||||||
ariaLabel={isMenuOpen ? 'Close' : 'Create new chat'}
|
ariaLabel={lang(isMenuOpen ? 'Close' : 'NewMessageTitle')}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<i className="icon-new-chat-filled" />
|
<i className="icon-new-chat-filled" />
|
||||||
@ -95,7 +95,7 @@ const NewChatButton: FC<OwnProps> = ({
|
|||||||
>
|
>
|
||||||
<MenuItem icon="channel" onClick={onNewChannel}>{lang('NewChannel')}</MenuItem>
|
<MenuItem icon="channel" onClick={onNewChannel}>{lang('NewChannel')}</MenuItem>
|
||||||
<MenuItem icon="group" onClick={onNewGroup}>{lang('NewGroup')}</MenuItem>
|
<MenuItem icon="group" onClick={onNewGroup}>{lang('NewGroup')}</MenuItem>
|
||||||
<MenuItem icon="user" onClick={onNewPrivateChat}>New Private Chat</MenuItem>
|
<MenuItem icon="user" onClick={onNewPrivateChat}>{lang('NewMessageTitle')}</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React, {
|
|||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { withGlobal } from '../../../lib/teact/teactn';
|
import { withGlobal } from '../../../lib/teact/teactn';
|
||||||
|
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang, { LangFn } from '../../../hooks/useLang';
|
||||||
|
|
||||||
import { GlobalActions, MessageListType } from '../../../global/types';
|
import { GlobalActions, MessageListType } from '../../../global/types';
|
||||||
import {
|
import {
|
||||||
@ -205,6 +205,7 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return (
|
return (
|
||||||
<p className="last-message">
|
<p className="last-message">
|
||||||
{renderText(renderActionMessageText(
|
{renderText(renderActionMessageText(
|
||||||
|
lang,
|
||||||
lastMessage,
|
lastMessage,
|
||||||
actionOrigin,
|
actionOrigin,
|
||||||
actionTargetUser,
|
actionTargetUser,
|
||||||
@ -223,7 +224,7 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
{senderName && (
|
{senderName && (
|
||||||
<span className="sender-name">{renderText(senderName)}</span>
|
<span className="sender-name">{renderText(senderName)}</span>
|
||||||
)}
|
)}
|
||||||
{renderMessageSummary(lastMessage!, mediaBlobUrl || mediaThumbnail)}
|
{renderMessageSummary(lang, lastMessage!, mediaBlobUrl || mediaThumbnail)}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -273,16 +274,16 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderMessageSummary(message: ApiMessage, blobUrl?: string) {
|
function renderMessageSummary(lang: LangFn, message: ApiMessage, blobUrl?: string) {
|
||||||
if (!blobUrl) {
|
if (!blobUrl) {
|
||||||
return renderText(getMessageSummaryText(message));
|
return renderText(getMessageSummaryText(lang, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="media-preview">
|
<span className="media-preview">
|
||||||
<img src={blobUrl} alt="" />
|
<img src={blobUrl} alt="" />
|
||||||
{getMessageVideo(message) && <i className="icon-play" />}
|
{getMessageVideo(message) && <i className="icon-play" />}
|
||||||
{renderText(getMessageSummaryText(message, true))}
|
{renderText(getMessageSummaryText(lang, message, true))}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,8 @@ const ChatFolders: FC<StateProps & DispatchProps> = ({
|
|||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const transitionRef = useRef<HTMLDivElement>(null);
|
const transitionRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -84,13 +86,13 @@ const ChatFolders: FC<StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ title: 'All' },
|
{ title: lang('FilterAllChats') },
|
||||||
...displayedFolders.map((folder) => ({
|
...displayedFolders.map((folder) => ({
|
||||||
title: folder.title,
|
title: folder.title,
|
||||||
...(folderCountersById && folderCountersById[folder.id]),
|
...(folderCountersById && folderCountersById[folder.id]),
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
}, [displayedFolders, folderCountersById]);
|
}, [displayedFolders, folderCountersById, lang]);
|
||||||
|
|
||||||
const handleSwitchTab = useCallback((index: number) => {
|
const handleSwitchTab = useCallback((index: number) => {
|
||||||
setActiveTab(index);
|
setActiveTab(index);
|
||||||
@ -135,8 +137,6 @@ const ChatFolders: FC<StateProps & DispatchProps> = ({
|
|||||||
shouldRender: shouldRenderPlaceholder, transitionClassNames,
|
shouldRender: shouldRenderPlaceholder, transitionClassNames,
|
||||||
} = useShowTransition(!orderedFolderIds, undefined, true);
|
} = useShowTransition(!orderedFolderIds, undefined, true);
|
||||||
|
|
||||||
const lang = useLang();
|
|
||||||
|
|
||||||
function renderCurrentTab() {
|
function renderCurrentTab() {
|
||||||
const activeFolder = Object.values(chatFoldersById)
|
const activeFolder = Object.values(chatFoldersById)
|
||||||
.find(({ title }) => title === folderTabs![activeTab].title);
|
.find(({ title }) => title === folderTabs![activeTab].title);
|
||||||
|
|||||||
@ -76,6 +76,7 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
setGlobalSearchDate,
|
setGlobalSearchDate,
|
||||||
setSettingOption,
|
setSettingOption,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
const hasMenu = content === LeftColumnContent.ChatList;
|
const hasMenu = content === LeftColumnContent.ChatList;
|
||||||
const clearedDateSearchParam = { date: undefined };
|
const clearedDateSearchParam = { date: undefined };
|
||||||
const clearedChatSearchParam = { id: undefined };
|
const clearedChatSearchParam = { id: undefined };
|
||||||
@ -107,12 +108,12 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
color="translucent"
|
color="translucent"
|
||||||
className={isOpen ? 'active' : ''}
|
className={isOpen ? 'active' : ''}
|
||||||
onClick={hasMenu ? onTrigger : () => onReset()}
|
onClick={hasMenu ? onTrigger : () => onReset()}
|
||||||
ariaLabel={hasMenu ? 'Open menu' : 'Return to chat list'}
|
ariaLabel={hasMenu ? lang('AccDescrOpenMenu2') : 'Return to chat list'}
|
||||||
>
|
>
|
||||||
<div className={buildClassName('animated-menu-icon', !hasMenu && 'state-back')} />
|
<div className={buildClassName('animated-menu-icon', !hasMenu && 'state-back')} />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}, [hasMenu, onReset]);
|
}, [hasMenu, lang, onReset]);
|
||||||
|
|
||||||
const handleSearchFocus = useCallback(() => {
|
const handleSearchFocus = useCallback(() => {
|
||||||
if (!searchQuery) {
|
if (!searchQuery) {
|
||||||
@ -148,8 +149,6 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
setSettingOption({ animationLevel: newLevel });
|
setSettingOption({ animationLevel: newLevel });
|
||||||
}, [animationLevel, setSettingOption]);
|
}, [animationLevel, setSettingOption]);
|
||||||
|
|
||||||
const lang = useLang();
|
|
||||||
|
|
||||||
const isSearchFocused = (
|
const isSearchFocused = (
|
||||||
Boolean(globalSearchChatId)
|
Boolean(globalSearchChatId)
|
||||||
|| content === LeftColumnContent.GlobalSearch
|
|| content === LeftColumnContent.GlobalSearch
|
||||||
@ -198,10 +197,10 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
icon="darkmode"
|
icon="darkmode"
|
||||||
onClick={handleDarkModeToggle}
|
onClick={handleDarkModeToggle}
|
||||||
>
|
>
|
||||||
<span className="menu-item-name">Dark Mode</span>
|
<span className="menu-item-name">{lang('lng_menu_night_mode')}</span>
|
||||||
<Switcher
|
<Switcher
|
||||||
id="darkmode"
|
id="darkmode"
|
||||||
label="Toggle Dark Mode"
|
label={lang(theme === 'dark' ? 'lng_settings_disable_night_theme' : 'lng_settings_enable_night_theme')}
|
||||||
checked={theme === 'dark'}
|
checked={theme === 'dark'}
|
||||||
noAnimation
|
noAnimation
|
||||||
/>
|
/>
|
||||||
@ -221,7 +220,7 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
icon="help"
|
icon="help"
|
||||||
onClick={openTipsChat}
|
onClick={openTipsChat}
|
||||||
>
|
>
|
||||||
Telegram Features
|
{lang('TelegramFeatures')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon="bug"
|
icon="bug"
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { formatMonthAndYear, toYearMonth } from '../../../util/dateFormat';
|
|||||||
import { getSenderName } from './helpers/getSenderName';
|
import { getSenderName } from './helpers/getSenderName';
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Audio from '../../common/Audio';
|
import Audio from '../../common/Audio';
|
||||||
@ -43,6 +44,7 @@ const AudioResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
focusMessage,
|
focusMessage,
|
||||||
openAudioPlayer,
|
openAudioPlayer,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
const currentType = isVoice ? 'voice' : 'audio';
|
const currentType = isVoice ? 'voice' : 'audio';
|
||||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||||
@ -115,7 +117,12 @@ const AudioResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
noFastList
|
noFastList
|
||||||
>
|
>
|
||||||
{!canRenderContents && <Loading />}
|
{!canRenderContents && <Loading />}
|
||||||
{canRenderContents && (!foundIds || foundIds.length === 0) && <NothingFound />}
|
{canRenderContents && (!foundIds || foundIds.length === 0) && (
|
||||||
|
<NothingFound
|
||||||
|
text={lang('ChatList.Search.NoResults')}
|
||||||
|
description={lang('ChatList.Search.NoResultsDescription')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import renderText from '../../common/helpers/renderText';
|
|||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import useMedia from '../../../hooks/useMedia';
|
import useMedia from '../../../hooks/useMedia';
|
||||||
import { formatPastTimeShort } from '../../../util/dateFormat';
|
import { formatPastTimeShort } from '../../../util/dateFormat';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang, { LangFn } from '../../../hooks/useLang';
|
||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import VerifiedIcon from '../../common/VerifiedIcon';
|
import VerifiedIcon from '../../common/VerifiedIcon';
|
||||||
@ -62,7 +62,7 @@ const ChatMessage: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
focusMessage({ chatId, messageId: message.id });
|
focusMessage({ chatId, messageId: message.id });
|
||||||
}, [chatId, focusMessage, message.id]);
|
}, [chatId, focusMessage, message.id]);
|
||||||
|
|
||||||
useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -96,7 +96,7 @@ const ChatMessage: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="subtitle">
|
<div className="subtitle">
|
||||||
<div className="message">
|
<div className="message">
|
||||||
{renderMessageSummary(message, mediaBlobUrl || mediaThumbnail, searchQuery)}
|
{renderMessageSummary(lang, message, mediaBlobUrl || mediaThumbnail, searchQuery)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -104,16 +104,16 @@ const ChatMessage: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderMessageSummary(message: ApiMessage, blobUrl?: string, searchQuery?: string) {
|
function renderMessageSummary(lang: LangFn, message: ApiMessage, blobUrl?: string, searchQuery?: string) {
|
||||||
if (!blobUrl) {
|
if (!blobUrl) {
|
||||||
return renderText(getMessageSummaryText(message));
|
return renderText(getMessageSummaryText(lang, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="media-preview">
|
<span className="media-preview">
|
||||||
<img src={blobUrl} alt="" />
|
<img src={blobUrl} alt="" />
|
||||||
{getMessageVideo(message) && <i className="icon-play" />}
|
{getMessageVideo(message) && <i className="icon-play" />}
|
||||||
{renderText(getMessageSummaryText(message, true), ['emoji', 'highlight'], { highlight: searchQuery })}
|
{renderText(getMessageSummaryText(lang, message, true), ['emoji', 'highlight'], { highlight: searchQuery })}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { pick } from '../../../util/iteratees';
|
|||||||
import { getMessageSummaryText } from '../../../modules/helpers';
|
import { getMessageSummaryText } from '../../../modules/helpers';
|
||||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import ChatMessage from './ChatMessage';
|
import ChatMessage from './ChatMessage';
|
||||||
@ -49,6 +50,7 @@ const ChatMessageResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
searchMessagesGlobal,
|
searchMessagesGlobal,
|
||||||
onSearchDateSelect,
|
onSearchDateSelect,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||||
runThrottled(() => {
|
runThrottled(() => {
|
||||||
@ -79,7 +81,7 @@ const ChatMessageResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}, [foundIds, globalMessagesByChatId]);
|
}, [foundIds, globalMessagesByChatId]);
|
||||||
|
|
||||||
function renderFoundMessage(message: ApiMessage) {
|
function renderFoundMessage(message: ApiMessage) {
|
||||||
const text = getMessageSummaryText(message);
|
const text = getMessageSummaryText(lang, message);
|
||||||
const chat = chatsById[message.chatId];
|
const chat = chatsById[message.chatId];
|
||||||
|
|
||||||
if (!text || !chat) {
|
if (!text || !chat) {
|
||||||
@ -113,7 +115,12 @@ const ChatMessageResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{nothingFound && <NothingFound />}
|
{nothingFound && (
|
||||||
|
<NothingFound
|
||||||
|
text={lang('ChatList.Search.NoResults')}
|
||||||
|
description={lang('ChatList.Search.NoResultsDescription')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{!!foundMessages.length && foundMessages.map(renderFoundMessage)}
|
{!!foundMessages.length && foundMessages.map(renderFoundMessage)}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -159,7 +159,7 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
function renderFoundMessage(message: ApiMessage) {
|
function renderFoundMessage(message: ApiMessage) {
|
||||||
const text = getMessageSummaryText(message);
|
const text = getMessageSummaryText(lang, message);
|
||||||
const chat = chatsById[message.chatId];
|
const chat = chatsById[message.chatId];
|
||||||
|
|
||||||
if (!text || !chat) {
|
if (!text || !chat) {
|
||||||
@ -199,7 +199,12 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{nothingFound && <NothingFound />}
|
{nothingFound && (
|
||||||
|
<NothingFound
|
||||||
|
text={lang('ChatList.Search.NoResults')}
|
||||||
|
description={lang('ChatList.Search.NoResultsDescription')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{!!localResults.length && (
|
{!!localResults.length && (
|
||||||
<div className="chat-selection no-selection no-scrollbar">
|
<div className="chat-selection no-selection no-scrollbar">
|
||||||
{localResults.map((id) => (
|
{localResults.map((id) => (
|
||||||
@ -215,9 +220,11 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
<div className="search-section">
|
<div className="search-section">
|
||||||
<h3 className="section-heading">
|
<h3 className="section-heading">
|
||||||
{localResults.length > LESS_LIST_ITEMS_AMOUNT && (
|
{localResults.length > LESS_LIST_ITEMS_AMOUNT && (
|
||||||
<Link onClick={handleClickShowMoreLocal}>{shouldShowMoreLocal ? 'Show less' : 'Show more'}</Link>
|
<Link onClick={handleClickShowMoreLocal}>
|
||||||
|
{lang(shouldShowMoreLocal ? 'ChatList.Search.ShowLess' : 'ChatList.Search.ShowMore')}
|
||||||
|
</Link>
|
||||||
)}
|
)}
|
||||||
Contacts and Chats
|
{lang('DialogList.SearchSectionDialogs')}
|
||||||
</h3>
|
</h3>
|
||||||
{localResults.map((id, index) => {
|
{localResults.map((id, index) => {
|
||||||
if (!shouldShowMoreLocal && index >= LESS_LIST_ITEMS_AMOUNT) {
|
if (!shouldShowMoreLocal && index >= LESS_LIST_ITEMS_AMOUNT) {
|
||||||
@ -237,9 +244,11 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
<div className="search-section">
|
<div className="search-section">
|
||||||
<h3 className="section-heading">
|
<h3 className="section-heading">
|
||||||
{globalResults.length > LESS_LIST_ITEMS_AMOUNT && (
|
{globalResults.length > LESS_LIST_ITEMS_AMOUNT && (
|
||||||
<Link onClick={handleClickShowMoreGlobal}>{shouldShowMoreGlobal ? 'Show less' : 'Show more'}</Link>
|
<Link onClick={handleClickShowMoreGlobal}>
|
||||||
|
{lang(shouldShowMoreGlobal ? 'ChatList.Search.ShowLess' : 'ChatList.Search.ShowMore')}
|
||||||
|
</Link>
|
||||||
)}
|
)}
|
||||||
Global Search
|
{lang('DialogList.SearchSectionGlobal')}
|
||||||
</h3>
|
</h3>
|
||||||
{globalResults.map((id, index) => {
|
{globalResults.map((id, index) => {
|
||||||
if (!shouldShowMoreGlobal && index >= LESS_LIST_ITEMS_AMOUNT) {
|
if (!shouldShowMoreGlobal && index >= LESS_LIST_ITEMS_AMOUNT) {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { getSenderName } from './helpers/getSenderName';
|
|||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
import { getMessageDocument } from '../../../modules/helpers';
|
import { getMessageDocument } from '../../../modules/helpers';
|
||||||
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import Document from '../../common/Document';
|
import Document from '../../common/Document';
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
@ -43,6 +44,7 @@ const FileResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
searchMessagesGlobal,
|
searchMessagesGlobal,
|
||||||
focusMessage,
|
focusMessage,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||||
runThrottled(() => {
|
runThrottled(() => {
|
||||||
@ -109,7 +111,12 @@ const FileResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
noFastList
|
noFastList
|
||||||
>
|
>
|
||||||
{!canRenderContents && <Loading />}
|
{!canRenderContents && <Loading />}
|
||||||
{canRenderContents && (!foundIds || foundIds.length === 0) && <NothingFound />}
|
{canRenderContents && (!foundIds || foundIds.length === 0) && (
|
||||||
|
<NothingFound
|
||||||
|
text={lang('ChatList.Search.NoResults')}
|
||||||
|
description={lang('ChatList.Search.NoResultsDescription')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { formatMonthAndYear, toYearMonth } from '../../../util/dateFormat';
|
|||||||
import { getSenderName } from './helpers/getSenderName';
|
import { getSenderName } from './helpers/getSenderName';
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import WebLink from '../../common/WebLink';
|
import WebLink from '../../common/WebLink';
|
||||||
@ -41,6 +42,7 @@ const LinkResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
searchMessagesGlobal,
|
searchMessagesGlobal,
|
||||||
focusMessage,
|
focusMessage,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||||
runThrottled(() => {
|
runThrottled(() => {
|
||||||
@ -103,7 +105,12 @@ const LinkResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
noFastList
|
noFastList
|
||||||
>
|
>
|
||||||
{!canRenderContents && <Loading />}
|
{!canRenderContents && <Loading />}
|
||||||
{canRenderContents && (!foundIds || foundIds.length === 0) && <NothingFound />}
|
{canRenderContents && (!foundIds || foundIds.length === 0) && (
|
||||||
|
<NothingFound
|
||||||
|
text={lang('ChatList.Search.NoResults')}
|
||||||
|
description={lang('ChatList.Search.NoResultsDescription')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,17 +7,18 @@ import { GlobalActions } from '../../../global/types';
|
|||||||
import { LoadMoreDirection, MediaViewerOrigin } from '../../../types';
|
import { LoadMoreDirection, MediaViewerOrigin } from '../../../types';
|
||||||
|
|
||||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||||
|
import { SLIDE_TRANSITION_DURATION } from '../../../config';
|
||||||
import { createMapStateToProps, StateProps } from './helpers/createMapStateToProps';
|
import { createMapStateToProps, StateProps } from './helpers/createMapStateToProps';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
||||||
|
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Media from '../../common/Media';
|
import Media from '../../common/Media';
|
||||||
import ChatMessage from './ChatMessage';
|
import ChatMessage from './ChatMessage';
|
||||||
import NothingFound from '../../common/NothingFound';
|
import NothingFound from '../../common/NothingFound';
|
||||||
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
|
|
||||||
import { SLIDE_TRANSITION_DURATION } from '../../../config';
|
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
@ -39,6 +40,7 @@ const MediaResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
searchMessagesGlobal,
|
searchMessagesGlobal,
|
||||||
openMediaViewer,
|
openMediaViewer,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||||
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
|
||||||
runThrottled(() => {
|
runThrottled(() => {
|
||||||
@ -115,7 +117,12 @@ const MediaResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
noFastList
|
noFastList
|
||||||
>
|
>
|
||||||
{!canRenderContents && <Loading />}
|
{!canRenderContents && <Loading />}
|
||||||
{canRenderContents && (!foundIds || foundIds.length === 0) && <NothingFound />}
|
{canRenderContents && (!foundIds || foundIds.length === 0) && (
|
||||||
|
<NothingFound
|
||||||
|
text={lang('ChatList.Search.NoResults')}
|
||||||
|
description={lang('ChatList.Search.NoResultsDescription')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{isMediaGrid && renderGallery()}
|
{isMediaGrid && renderGallery()}
|
||||||
{isMessageList && renderSearchResult()}
|
{isMessageList && renderSearchResult()}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
|
|||||||
@ -199,7 +199,7 @@ const SettingsEditProfile: FC<StateProps & DispatchProps> = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<p className="settings-item-description">
|
<p className="settings-item-description">
|
||||||
{renderText(lang('BioAbout'), ['br', 'simple_markdown'])}
|
{renderText(lang('lng_settings_about_bio'), ['br', 'simple_markdown'])}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ const SettingsEditProfile: FC<StateProps & DispatchProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
{username && (
|
{username && (
|
||||||
<p className="settings-item-description">
|
<p className="settings-item-description">
|
||||||
This link opens a chat with you:<br />
|
{lang('lng_username_link')}<br />
|
||||||
<span className="username-link">https://t.me/{username}</span>
|
<span className="username-link">https://t.me/{username}</span>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -31,11 +31,6 @@ type StateProps = ISettings['byKey'] & {
|
|||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, 'setSettingOption' | 'loadStickerSets' | 'loadAddedStickers'>;
|
type DispatchProps = Pick<GlobalActions, 'setSettingOption' | 'loadStickerSets' | 'loadAddedStickers'>;
|
||||||
|
|
||||||
const KEYBOARD_SEND_OPTIONS = !IS_TOUCH_ENV ? [
|
|
||||||
{ value: 'enter', label: 'Send by Enter', subLabel: 'New line by Shift + Enter' },
|
|
||||||
{ value: 'ctrl-enter', label: `Send by ${IS_MAC_OS ? 'Cmd' : 'Ctrl'} + Enter`, subLabel: 'New line by Enter' },
|
|
||||||
] : undefined;
|
|
||||||
|
|
||||||
const ANIMATION_LEVEL_OPTIONS = [
|
const ANIMATION_LEVEL_OPTIONS = [
|
||||||
'Solid and Steady',
|
'Solid and Steady',
|
||||||
'Nice and Fast',
|
'Nice and Fast',
|
||||||
@ -67,6 +62,17 @@ const SettingsGeneral: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
const [isModalOpen, openModal, closeModal] = useFlag();
|
const [isModalOpen, openModal, closeModal] = useFlag();
|
||||||
const [sticker, setSticker] = useState<ApiSticker>();
|
const [sticker, setSticker] = useState<ApiSticker>();
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const KEYBOARD_SEND_OPTIONS = !IS_TOUCH_ENV ? [
|
||||||
|
{ value: 'enter', label: lang('lng_settings_send_enter'), subLabel: 'New line by Shift + Enter' },
|
||||||
|
{
|
||||||
|
value: 'ctrl-enter',
|
||||||
|
label: lang(IS_MAC_OS ? 'lng_settings_send_cmdenter' : 'lng_settings_send_ctrlenter'),
|
||||||
|
subLabel: 'New line by Enter',
|
||||||
|
},
|
||||||
|
] : undefined;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadStickerSets();
|
loadStickerSets();
|
||||||
}, [loadStickerSets]);
|
}, [loadStickerSets]);
|
||||||
@ -96,9 +102,6 @@ const SettingsGeneral: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
openModal();
|
openModal();
|
||||||
}, [openModal]);
|
}, [openModal]);
|
||||||
|
|
||||||
const lang = useLang();
|
|
||||||
|
|
||||||
|
|
||||||
const stickerSets = stickerSetIds && stickerSetIds.map((id: string) => {
|
const stickerSets = stickerSetIds && stickerSetIds.map((id: string) => {
|
||||||
return stickerSetsById && stickerSetsById[id] && stickerSetsById[id].installedDate ? stickerSetsById[id] : false;
|
return stickerSetsById && stickerSetsById[id] && stickerSetsById[id].installedDate ? stickerSetsById[id] : false;
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
|
|||||||
@ -83,7 +83,7 @@ const SettingsHeader: FC<OwnProps & DispatchProps> = ({
|
|||||||
function renderHeaderContent() {
|
function renderHeaderContent() {
|
||||||
switch (currentScreen) {
|
switch (currentScreen) {
|
||||||
case SettingsScreens.EditProfile:
|
case SettingsScreens.EditProfile:
|
||||||
return <h3>{lang('EditProfile')}</h3>;
|
return <h3>{lang('lng_settings_information')}</h3>;
|
||||||
case SettingsScreens.General:
|
case SettingsScreens.General:
|
||||||
return <h3>{lang('General')}</h3>;
|
return <h3>{lang('General')}</h3>;
|
||||||
case SettingsScreens.Notifications:
|
case SettingsScreens.Notifications:
|
||||||
|
|||||||
@ -40,7 +40,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
|||||||
icon="edit"
|
icon="edit"
|
||||||
onClick={() => onScreenSelect(SettingsScreens.EditProfile)}
|
onClick={() => onScreenSelect(SettingsScreens.EditProfile)}
|
||||||
>
|
>
|
||||||
{lang('EditProfile')}
|
{lang('lng_settings_information')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="folder"
|
icon="folder"
|
||||||
|
|||||||
@ -41,7 +41,7 @@ const SenderInfo: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
focusMessage({ chatId, messageId });
|
focusMessage({ chatId, messageId });
|
||||||
}, [chatId, focusMessage, messageId, closeMediaViewer]);
|
}, [chatId, focusMessage, messageId, closeMediaViewer]);
|
||||||
|
|
||||||
useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
if (!sender || (!message && !isAvatar)) {
|
if (!sender || (!message && !isAvatar)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -62,7 +62,7 @@ const SenderInfo: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
{senderTitle && renderText(senderTitle)}
|
{senderTitle && renderText(senderTitle)}
|
||||||
</div>
|
</div>
|
||||||
<div className="date">
|
<div className="date">
|
||||||
{isAvatar ? 'Profile photo' : formatMediaDateTime(message!.date * 1000)}
|
{isAvatar ? lang('lng_mediaview_profile_photo') : formatMediaDateTime(message!.date * 1000)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -65,7 +65,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
|||||||
useEnsureMessage(message.chatId, message.replyToMessageId, targetMessage);
|
useEnsureMessage(message.chatId, message.replyToMessageId, targetMessage);
|
||||||
useFocusMessage(ref, message.chatId, isFocused, focusDirection, noFocusHighlight);
|
useFocusMessage(ref, message.chatId, isFocused, focusDirection, noFocusHighlight);
|
||||||
|
|
||||||
useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
const noAppearanceAnimation = appearanceOrder <= 0;
|
const noAppearanceAnimation = appearanceOrder <= 0;
|
||||||
const [isShown, markShown] = useFlag(noAppearanceAnimation);
|
const [isShown, markShown] = useFlag(noAppearanceAnimation);
|
||||||
@ -79,6 +79,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
|||||||
const { transitionClassNames } = useShowTransition(isShown, undefined, noAppearanceAnimation, false);
|
const { transitionClassNames } = useShowTransition(isShown, undefined, noAppearanceAnimation, false);
|
||||||
|
|
||||||
const content = renderActionMessageText(
|
const content = renderActionMessageText(
|
||||||
|
lang,
|
||||||
message,
|
message,
|
||||||
sender,
|
sender,
|
||||||
targetUser,
|
targetUser,
|
||||||
|
|||||||
@ -165,7 +165,9 @@ const HeaderMenuContainer: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
icon="delete"
|
icon="delete"
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
>
|
>
|
||||||
{lang(isPrivate ? 'Delete' : (canDeleteChat ? 'Delete and Leave' : 'Leave'))}
|
{lang(isPrivate
|
||||||
|
? 'Delete'
|
||||||
|
: (canDeleteChat ? 'GroupInfo.DeleteAndExit' : (isChannel ? 'LeaveChannel' : 'Group.LeaveGroup')))}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
@ -31,10 +31,11 @@ type OwnProps = {
|
|||||||
const HeaderPinnedMessage: FC<OwnProps> = ({
|
const HeaderPinnedMessage: FC<OwnProps> = ({
|
||||||
message, count, index, customTitle, className, onUnpinMessage, onClick, onAllPinnedClick,
|
message, count, index, customTitle, className, onUnpinMessage, onClick, onAllPinnedClick,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
const mediaThumbnail = useWebpThumbnail(message);
|
const mediaThumbnail = useWebpThumbnail(message);
|
||||||
const mediaBlobUrl = useMedia(getMessageMediaHash(message, 'pictogram'));
|
const mediaBlobUrl = useMedia(getMessageMediaHash(message, 'pictogram'));
|
||||||
|
|
||||||
const text = getMessageSummaryText(message, Boolean(mediaThumbnail));
|
const text = getMessageSummaryText(lang, message, Boolean(mediaThumbnail));
|
||||||
const [isUnpinDialogOpen, openUnpinDialog, closeUnpinDialog] = useFlag();
|
const [isUnpinDialogOpen, openUnpinDialog, closeUnpinDialog] = useFlag();
|
||||||
|
|
||||||
const handleUnpinMessage = useCallback(() => {
|
const handleUnpinMessage = useCallback(() => {
|
||||||
@ -45,8 +46,6 @@ const HeaderPinnedMessage: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
}, [closeUnpinDialog, onUnpinMessage, message.id]);
|
}, [closeUnpinDialog, onUnpinMessage, message.id]);
|
||||||
|
|
||||||
const lang = useLang();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName('HeaderPinnedMessage-wrapper', className)}>
|
<div className={buildClassName('HeaderPinnedMessage-wrapper', className)}>
|
||||||
{count > 1 && (
|
{count > 1 && (
|
||||||
|
|||||||
@ -8,9 +8,10 @@ import { GlobalActions } from '../../../global/types';
|
|||||||
|
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import { isChatPrivate } from '../../../modules/helpers';
|
import { isChatPrivate } from '../../../modules/helpers';
|
||||||
import { formatInteger, formatIntegerCompact } from '../../../util/textFormat';
|
import { formatIntegerCompact } from '../../../util/textFormat';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { selectThreadInfo } from '../../../modules/selectors';
|
import { selectThreadInfo } from '../../../modules/selectors';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ type DispatchProps = Pick<GlobalActions, 'openChat'>;
|
|||||||
const CommentButton: FC<OwnProps & StateProps & DispatchProps> = ({
|
const CommentButton: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
disabled, threadInfo, usersById, chatsById, openChat,
|
disabled, threadInfo, usersById, chatsById, openChat,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
const {
|
const {
|
||||||
threadId, chatId, messagesCount, lastMessageId, lastReadInboxMessageId, recentReplierIds,
|
threadId, chatId, messagesCount, lastMessageId, lastReadInboxMessageId, recentReplierIds,
|
||||||
} = threadInfo;
|
} = threadInfo;
|
||||||
@ -76,24 +78,14 @@ const CommentButton: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
<i className="icon-comments-sticker" />
|
<i className="icon-comments-sticker" />
|
||||||
{(!recentRepliers || recentRepliers.length === 0) && <i className="icon-comments" />}
|
{(!recentRepliers || recentRepliers.length === 0) && <i className="icon-comments" />}
|
||||||
{renderRecentRepliers()}
|
{renderRecentRepliers()}
|
||||||
<div className="label">{renderLabel(messagesCount)}</div>
|
<div className="label">
|
||||||
|
{messagesCount ? lang('Comments', messagesCount, 'i') : lang('LeaveAComment')}
|
||||||
|
</div>
|
||||||
<i className="icon-next" />
|
<i className="icon-next" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderLabel(messagesCount: number) {
|
|
||||||
if (messagesCount === 0) {
|
|
||||||
return 'Leave a Comment';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messagesCount === 1) {
|
|
||||||
return '1 Comment';
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${formatInteger(messagesCount)} Comments`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global, { message }) => {
|
(global, { message }) => {
|
||||||
const { threadId, chatId } = message.threadInfo!;
|
const { threadId, chatId } = message.threadInfo!;
|
||||||
|
|||||||
@ -756,7 +756,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
color="translucent-white"
|
color="translucent-white"
|
||||||
round
|
round
|
||||||
size="tiny"
|
size="tiny"
|
||||||
ariaLabel="Forward message"
|
ariaLabel={lang('lng_context_forward_msg')}
|
||||||
onClick={isLastInDocumentGroup ? handleGroupForward : handleForward}
|
onClick={isLastInDocumentGroup ? handleGroupForward : handleForward}
|
||||||
>
|
>
|
||||||
<i className="icon-share-filled" />
|
<i className="icon-share-filled" />
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export function getMessageCopyOptions(
|
|||||||
|
|
||||||
if (canImageBeCopied) {
|
if (canImageBeCopied) {
|
||||||
options.push({
|
options.push({
|
||||||
label: 'Copy Media',
|
label: 'lng_context_copy_image',
|
||||||
handler: () => {
|
handler: () => {
|
||||||
mediaLoader.fetch(mediaHash, ApiMediaFormat.BlobUrl).then(copyImageToClipboard);
|
mediaLoader.fetch(mediaHash, ApiMediaFormat.BlobUrl).then(copyImageToClipboard);
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ export function getMessageCopyOptions(
|
|||||||
|
|
||||||
if (onCopyLink) {
|
if (onCopyLink) {
|
||||||
options.push({
|
options.push({
|
||||||
label: 'CopyMessageLink',
|
label: 'lng_context_copy_message_link',
|
||||||
handler: () => {
|
handler: () => {
|
||||||
onCopyLink();
|
onCopyLink();
|
||||||
|
|
||||||
@ -73,11 +73,11 @@ export function getMessageCopyOptions(
|
|||||||
|
|
||||||
function getCopyLabel(hasSelection: boolean, canImageBeCopied: boolean): string {
|
function getCopyLabel(hasSelection: boolean, canImageBeCopied: boolean): string {
|
||||||
if (hasSelection) {
|
if (hasSelection) {
|
||||||
return 'Copy Selected Text';
|
return 'lng_context_copy_selected';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canImageBeCopied) {
|
if (canImageBeCopied) {
|
||||||
return 'Copy Text';
|
return 'lng_context_copy_text';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Copy';
|
return 'Copy';
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import useProfileViewportIds from './hooks/useProfileViewportIds';
|
|||||||
import useProfileState from './hooks/useProfileState';
|
import useProfileState from './hooks/useProfileState';
|
||||||
import useTransitionFixes from './hooks/useTransitionFixes';
|
import useTransitionFixes from './hooks/useTransitionFixes';
|
||||||
import useAsyncRendering from './hooks/useAsyncRendering';
|
import useAsyncRendering from './hooks/useAsyncRendering';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
import Transition from '../ui/Transition';
|
import Transition from '../ui/Transition';
|
||||||
import InfiniteScroll from '../ui/InfiniteScroll';
|
import InfiniteScroll from '../ui/InfiniteScroll';
|
||||||
@ -120,6 +121,8 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const transitionRef = useRef<HTMLDivElement>(null);
|
const transitionRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
|
|
||||||
const tabs = useMemo(() => ([
|
const tabs = useMemo(() => ([
|
||||||
@ -228,16 +231,16 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
text = areMembersHidden ? 'You have no access to group members list.' : 'No members found';
|
text = areMembersHidden ? 'You have no access to group members list.' : 'No members found';
|
||||||
break;
|
break;
|
||||||
case 'documents':
|
case 'documents':
|
||||||
text = 'No documents found.';
|
text = lang('lng_media_file_empty_search');
|
||||||
break;
|
break;
|
||||||
case 'links':
|
case 'links':
|
||||||
text = 'No links found.';
|
text = lang('lng_media_link_empty_search');
|
||||||
break;
|
break;
|
||||||
case 'audio':
|
case 'audio':
|
||||||
text = 'No audio found.';
|
text = lang('lng_media_song_empty_search');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
text = 'No media found.';
|
text = lang('SharedMedia.EmptyTitle');
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -235,7 +235,7 @@ const RightHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return (
|
return (
|
||||||
<SearchInput
|
<SearchInput
|
||||||
value={stickerSearchQuery}
|
value={stickerSearchQuery}
|
||||||
placeholder="Search Stickers"
|
placeholder={lang('SearchStickersHint')}
|
||||||
onChange={handleStickerSearchQueryChange}
|
onChange={handleStickerSearchQueryChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import {
|
|||||||
isChatChannel,
|
isChatChannel,
|
||||||
} from '../../modules/helpers';
|
} from '../../modules/helpers';
|
||||||
import renderText from '../common/helpers/renderText';
|
import renderText from '../common/helpers/renderText';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
import { orderBy, pick } from '../../util/iteratees';
|
import { orderBy, pick } from '../../util/iteratees';
|
||||||
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
|
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
|
||||||
|
|
||||||
@ -60,6 +61,8 @@ const RightSearch: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
searchTextMessagesLocal,
|
searchTextMessagesLocal,
|
||||||
focusMessage,
|
focusMessage,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
const foundResults = useMemo(() => {
|
const foundResults = useMemo(() => {
|
||||||
if (!query || !foundIds || !foundIds.length || !messagesById) {
|
if (!query || !foundIds || !foundIds.length || !messagesById) {
|
||||||
return MEMO_EMPTY_ARRAY;
|
return MEMO_EMPTY_ARRAY;
|
||||||
@ -98,7 +101,7 @@ const RightSearch: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
message, senderUser, senderChat, onClick,
|
message, senderUser, senderChat, onClick,
|
||||||
}: Result) => {
|
}: Result) => {
|
||||||
const title = senderChat ? getChatTitle(senderChat) : getUserFullName(senderUser);
|
const title = senderChat ? getChatTitle(senderChat) : getUserFullName(senderUser);
|
||||||
const text = getMessageSummaryText(message);
|
const text = getMessageSummaryText(lang, message);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem className="chat-item-clickable search-result-message m-0" onClick={onClick}>
|
<ListItem className="chat-item-clickable search-result-message m-0" onClick={onClick}>
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { pick } from '../../util/iteratees';
|
|||||||
import { selectShouldLoopStickers, selectStickerSet } from '../../modules/selectors';
|
import { selectShouldLoopStickers, selectStickerSet } from '../../modules/selectors';
|
||||||
import useFlag from '../../hooks/useFlag';
|
import useFlag from '../../hooks/useFlag';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useOnChange from '../../hooks/useOnChange';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import StickerButton from '../common/StickerButton';
|
import StickerButton from '../common/StickerButton';
|
||||||
@ -37,6 +38,7 @@ const STICKERS_TO_DISPLAY = 5;
|
|||||||
const StickerSetResult: FC<OwnProps & StateProps & DispatchProps> = ({
|
const StickerSetResult: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
stickerSetId, observeIntersection, set, shouldPlay, loadStickers, toggleStickerSet, isSomeModalOpen, onModalToggle,
|
stickerSetId, observeIntersection, set, shouldPlay, loadStickers, toggleStickerSet, isSomeModalOpen, onModalToggle,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
const isAdded = set && Boolean(set.installedDate);
|
const isAdded = set && Boolean(set.installedDate);
|
||||||
const areStickersLoaded = Boolean(set && set.stickers);
|
const areStickersLoaded = Boolean(set && set.stickers);
|
||||||
|
|
||||||
@ -79,7 +81,7 @@ const StickerSetResult: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
<div className="sticker-set-header">
|
<div className="sticker-set-header">
|
||||||
<div className="title-wrapper">
|
<div className="title-wrapper">
|
||||||
<h3 className="title">{set.title}</h3>
|
<h3 className="title">{set.title}</h3>
|
||||||
<p className="count">{set.count} stickers</p>
|
<p className="count">{lang('Stickers', set.count, 'i')}</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
className={isAdded ? 'is-added' : undefined}
|
className={isAdded ? 'is-added' : undefined}
|
||||||
@ -89,7 +91,7 @@ const StickerSetResult: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
fluid
|
fluid
|
||||||
onClick={handleAddClick}
|
onClick={handleAddClick}
|
||||||
>
|
>
|
||||||
{isAdded ? 'Added' : 'Add'}
|
{lang(isAdded ? 'Stickers.Installed' : 'Stickers.Install')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="sticker-set-main">
|
<div className="sticker-set-main">
|
||||||
|
|||||||
@ -111,7 +111,7 @@ const ManageGroupRecentActions: FC<OwnProps & StateProps> = ({ chat }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="section not-implemented">
|
<div className="section not-implemented">
|
||||||
<h3 className="section-heading">Admins</h3>
|
<h3 className="section-heading">{lang('Channel.Management.Title')}</h3>
|
||||||
|
|
||||||
<div className="ListItem no-selection">
|
<div className="ListItem no-selection">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|||||||
@ -124,7 +124,7 @@ export const MAX_MEDIA_FILES_FOR_ALBUM = 10;
|
|||||||
export const MAX_ACTIVE_PINNED_CHATS = 5;
|
export const MAX_ACTIVE_PINNED_CHATS = 5;
|
||||||
export const SCHEDULED_WHEN_ONLINE = 0x7FFFFFFE;
|
export const SCHEDULED_WHEN_ONLINE = 0x7FFFFFFE;
|
||||||
export const DEFAULT_LANG_PACK = 'android';
|
export const DEFAULT_LANG_PACK = 'android';
|
||||||
export const LANG_PACKS = ['android', 'ios'];
|
export const LANG_PACKS = ['android', 'ios', 'tdesktop'];
|
||||||
export const TIPS_USERNAME = 'TelegramTips';
|
export const TIPS_USERNAME = 'TelegramTips';
|
||||||
export const FEEDBACK_URL = 'https://bugs.telegram.org/?tag_ids=41&sort=time';
|
export const FEEDBACK_URL = 'https://bugs.telegram.org/?tag_ids=41&sort=time';
|
||||||
export const DARK_THEME_BG_COLOR = '#0F0F0F';
|
export const DARK_THEME_BG_COLOR = '#0F0F0F';
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import { useMemo } from '../lib/teact/teact';
|
import { useMemo } from '../lib/teact/teact';
|
||||||
import { getDispatch } from '../lib/teact/teactn';
|
import { getDispatch } from '../lib/teact/teactn';
|
||||||
import { ApiChat, ApiUser } from '../api/types';
|
import { ApiChat, ApiUser } from '../api/types';
|
||||||
import { isChatArchived, getCanDeleteChat, isChatPrivate } from '../modules/helpers';
|
import {
|
||||||
|
isChatArchived, getCanDeleteChat, isChatPrivate, isChatChannel,
|
||||||
|
} from '../modules/helpers';
|
||||||
|
import useLang from './useLang';
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
chat,
|
chat,
|
||||||
@ -16,6 +19,8 @@ export default ({
|
|||||||
folderId?: number;
|
folderId?: number;
|
||||||
isPinned?: boolean;
|
isPinned?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
toggleChatPinned,
|
toggleChatPinned,
|
||||||
updateChatMutedState,
|
updateChatMutedState,
|
||||||
@ -31,23 +36,39 @@ export default ({
|
|||||||
const isChatWithSelf = privateChatUser && privateChatUser.isSelf;
|
const isChatWithSelf = privateChatUser && privateChatUser.isSelf;
|
||||||
|
|
||||||
const actionUnreadMark = chat.unreadCount || chat.hasUnreadMark
|
const actionUnreadMark = chat.unreadCount || chat.hasUnreadMark
|
||||||
? { title: 'Mark as Read', icon: 'readchats', handler: () => toggleChatUnread({ id: chat.id }) }
|
? { title: lang('MarkAsRead'), icon: 'readchats', handler: () => toggleChatUnread({ id: chat.id }) }
|
||||||
: { title: 'Mark as Unread', icon: 'unread', handler: () => toggleChatUnread({ id: chat.id }) };
|
: { title: lang('MarkAsUnread'), icon: 'unread', handler: () => toggleChatUnread({ id: chat.id }) };
|
||||||
|
|
||||||
const actionPin = isPinned
|
const actionPin = isPinned
|
||||||
? { title: 'Unpin', icon: 'unpin', handler: () => toggleChatPinned({ id: chat.id, folderId }) }
|
? {
|
||||||
: { title: 'Pin', icon: 'pin', handler: () => toggleChatPinned({ id: chat.id, folderId }) };
|
title: lang('UnpinFromTop'),
|
||||||
|
icon: 'unpin',
|
||||||
|
handler: () => toggleChatPinned({ id: chat.id, folderId }),
|
||||||
|
}
|
||||||
|
: { title: lang('PinToTop'), icon: 'pin', handler: () => toggleChatPinned({ id: chat.id, folderId }) };
|
||||||
|
|
||||||
const actionMute = chat.isMuted
|
const actionMute = chat.isMuted
|
||||||
? { title: 'Unmute', icon: 'unmute', handler: () => updateChatMutedState({ chatId: chat.id, isMuted: false }) }
|
? {
|
||||||
: { title: 'Mute', icon: 'mute', handler: () => updateChatMutedState({ chatId: chat.id, isMuted: true }) };
|
title: lang('ChatList.Unmute'),
|
||||||
|
icon: 'unmute',
|
||||||
|
handler: () => updateChatMutedState({ chatId: chat.id, isMuted: false }),
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: lang('ChatList.Mute'),
|
||||||
|
icon: 'mute',
|
||||||
|
handler: () => updateChatMutedState({ chatId: chat.id, isMuted: true }),
|
||||||
|
};
|
||||||
|
|
||||||
const actionArchive = isChatArchived(chat)
|
const actionArchive = isChatArchived(chat)
|
||||||
? { title: 'Unarchive', icon: 'unarchive', handler: () => toggleChatArchived({ id: chat.id }) }
|
? { title: lang('Unarchive'), icon: 'unarchive', handler: () => toggleChatArchived({ id: chat.id }) }
|
||||||
: { title: 'Archive', icon: 'archive', handler: () => toggleChatArchived({ id: chat.id }) };
|
: { title: lang('Archive'), icon: 'archive', handler: () => toggleChatArchived({ id: chat.id }) };
|
||||||
|
|
||||||
const actionDelete = {
|
const actionDelete = {
|
||||||
title: isChatPrivate(chat.id) ? 'Delete' : (getCanDeleteChat(chat) ? 'Delete and Leave' : 'Leave'),
|
title: isChatPrivate(chat.id)
|
||||||
|
? lang('Delete')
|
||||||
|
: lang(getCanDeleteChat(chat)
|
||||||
|
? 'DeleteChat'
|
||||||
|
: (isChatChannel(chat) ? 'LeaveChannel' : 'Group.LeaveGroup')),
|
||||||
icon: 'delete',
|
icon: 'delete',
|
||||||
destructive: true,
|
destructive: true,
|
||||||
handler: handleDelete,
|
handler: handleDelete,
|
||||||
@ -63,7 +84,7 @@ export default ({
|
|||||||
actionDelete,
|
actionDelete,
|
||||||
];
|
];
|
||||||
}, [
|
}, [
|
||||||
chat, privateChatUser, handleDelete, folderId, isPinned,
|
chat, privateChatUser, lang, isPinned, handleDelete, toggleChatUnread, toggleChatPinned, folderId,
|
||||||
toggleChatPinned, updateChatMutedState, toggleChatArchived, toggleChatUnread,
|
updateChatMutedState, toggleChatArchived,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -428,7 +428,7 @@ export function getMessageSenderName(chatId: number, sender?: ApiUser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sender.isSelf) {
|
if (sender.isSelf) {
|
||||||
return 'You';
|
return getTranslation('FromYou');
|
||||||
}
|
}
|
||||||
|
|
||||||
return getUserFirstOrLastName(sender);
|
return getUserFirstOrLastName(sender);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
ApiChat, ApiMessage, ApiMessageEntityTypes, ApiUser,
|
ApiChat, ApiMessage, ApiMessageEntityTypes, ApiUser,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
|
import { LangFn } from '../../hooks/useLang';
|
||||||
|
|
||||||
import { LOCAL_MESSAGE_ID_BASE, SERVICE_NOTIFICATIONS_USER_ID, RE_LINK_TEMPLATE } from '../../config';
|
import { LOCAL_MESSAGE_ID_BASE, SERVICE_NOTIFICATIONS_USER_ID, RE_LINK_TEMPLATE } from '../../config';
|
||||||
import parseEmojiOnlyString from '../../components/common/helpers/parseEmojiOnlyString';
|
import parseEmojiOnlyString from '../../components/common/helpers/parseEmojiOnlyString';
|
||||||
@ -26,62 +27,34 @@ export function getMessageOriginalId(message: ApiMessage) {
|
|||||||
return message.previousLocalId || message.id;
|
return message.previousLocalId || message.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMessageSummaryText(message: ApiMessage, noEmoji = false) {
|
export function getMessageSummaryText(lang: LangFn, message: ApiMessage, noEmoji = false) {
|
||||||
const {
|
const {
|
||||||
text, photo, video, audio, voice, document, sticker, contact, poll, invoice,
|
text, photo, video, audio, voice, document, sticker, contact, poll, invoice,
|
||||||
} = message.content;
|
} = message.content;
|
||||||
|
|
||||||
if (message.groupedId) {
|
if (message.groupedId) {
|
||||||
if (text) {
|
return `${noEmoji ? '' : '🖼 '}${text ? text.text : lang('lng_in_dlg_album')}`;
|
||||||
return `${noEmoji ? '' : '🖼 '}${text.text}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Album';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (photo) {
|
if (photo) {
|
||||||
if (text) {
|
return `${noEmoji ? '' : '🖼 '}${text ? text.text : lang('AttachPhoto')}`;
|
||||||
return `${noEmoji ? '' : '🖼 '}${text.text}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Photo';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video) {
|
if (video) {
|
||||||
if (video.isGif) {
|
return `${noEmoji ? '' : '📹 '}${text ? text.text : lang(video.isGif ? 'AttachGif' : 'AttachVideo')}`;
|
||||||
if (text) {
|
|
||||||
return `${noEmoji ? '' : 'GIF '}${text.text}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'GIF';
|
|
||||||
} else {
|
|
||||||
if (text) {
|
|
||||||
return `${noEmoji ? '' : '📹 '}${text.text}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Video';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sticker) {
|
if (sticker) {
|
||||||
return `${sticker.emoji} Sticker`;
|
return `${sticker.emoji} ${lang('AttachSticker')} `;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio) {
|
if (audio) {
|
||||||
const caption = [audio.title, audio.performer].filter(Boolean).join(' — ') || (text && text.text);
|
const caption = [audio.title, audio.performer].filter(Boolean).join(' — ') || (text && text.text);
|
||||||
if (caption) {
|
return `${noEmoji ? '' : '🎧 '}${caption || lang('AttachMusic')}`;
|
||||||
return `🎧 ${caption}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Audio';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (voice) {
|
if (voice) {
|
||||||
if (text) {
|
return `${noEmoji ? '' : '🎤 '}${text ? text.text : lang('AttachAudio')}`;
|
||||||
return `${noEmoji ? '' : '🎤 '}${text.text}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Voice Message';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document) {
|
if (document) {
|
||||||
@ -89,11 +62,11 @@ export function getMessageSummaryText(message: ApiMessage, noEmoji = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (contact) {
|
if (contact) {
|
||||||
return 'Contact';
|
return lang('AttachContact');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (poll) {
|
if (poll) {
|
||||||
return `📊 ${poll.summary.question}`;
|
return `${noEmoji ? '' : '📊 '}${poll.summary.question}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invoice) {
|
if (invoice) {
|
||||||
@ -107,60 +80,6 @@ export function getMessageSummaryText(message: ApiMessage, noEmoji = false) {
|
|||||||
return CONTENT_NOT_SUPPORTED;
|
return CONTENT_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNotificationText(message: ApiMessage) {
|
|
||||||
const {
|
|
||||||
text, photo, video, audio, voice, document, sticker, contact, poll, invoice,
|
|
||||||
} = message.content;
|
|
||||||
|
|
||||||
if (message.groupedId) {
|
|
||||||
return `🖼 ${text ? text.text : 'Album'}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (photo) {
|
|
||||||
return `🖼 ${text ? text.text : 'Photo'}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (video) {
|
|
||||||
return `📹 ${text ? text.text : video.isGif ? 'GIF' : 'Video'}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sticker) {
|
|
||||||
return `${sticker.emoji} Sticker `;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audio) {
|
|
||||||
const caption = [audio.title, audio.performer].filter(Boolean).join(' — ') || (text && text.text);
|
|
||||||
return `🎧 ${caption || 'Audio'}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (voice) {
|
|
||||||
return `🎤 ${text ? text.text : 'Voice Message'}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document) {
|
|
||||||
return `📎 ${text ? text.text : document.fileName}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contact) {
|
|
||||||
return 'Contact';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (poll) {
|
|
||||||
return `📊 ${poll.summary.question}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invoice) {
|
|
||||||
return 'Invoice';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text) {
|
|
||||||
return text.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CONTENT_NOT_SUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function getMessageText(message: ApiMessage) {
|
export function getMessageText(message: ApiMessage) {
|
||||||
const {
|
const {
|
||||||
text, sticker, photo, video, audio, voice, document, poll, webPage, contact, invoice,
|
text, sticker, photo, video, audio, voice, document, poll, webPage, contact, invoice,
|
||||||
|
|||||||
@ -70,7 +70,7 @@ export function getUserStatus(user: ApiUser, lang: LangFn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (user.type && user.type === 'userTypeBot') {
|
if (user.type && user.type === 'userTypeBot') {
|
||||||
return 'bot';
|
return lang('Bot');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user.status) {
|
if (!user.status) {
|
||||||
|
|||||||
@ -7,11 +7,12 @@ import {
|
|||||||
getChatTitle,
|
getChatTitle,
|
||||||
getMessageAction,
|
getMessageAction,
|
||||||
getMessageSenderName,
|
getMessageSenderName,
|
||||||
getNotificationText,
|
getMessageSummaryText,
|
||||||
getPrivateChatUserId,
|
getPrivateChatUserId,
|
||||||
isActionMessage,
|
isActionMessage,
|
||||||
isChatChannel,
|
isChatChannel,
|
||||||
} from '../modules/helpers';
|
} from '../modules/helpers';
|
||||||
|
import { getTranslation } from './langProvider';
|
||||||
import { replaceSettings } from '../modules/reducers';
|
import { replaceSettings } from '../modules/reducers';
|
||||||
import { selectChatMessage, selectUser } from '../modules/selectors';
|
import { selectChatMessage, selectUser } from '../modules/selectors';
|
||||||
import { IS_SERVICE_WORKER_SUPPORTED } from './environment';
|
import { IS_SERVICE_WORKER_SUPPORTED } from './environment';
|
||||||
@ -225,6 +226,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage) {
|
|||||||
? chat
|
? chat
|
||||||
: messageSender;
|
: messageSender;
|
||||||
body = renderActionMessageText(
|
body = renderActionMessageText(
|
||||||
|
getTranslation,
|
||||||
message,
|
message,
|
||||||
actionOrigin,
|
actionOrigin,
|
||||||
actionTargetUser,
|
actionTargetUser,
|
||||||
@ -234,7 +236,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage) {
|
|||||||
) as string;
|
) as string;
|
||||||
} else {
|
} else {
|
||||||
const senderName = getMessageSenderName(chat.id, messageSender);
|
const senderName = getMessageSenderName(chat.id, messageSender);
|
||||||
const summary = getNotificationText(message);
|
const summary = getMessageSummaryText(getTranslation, message);
|
||||||
|
|
||||||
body = senderName ? `${senderName}: ${summary}` : summary;
|
body = senderName ? `${senderName}: ${summary}` : summary;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -137,7 +137,7 @@ export function expectCommentButton(
|
|||||||
expect(button.querySelector('.label')).toHaveTextContent(`${commentsCount} Comments`);
|
expect(button.querySelector('.label')).toHaveTextContent(`${commentsCount} Comments`);
|
||||||
expect(button.querySelectorAll('.Avatar')).toHaveLength(Math.min(authorsCount, 3));
|
expect(button.querySelectorAll('.Avatar')).toHaveLength(Math.min(authorsCount, 3));
|
||||||
} else {
|
} else {
|
||||||
expect(button.querySelector('.label')).toHaveTextContent('Leave a Comment');
|
expect(button.querySelector('.label')).toHaveTextContent('Leave a comment');
|
||||||
expect(button.querySelectorAll('.Avatar')).toHaveLength(0);
|
expect(button.querySelectorAll('.Avatar')).toHaveLength(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user