Localization: Fix translations sometimes not applied

This commit is contained in:
Alexander Zinchuk 2021-06-01 11:49:33 +03:00
parent 9ebd0c4e00
commit 6bfcbe4da1
38 changed files with 151 additions and 133 deletions

View File

@ -27,7 +27,7 @@ import useMediaWithDownloadProgress from '../../hooks/useMediaWithDownloadProgre
import useShowTransition from '../../hooks/useShowTransition'; import useShowTransition from '../../hooks/useShowTransition';
import useBuffering from '../../hooks/useBuffering'; import useBuffering from '../../hooks/useBuffering';
import useAudioPlayer from '../../hooks/useAudioPlayer'; import useAudioPlayer from '../../hooks/useAudioPlayer';
import useLang from '../../hooks/useLang'; import useLang, { LangFn } from '../../hooks/useLang';
import Button from '../ui/Button'; import Button from '../ui/Button';
import ProgressSpinner from '../ui/ProgressSpinner'; import ProgressSpinner from '../ui/ProgressSpinner';
@ -177,7 +177,7 @@ const Audio: FC<OwnProps & StateProps> = ({
onDateClick!(message.id, message.chatId); onDateClick!(message.id, message.chatId);
}, [onDateClick, message.id, message.chatId]); }, [onDateClick, message.id, message.chatId]);
useLang(); const lang = useLang();
function getFirstLine() { function getFirstLine() {
if (isVoice) { if (isVoice) {
@ -245,7 +245,7 @@ const Audio: FC<OwnProps & StateProps> = ({
className="date" className="date"
onClick={handleDateClick} onClick={handleDateClick}
> >
{formatPastTimeShort(date * 1000)} {formatPastTimeShort(lang, date * 1000)}
</Link> </Link>
)} )}
</div> </div>
@ -293,7 +293,7 @@ const Audio: FC<OwnProps & StateProps> = ({
)} )}
{renderingFor === 'searchResult' && renderSearchResult()} {renderingFor === 'searchResult' && renderSearchResult()}
{renderingFor !== 'searchResult' && audio && renderAudio( {renderingFor !== 'searchResult' && audio && renderAudio(
audio, isPlaying, playProgress, bufferedProgress, seekHandlers, date, lang, audio, isPlaying, playProgress, bufferedProgress, seekHandlers, date,
onDateClick ? handleDateClick : undefined, onDateClick ? handleDateClick : undefined,
)} )}
{renderingFor !== 'searchResult' && voice && renderVoice(voice, renderedWaveform, isMediaUnread)} {renderingFor !== 'searchResult' && voice && renderVoice(voice, renderedWaveform, isMediaUnread)}
@ -302,6 +302,7 @@ const Audio: FC<OwnProps & StateProps> = ({
}; };
function renderAudio( function renderAudio(
lang: LangFn,
audio: ApiAudio, audio: ApiAudio,
isPlaying: boolean, isPlaying: boolean,
playProgress: number, playProgress: number,
@ -327,7 +328,7 @@ function renderAudio(
{' '} {' '}
&bull; &bull;
{' '} {' '}
<Link className="date" onClick={handleDateClick}>{formatMediaDateTime(date * 1000)}</Link> <Link className="date" onClick={handleDateClick}>{formatMediaDateTime(lang, date * 1000)}</Link>
</> </>
)} )}
</div> </div>

View File

@ -54,7 +54,7 @@ const Avatar: FC<OwnProps> = ({
const dataUri = useMedia(imageHash, false, ApiMediaFormat.DataUri, lastSyncTime); const dataUri = useMedia(imageHash, false, ApiMediaFormat.DataUri, lastSyncTime);
const { shouldRenderFullMedia, transitionClassNames } = useTransitionForMedia(dataUri, 'slow'); const { shouldRenderFullMedia, transitionClassNames } = useTransitionForMedia(dataUri, 'slow');
useLang(); const lang = useLang();
let content: string | undefined = ''; let content: string | undefined = '';
@ -68,7 +68,7 @@ const Avatar: FC<OwnProps> = ({
const userFullName = getUserFullName(user); const userFullName = getUserFullName(user);
content = userFullName ? getFirstLetters(userFullName, 2) : undefined; content = userFullName ? getFirstLetters(userFullName, 2) : undefined;
} else if (chat) { } else if (chat) {
const title = getChatTitle(chat); const title = getChatTitle(lang, chat);
content = title && getFirstLetters(title, isChatPrivate(chat.id) ? 2 : 1); content = title && getFirstLetters(title, isChatPrivate(chat.id) ? 2 : 1);
} else if (text) { } else if (text) {
content = getFirstLetters(text, 2); content = getFirstLetters(text, 2);

View File

@ -7,6 +7,7 @@ import {
formatMonthAndYear, formatHumanDate, formatTime, formatMonthAndYear, formatHumanDate, formatTime,
} from '../../util/dateFormat'; } from '../../util/dateFormat';
import { IS_MOBILE_SCREEN } from '../../util/environment'; import { IS_MOBILE_SCREEN } from '../../util/environment';
import useLang, { LangFn } from '../../hooks/useLang';
import Modal from '../ui/Modal'; import Modal from '../ui/Modal';
import Button from '../ui/Button'; import Button from '../ui/Button';
@ -42,6 +43,7 @@ const CalendarModal: FC<OwnProps> = ({
onSubmit, onSubmit,
onSecondButtonClick, onSecondButtonClick,
}) => { }) => {
const lang = useLang();
const now = new Date(); const now = new Date();
const defaultSelectedDate = useMemo(() => (selectedAt ? new Date(selectedAt) : new Date()), [selectedAt]); const defaultSelectedDate = useMemo(() => (selectedAt ? new Date(selectedAt) : new Date()), [selectedAt]);
const maxDate = maxAt ? new Date(maxAt) : undefined; const maxDate = maxAt ? new Date(maxAt) : undefined;
@ -181,7 +183,7 @@ const CalendarModal: FC<OwnProps> = ({
</Button> </Button>
<h4> <h4>
{formatMonthAndYear(selectedDate, IS_MOBILE_SCREEN)} {formatMonthAndYear(lang, selectedDate, IS_MOBILE_SCREEN)}
</h4> </h4>
<Button <Button
@ -240,7 +242,7 @@ const CalendarModal: FC<OwnProps> = ({
<div className="footer"> <div className="footer">
<Button onClick={handleSubmit}> <Button onClick={handleSubmit}>
{withTimePicker ? formatSubmitLabel(selectedDate) : submitButtonLabel} {withTimePicker ? formatSubmitLabel(lang, selectedDate) : submitButtonLabel}
</Button> </Button>
{secondButtonLabel && ( {secondButtonLabel && (
<Button onClick={onSecondButtonClick} isText> <Button onClick={onSecondButtonClick} isText>
@ -293,10 +295,14 @@ function formatInputTime(value: string | number) {
return String(value).padStart(2, '0'); return String(value).padStart(2, '0');
} }
function formatSubmitLabel(date: Date) { function formatSubmitLabel(lang: LangFn, date: Date) {
const day = formatHumanDate(date, true); const day = formatHumanDate(lang, date, true);
return `Send ${day === 'Today' ? day : `on ${day}`} at ${formatTime(date)}`; if (day === 'Today') {
return lang('Conversation.ScheduleMessage.SendToday', formatTime(date));
}
return lang('Conversation.ScheduleMessage.SendOn', day).replace('%@', formatTime(date));
} }
export default memo(CalendarModal); export default memo(CalendarModal);

View File

@ -37,7 +37,6 @@ type StateProps = {
isBasicGroup: boolean; isBasicGroup: boolean;
isSuperGroup: boolean; isSuperGroup: boolean;
canDeleteForAll?: boolean; canDeleteForAll?: boolean;
chatTitle: string;
contactName?: string; contactName?: string;
}; };
@ -52,13 +51,15 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
isBasicGroup, isBasicGroup,
isSuperGroup, isSuperGroup,
canDeleteForAll, canDeleteForAll,
chatTitle,
contactName, contactName,
onClose, onClose,
leaveChannel, leaveChannel,
deleteHistory, deleteHistory,
deleteChannel, deleteChannel,
}) => { }) => {
const lang = useLang();
const chatTitle = getChatTitle(lang, chat);
const handleDeleteMessageForAll = useCallback(() => { const handleDeleteMessageForAll = useCallback(() => {
deleteHistory({ chatId: chat.id, maxId: chat.lastMessage!.id, shouldDeleteForAll: true }); deleteHistory({ chatId: chat.id, maxId: chat.lastMessage!.id, shouldDeleteForAll: true });
onClose(); onClose();
@ -87,8 +88,6 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
deleteChannel, deleteChannel,
]); ]);
const lang = useLang();
function renderHeader() { function renderHeader() {
return ( return (
<div className="modal-header"> <div className="modal-header">
@ -182,7 +181,6 @@ export default memo(withGlobal<OwnProps>(
isBasicGroup: isChatBasicGroup(chat), isBasicGroup: isChatBasicGroup(chat),
isSuperGroup: isChatSuperGroup(chat), isSuperGroup: isChatSuperGroup(chat),
canDeleteForAll, canDeleteForAll,
chatTitle: getChatTitle(chat),
contactName, contactName,
}; };
}, },

View File

@ -51,7 +51,7 @@ const EmbeddedMessage: FC<OwnProps> = ({
const lang = useLang(); const lang = useLang();
const senderTitle = sender && getSenderTitle(sender); const senderTitle = sender && getSenderTitle(lang, sender);
return ( return (
<div <div

View File

@ -8,6 +8,7 @@ import { formatMediaDateTime, formatPastTimeShort } from '../../util/dateFormat'
import { getColorFromExtension, getFileSizeString } from './helpers/documentInfo'; import { getColorFromExtension, getFileSizeString } from './helpers/documentInfo';
import { getDocumentThumbnailDimensions } from './helpers/mediaDimensions'; import { getDocumentThumbnailDimensions } from './helpers/mediaDimensions';
import renderText from './helpers/renderText'; import renderText from './helpers/renderText';
import useLang from '../../hooks/useLang';
import ProgressSpinner from '../ui/ProgressSpinner'; import ProgressSpinner from '../ui/ProgressSpinner';
import Link from '../ui/Link'; import Link from '../ui/Link';
@ -53,6 +54,7 @@ const File: FC<OwnProps> = ({
onClick, onClick,
onDateClick, onDateClick,
}) => { }) => {
const lang = useLang();
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
let elementRef = useRef<HTMLDivElement>(null); let elementRef = useRef<HTMLDivElement>(null);
if (ref) { if (ref) {
@ -136,13 +138,13 @@ const File: FC<OwnProps> = ({
{!sender && timestamp && ( {!sender && timestamp && (
<> <>
{' '} {' '}
<Link onClick={onDateClick}>{formatMediaDateTime(timestamp * 1000)}</Link> <Link onClick={onDateClick}>{formatMediaDateTime(lang, timestamp * 1000)}</Link>
</> </>
)} )}
</div> </div>
</div> </div>
{sender && timestamp && ( {sender && timestamp && (
<Link onClick={onDateClick}>{formatPastTimeShort(timestamp * 1000)}</Link> <Link onClick={onDateClick}>{formatPastTimeShort(lang, timestamp * 1000)}</Link>
)} )}
</div> </div>
); );

View File

@ -103,7 +103,7 @@ const GroupChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
} }
const handle = withUsername ? chat.username : undefined; const handle = withUsername ? chat.username : undefined;
const groupStatus = getGroupStatus(chat, lang); const groupStatus = getGroupStatus(lang, chat);
const onlineStatus = onlineCount ? `, ${lang('OnlineCount', onlineCount, 'i')}` : undefined; const onlineStatus = onlineCount ? `, ${lang('OnlineCount', onlineCount, 'i')}` : undefined;
return ( return (
@ -125,7 +125,7 @@ const GroupChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
/> />
<div className="info"> <div className="info">
<div className="title"> <div className="title">
<h3>{renderText(getChatTitle(chat))}</h3> <h3>{renderText(getChatTitle(lang, chat))}</h3>
{chat.isVerified && <VerifiedIcon />} {chat.isVerified && <VerifiedIcon />}
</div> </div>
{renderStatusOrTyping()} {renderStatusOrTyping()}
@ -134,7 +134,7 @@ const GroupChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
); );
}; };
function getGroupStatus(chat: ApiChat, lang: LangFn) { function getGroupStatus(lang: LangFn, chat: ApiChat) {
const chatTypeString = lang(getChatTypeString(chat)); const chatTypeString = lang(getChatTypeString(chat));
const { membersCount } = chat; const { membersCount } = chat;

View File

@ -1,8 +1,12 @@
import React, { FC, memo } from '../../lib/teact/teact'; import React, { FC, memo } from '../../lib/teact/teact';
import { ApiMessage, ApiMessageOutgoingStatus } from '../../api/types'; import { ApiMessage, ApiMessageOutgoingStatus } from '../../api/types';
import { formatPastTimeShort } from '../../util/dateFormat'; import { formatPastTimeShort } from '../../util/dateFormat';
import useLang from '../../hooks/useLang';
import MessageOutgoingStatus from './MessageOutgoingStatus'; import MessageOutgoingStatus from './MessageOutgoingStatus';
import './LastMessageMeta.scss'; import './LastMessageMeta.scss';
type OwnProps = { type OwnProps = {
@ -11,12 +15,13 @@ type OwnProps = {
}; };
const LastMessageMeta: FC<OwnProps> = ({ message, outgoingStatus }) => { const LastMessageMeta: FC<OwnProps> = ({ message, outgoingStatus }) => {
const lang = useLang();
return ( return (
<div className="LastMessageMeta"> <div className="LastMessageMeta">
{outgoingStatus && ( {outgoingStatus && (
<MessageOutgoingStatus status={outgoingStatus} /> <MessageOutgoingStatus status={outgoingStatus} />
)} )}
<span className="time">{formatPastTimeShort(message.date * 1000)}</span> <span className="time">{formatPastTimeShort(lang, message.date * 1000)}</span>
</div> </div>
); );
}; };

View File

@ -40,7 +40,7 @@ const PickerSelectedItem: FC<OwnProps & StateProps> = ({
user, user,
className, className,
}) => { }) => {
useLang(); const lang = useLang();
let iconElement: any; let iconElement: any;
let titleText: any; let titleText: any;
@ -65,7 +65,7 @@ const PickerSelectedItem: FC<OwnProps & StateProps> = ({
const name = !chat || (user && !user.isSelf) const name = !chat || (user && !user.isSelf)
? getUserFirstOrLastName(user) ? getUserFirstOrLastName(user)
: getChatTitle(chat, user); : getChatTitle(lang, chat, user);
titleText = name ? renderText(name) : undefined; titleText = name ? renderText(name) : undefined;
} }

View File

@ -104,7 +104,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
return ( return (
<div className={`status ${isUserOnline(user) ? 'online' : ''}`}> <div className={`status ${isUserOnline(user) ? 'online' : ''}`}>
{withUsername && user.username && <span className="handle">{user.username}</span>} {withUsername && user.username && <span className="handle">{user.username}</span>}
<span className="user-status">{getUserStatus(user, lang)}</span> <span className="user-status">{getUserStatus(lang, user)}</span>
</div> </div>
); );
} }

View File

@ -94,7 +94,7 @@ const WebLink: FC<OwnProps> = ({ message, senderTitle, onMessageClick }) => {
className="date" className="date"
onClick={handleMessageClick} onClick={handleMessageClick}
> >
{formatPastTimeShort(message.date * 1000)} {formatPastTimeShort(lang, message.date * 1000)}
</Link> </Link>
</div> </div>
)} )}

View File

@ -46,7 +46,7 @@ export function renderActionMessageText(
text, text,
'%action_origin%', '%action_origin%',
actionOrigin actionOrigin
? (!options.isEmbedded && renderOriginContent(actionOrigin, options.asPlain)) || NBSP ? (!options.isEmbedded && renderOriginContent(lang, actionOrigin, options.asPlain)) || NBSP
: 'User', : 'User',
); );
@ -144,9 +144,9 @@ function renderMessageContent(lang: LangFn, message: ApiMessage, options: Action
); );
} }
function renderOriginContent(origin: ApiUser | ApiChat, asPlain?: boolean) { function renderOriginContent(lang: LangFn, origin: ApiUser | ApiChat, asPlain?: boolean) {
return isChat(origin) return isChat(origin)
? renderChatContent(origin, asPlain) ? renderChatContent(lang, origin, asPlain)
: renderUserContent(origin, asPlain); : renderUserContent(origin, asPlain);
} }
@ -160,8 +160,8 @@ function renderUserContent(sender: ApiUser, asPlain?: boolean): string | TextPar
return <UserLink className="action-link" sender={sender}>{sender && renderText(text!)}</UserLink>; return <UserLink className="action-link" sender={sender}>{sender && renderText(text!)}</UserLink>;
} }
function renderChatContent(chat: ApiChat, asPlain?: boolean): string | TextPart | undefined { function renderChatContent(lang: LangFn, chat: ApiChat, asPlain?: boolean): string | TextPart | undefined {
const text = trimText(getChatTitle(chat)); const text = trimText(getChatTitle(lang, chat));
if (asPlain) { if (asPlain) {
return text; return text;

View File

@ -221,7 +221,7 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
); );
} }
const senderName = getMessageSenderName(chatId, lastMessageSender); const senderName = getMessageSenderName(lang, chatId, lastMessageSender);
return ( return (
<p className="last-message"> <p className="last-message">
@ -257,7 +257,7 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
/> />
<div className="info"> <div className="info">
<div className="title"> <div className="title">
<h3>{renderText(getChatTitle(chat, privateChatUser))}</h3> <h3>{renderText(getChatTitle(lang, chat, privateChatUser))}</h3>
{chat.isVerified && <VerifiedIcon />} {chat.isVerified && <VerifiedIcon />}
{isMuted && <i className="icon-muted-chat" />} {isMuted && <i className="icon-muted-chat" />}
{chat.lastMessage && ( {chat.lastMessage && (

View File

@ -88,13 +88,13 @@ const AudioResults: FC<OwnProps & StateProps & DispatchProps> = ({
key={message.id} key={message.id}
> >
{shouldDrawDateDivider && ( {shouldDrawDateDivider && (
<p className="section-heading">{formatMonthAndYear(new Date(message.date * 1000))}</p> <p className="section-heading">{formatMonthAndYear(lang, new Date(message.date * 1000))}</p>
)} )}
<Audio <Audio
key={message.id} key={message.id}
message={message} message={message}
renderingFor="searchResult" renderingFor="searchResult"
senderTitle={getSenderName(message, chatsById, usersById)} senderTitle={getSenderName(lang, message, chatsById, usersById)}
date={message.date} date={message.date}
lastSyncTime={lastSyncTime} lastSyncTime={lastSyncTime}
className="scroll-item" className="scroll-item"

View File

@ -84,12 +84,12 @@ const ChatMessage: FC<OwnProps & StateProps & DispatchProps> = ({
<div className="info"> <div className="info">
<div className="info-row"> <div className="info-row">
<div className="title"> <div className="title">
<h3>{renderText(getChatTitle(chat, privateChatUser))}</h3> <h3>{renderText(getChatTitle(lang, chat, privateChatUser))}</h3>
{chat.isVerified && <VerifiedIcon />} {chat.isVerified && <VerifiedIcon />}
</div> </div>
<div className="message-date"> <div className="message-date">
<Link className="date"> <Link className="date">
{formatPastTimeShort(message.date * 1000)} {formatPastTimeShort(lang, message.date * 1000)}
</Link> </Link>
</div> </div>

View File

@ -23,7 +23,6 @@ import DateSuggest from './DateSuggest';
import Link from '../../ui/Link'; import Link from '../../ui/Link';
import NothingFound from '../../common/NothingFound'; import NothingFound from '../../common/NothingFound';
import PickerSelectedItem from '../../common/PickerSelectedItem'; import PickerSelectedItem from '../../common/PickerSelectedItem';
import { getTranslation } from '../../../util/langProvider';
export type OwnProps = { export type OwnProps = {
searchQuery?: string; searchQuery?: string;
@ -63,6 +62,8 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
foundIds, globalMessagesByChatId, chatsById, usersById, fetchingStatus, lastSyncTime, foundIds, globalMessagesByChatId, chatsById, usersById, fetchingStatus, lastSyncTime,
onReset, onSearchDateSelect, openChat, addRecentlyFoundChatId, searchMessagesGlobal, setGlobalSearchChatId, onReset, onSearchDateSelect, openChat, addRecentlyFoundChatId, searchMessagesGlobal, setGlobalSearchChatId,
}) => { }) => {
const lang = useLang();
const [shouldShowMoreLocal, setShouldShowMoreLocal] = useState<boolean>(false); const [shouldShowMoreLocal, setShouldShowMoreLocal] = useState<boolean>(false);
const [shouldShowMoreGlobal, setShouldShowMoreGlobal] = useState<boolean>(false); const [shouldShowMoreGlobal, setShouldShowMoreGlobal] = useState<boolean>(false);
@ -114,14 +115,14 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
: []; : [];
return [ return [
...(currentUserId && searchWords(getTranslation('SavedMessages'), searchQuery) ? [currentUserId] : []), ...(currentUserId && searchWords(lang('SavedMessages'), searchQuery) ? [currentUserId] : []),
...sortChatIds(unique([ ...sortChatIds(unique([
...foundContactIds, ...foundContactIds,
...(localChatIds || []), ...(localChatIds || []),
...(localUserIds || []), ...(localUserIds || []),
]), chatsById), ]), chatsById),
]; ];
}, [searchQuery, localContactIds, currentUserId, localChatIds, localUserIds, chatsById, usersById]); }, [searchQuery, localContactIds, currentUserId, lang, localChatIds, localUserIds, chatsById, usersById]);
const globalResults = useMemo(() => { const globalResults = useMemo(() => {
if (!searchQuery || searchQuery.length < MIN_QUERY_LENGTH_FOR_GLOBAL_SEARCH || !globalChatIds || !globalUserIds) { if (!searchQuery || searchQuery.length < MIN_QUERY_LENGTH_FOR_GLOBAL_SEARCH || !globalChatIds || !globalUserIds) {
@ -156,8 +157,6 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
setShouldShowMoreGlobal(!shouldShowMoreGlobal); setShouldShowMoreGlobal(!shouldShowMoreGlobal);
}, [shouldShowMoreGlobal]); }, [shouldShowMoreGlobal]);
const lang = useLang();
function renderFoundMessage(message: ApiMessage) { function renderFoundMessage(message: ApiMessage) {
const text = getMessageSummaryText(lang, message); const text = getMessageSummaryText(lang, message);
const chat = chatsById[message.chatId]; const chat = chatsById[message.chatId];

View File

@ -84,14 +84,14 @@ const FileResults: FC<OwnProps & StateProps & DispatchProps> = ({
key={message.id} key={message.id}
> >
{shouldDrawDateDivider && ( {shouldDrawDateDivider && (
<p className="section-heading">{formatMonthAndYear(new Date(message.date * 1000))}</p> <p className="section-heading">{formatMonthAndYear(lang, new Date(message.date * 1000))}</p>
)} )}
<Document <Document
message={message} message={message}
withDate withDate
datetime={message.date} datetime={message.date}
smaller smaller
sender={getSenderName(message, chatsById, usersById)} sender={getSenderName(lang, message, chatsById, usersById)}
className="scroll-item" className="scroll-item"
onDateClick={handleMessageFocus} onDateClick={handleMessageFocus}
/> />

View File

@ -81,12 +81,12 @@ const LinkResults: FC<OwnProps & StateProps & DispatchProps> = ({
key={message.id} key={message.id}
> >
{shouldDrawDateDivider && ( {shouldDrawDateDivider && (
<p className="section-heading">{formatMonthAndYear(new Date(message.date * 1000))}</p> <p className="section-heading">{formatMonthAndYear(lang, new Date(message.date * 1000))}</p>
)} )}
<WebLink <WebLink
key={message.id} key={message.id}
message={message} message={message}
senderTitle={getSenderName(message, chatsById, usersById)} senderTitle={getSenderName(lang, message, chatsById, usersById)}
onMessageClick={handleMessageFocus} onMessageClick={handleMessageFocus}
/> />
</div> </div>

View File

@ -5,9 +5,10 @@ import {
isChatPrivate, isChatPrivate,
isChatGroup, isChatGroup,
} from '../../../../modules/helpers'; } from '../../../../modules/helpers';
import { LangFn } from '../../../../hooks/useLang';
export function getSenderName( export function getSenderName(
message: ApiMessage, chatsById: Record<number, ApiChat>, usersById: Record<number, ApiUser>, lang: LangFn, message: ApiMessage, chatsById: Record<number, ApiChat>, usersById: Record<number, ApiUser>,
) { ) {
const { senderId } = message; const { senderId } = message;
if (!senderId) { if (!senderId) {
@ -16,14 +17,14 @@ export function getSenderName(
const sender = isChatPrivate(senderId) ? usersById[senderId] : chatsById[senderId]; const sender = isChatPrivate(senderId) ? usersById[senderId] : chatsById[senderId];
let senderName = getSenderTitle(sender); let senderName = getSenderTitle(lang, sender);
const chat = chatsById[message.chatId]; const chat = chatsById[message.chatId];
if (chat) { if (chat) {
if (isChatPrivate(senderId) && (sender as ApiUser).isSelf) { if (isChatPrivate(senderId) && (sender as ApiUser).isSelf) {
senderName = `You → ${getChatTitle(chat)}`; senderName = `${lang('FromYou')}${getChatTitle(lang, chat)}`;
} else if (isChatGroup(chat)) { } else if (isChatGroup(chat)) {
senderName += `${getChatTitle(chat)}`; senderName += `${getChatTitle(lang, chat)}`;
} }
} }

View File

@ -103,7 +103,7 @@ const SettingsPrivacyActiveSessions: FC<StateProps & DispatchProps> = ({
}]} }]}
> >
<div className="multiline-menu-item full-size"> <div className="multiline-menu-item full-size">
<span className="date">{formatPastTimeShort(session.dateActive * 1000)}</span> <span className="date">{formatPastTimeShort(lang, session.dateActive * 1000)}</span>
<span className="title">{session.appName}</span> <span className="title">{session.appName}</span>
<span className="subtitle black tight">{getDeviceEnvironment(session)}</span> <span className="subtitle black tight">{getDeviceEnvironment(session)}</span>
<span className="subtitle">{session.ip} - {getLocation(session)}</span> <span className="subtitle">{session.ip} - {getLocation(session)}</span>

View File

@ -68,7 +68,7 @@ const SettingsPrivacyBlockedUsers: FC<StateProps & DispatchProps> = ({
> >
<Avatar size="medium" user={user} chat={chat} /> <Avatar size="medium" user={user} chat={chat} />
<div className="contact-info"> <div className="contact-info">
<h3>{renderText((isPrivate ? getUserFullName(user) : getChatTitle(chat!)) || '')}</h3> <h3>{renderText((isPrivate ? getUserFullName(user) : getChatTitle(lang, chat!)) || '')}</h3>
{user && user.phoneNumber && ( {user && user.phoneNumber && (
<div className="contact-phone">{formatPhoneNumberWithCode(user.phoneNumber)}</div> <div className="contact-phone">{formatPhoneNumberWithCode(user.phoneNumber)}</div>
)} )}

View File

@ -49,6 +49,8 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps & Dispatc
setPrivacySettings, setPrivacySettings,
onScreenSelect, onScreenSelect,
}) => { }) => {
const lang = useLang();
const selectedContactIds = useMemo(() => { const selectedContactIds = useMemo(() => {
if (!settings) { if (!settings) {
return []; return [];
@ -98,12 +100,12 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps & Dispatc
((isChatPrivate(chat.id) && chat.id !== currentUserId) || isChatGroup(chat)) ((isChatPrivate(chat.id) && chat.id !== currentUserId) || isChatGroup(chat))
&& ( && (
!searchQuery !searchQuery
|| searchWords(getChatTitle(chat), searchQuery) || searchWords(getChatTitle(lang, chat), searchQuery)
|| selectedContactIds.includes(chat.id) || selectedContactIds.includes(chat.id)
) )
)) ))
.map(({ id }) => id); .map(({ id }) => id);
}, [chats, currentUserId, searchQuery, selectedContactIds]); }, [chats, currentUserId, lang, searchQuery, selectedContactIds]);
const handleSelectedContactIdsChange = useCallback((value: number[]) => { const handleSelectedContactIdsChange = useCallback((value: number[]) => {
setNewSelectedContactIds(value); setNewSelectedContactIds(value);
@ -120,8 +122,6 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps & Dispatc
onScreenSelect(SettingsScreens.Privacy); onScreenSelect(SettingsScreens.Privacy);
}, [isAllowList, newSelectedContactIds, onScreenSelect, screen, setPrivacySettings]); }, [isAllowList, newSelectedContactIds, onScreenSelect, screen, setPrivacySettings]);
const lang = useLang();
return ( return (
<div className="NewChat-inner step-1"> <div className="NewChat-inner step-1">
<Picker <Picker

View File

@ -6,6 +6,7 @@ import { withGlobal } from '../../../../lib/teact/teactn';
import { GlobalActions } from '../../../../global/types'; import { GlobalActions } from '../../../../global/types';
import { ApiChat } from '../../../../api/types'; import { ApiChat } from '../../../../api/types';
import useLang from '../../../../hooks/useLang';
import { pick } from '../../../../util/iteratees'; import { pick } from '../../../../util/iteratees';
import searchWords from '../../../../util/searchWords'; import searchWords from '../../../../util/searchWords';
import { prepareChatList, getChatTitle } from '../../../../modules/helpers'; import { prepareChatList, getChatTitle } from '../../../../modules/helpers';
@ -49,6 +50,7 @@ const SettingsFoldersChatFilters: FC<OwnProps & StateProps & DispatchProps> = ({
const { chatFilter } = state; const { chatFilter } = state;
const { selectedChatIds, selectedChatTypes } = selectChatFilters(state, mode, true); const { selectedChatIds, selectedChatTypes } = selectChatFilters(state, mode, true);
const lang = useLang();
const chats = useMemo(() => { const chats = useMemo(() => {
const activeChatArrays = listIds const activeChatArrays = listIds
? prepareChatList(chatsById, listIds, orderedPinnedIds, 'all') ? prepareChatList(chatsById, listIds, orderedPinnedIds, 'all')
@ -78,11 +80,11 @@ const SettingsFoldersChatFilters: FC<OwnProps & StateProps & DispatchProps> = ({
return chats return chats
.filter((chat) => ( .filter((chat) => (
!chatFilter !chatFilter
|| searchWords(getChatTitle(chat), chatFilter) || searchWords(getChatTitle(lang, chat), chatFilter)
|| selectedChatIds.includes(chat.id) || selectedChatIds.includes(chat.id)
)) ))
.map(({ id }) => id); .map(({ id }) => id);
}, [chats, chatFilter, selectedChatIds]); }, [chats, chatFilter, lang, selectedChatIds]);
const handleFilterChange = useCallback((newFilter: string) => { const handleFilterChange = useCallback((newFilter: string) => {
dispatch({ dispatch({

View File

@ -103,7 +103,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
id: folder.id, id: folder.id,
title: folder.title, title: folder.title,
subtitle: getFolderDescriptionText( subtitle: getFolderDescriptionText(
chatsById, usersById, folder, chatIds, lang, notifySettings, notifyExceptions, lang, chatsById, usersById, folder, chatIds, notifySettings, notifyExceptions,
), ),
}; };
}); });

View File

@ -58,6 +58,8 @@ const ForwardPicker: FC<OwnProps & StateProps & DispatchProps> = ({
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const lang = useLang();
useEffect(() => { useEffect(() => {
if (isOpen) { if (isOpen) {
if (!IS_MOBILE_SCREEN) { if (!IS_MOBILE_SCREEN) {
@ -101,10 +103,10 @@ const ForwardPicker: FC<OwnProps & StateProps & DispatchProps> = ({
return true; return true;
} }
return searchWords(getChatTitle(chatsById[id], undefined, id === currentUserId), filter); return searchWords(getChatTitle(lang, chatsById[id], undefined, id === currentUserId), filter);
}), }),
], chatsById, undefined, currentUserId ? [currentUserId] : undefined); ], chatsById, undefined, currentUserId ? [currentUserId] : undefined);
}, [activeListIds, archivedListIds, chatsById, currentUserId, filter]); }, [activeListIds, archivedListIds, chatsById, currentUserId, filter, lang]);
const [viewportIds, getMore] = useInfiniteScroll(loadMoreChats, chatIds, Boolean(filter)); const [viewportIds, getMore] = useInfiniteScroll(loadMoreChats, chatIds, Boolean(filter));
@ -112,8 +114,6 @@ const ForwardPicker: FC<OwnProps & StateProps & DispatchProps> = ({
setFilter(e.currentTarget.value); setFilter(e.currentTarget.value);
}, []); }, []);
const lang = useLang();
const modalHeader = ( const modalHeader = (
<div className="modal-header"> <div className="modal-header">
<Button <Button

View File

@ -48,7 +48,7 @@ const SenderInfo: FC<OwnProps & StateProps & DispatchProps> = ({
} }
const isFromChat = sender.id < 0; const isFromChat = sender.id < 0;
const senderTitle = getSenderTitle(sender); const senderTitle = getSenderTitle(lang, sender);
return ( return (
<div className="SenderInfo" onClick={handleFocusMessage}> <div className="SenderInfo" onClick={handleFocusMessage}>
@ -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 ? lang('lng_mediaview_profile_photo') : formatMediaDateTime(message!.date * 1000)} {isAvatar ? lang('lng_mediaview_profile_photo') : formatMediaDateTime(lang, message!.date * 1000)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -2,7 +2,9 @@ import React, { FC, useCallback } from '../../lib/teact/teact';
import { withGlobal } from '../../lib/teact/teactn'; import { withGlobal } from '../../lib/teact/teactn';
import { GlobalActions } from '../../global/types'; import { GlobalActions } from '../../global/types';
import { ApiAudio, ApiMessage } from '../../api/types'; import {
ApiAudio, ApiChat, ApiMessage, ApiUser,
} from '../../api/types';
import { IS_MOBILE_SCREEN } from '../../util/environment'; import { IS_MOBILE_SCREEN } from '../../util/environment';
import * as mediaLoader from '../../util/mediaLoader'; import * as mediaLoader from '../../util/mediaLoader';
@ -28,14 +30,17 @@ type OwnProps = {
}; };
type StateProps = { type StateProps = {
senderName?: string; sender?: ApiChat | ApiUser;
}; };
type DispatchProps = Pick<GlobalActions, 'focusMessage' | 'closeAudioPlayer'>; type DispatchProps = Pick<GlobalActions, 'focusMessage' | 'closeAudioPlayer'>;
const AudioPlayer: FC<OwnProps & StateProps & DispatchProps> = ({ const AudioPlayer: FC<OwnProps & StateProps & DispatchProps> = ({
message, className, noUi, senderName, focusMessage, closeAudioPlayer, message, className, noUi, sender, focusMessage, closeAudioPlayer,
}) => { }) => {
const lang = useLang();
const senderName = sender ? getSenderTitle(lang, sender) : undefined;
const mediaData = mediaLoader.getFromMemory(getMessageMediaHash(message, 'inline')!) as (string | undefined); const mediaData = mediaLoader.getFromMemory(getMessageMediaHash(message, 'inline')!) as (string | undefined);
const { playPause, isPlaying } = useAudioPlayer( const { playPause, isPlaying } = useAudioPlayer(
getMessageKey(message), getMediaDuration(message)!, mediaData, undefined, undefined, true, getMessageKey(message), getMediaDuration(message)!, mediaData, undefined, undefined, true,
@ -52,8 +57,6 @@ const AudioPlayer: FC<OwnProps & StateProps & DispatchProps> = ({
closeAudioPlayer(); closeAudioPlayer();
}, [closeAudioPlayer, isPlaying, playPause]); }, [closeAudioPlayer, isPlaying, playPause]);
const lang = useLang();
if (noUi) { if (noUi) {
return undefined; return undefined;
} }
@ -117,11 +120,10 @@ function renderVoice(subtitle: string, senderName?: string) {
} }
export default withGlobal<OwnProps>( export default withGlobal<OwnProps>(
(global, { message }) => { (global, { message }): StateProps => {
const sender = selectSender(global, message); const sender = selectSender(global, message);
const senderName = sender ? getSenderTitle(sender) : undefined;
return { senderName }; return { sender };
}, },
(setGlobal, actions): DispatchProps => pick(actions, ['focusMessage', 'closeAudioPlayer']), (setGlobal, actions): DispatchProps => pick(actions, ['focusMessage', 'closeAudioPlayer']),
)(AudioPlayer); )(AudioPlayer);

View File

@ -707,9 +707,9 @@ function renderMessages(
lang('MessageScheduledUntilOnline') lang('MessageScheduledUntilOnline')
)} )}
{isSchedule && dateGroup.originalDate !== SCHEDULED_WHEN_ONLINE && ( {isSchedule && dateGroup.originalDate !== SCHEDULED_WHEN_ONLINE && (
lang('MessageScheduledOn', formatHumanDate(dateGroup.datetime, undefined, true)) lang('MessageScheduledOn', formatHumanDate(lang, dateGroup.datetime, undefined, true))
)} )}
{!isSchedule && formatHumanDate(dateGroup.datetime)} {!isSchedule && formatHumanDate(lang, dateGroup.datetime)}
</span> </span>
</div> </div>
{flatten(senderGroups)} {flatten(senderGroups)}

View File

@ -9,7 +9,7 @@ import {
ApiMessage, ApiMessage,
ApiChat, ApiChat,
ApiTypingStatus, ApiTypingStatus,
MAIN_THREAD_ID, MAIN_THREAD_ID, ApiUser,
} from '../../api/types'; } from '../../api/types';
import { import {
@ -72,16 +72,16 @@ type OwnProps = {
}; };
type StateProps = { type StateProps = {
chat?: ApiChat;
pinnedMessageIds?: number[] | number; pinnedMessageIds?: number[] | number;
messagesById?: Record<number, ApiMessage>; messagesById?: Record<number, ApiMessage>;
canUnpin?: boolean; canUnpin?: boolean;
topMessageTitle?: string; topMessageSender?: ApiChat | ApiUser;
typingStatus?: ApiTypingStatus; typingStatus?: ApiTypingStatus;
isSelectModeActive?: boolean; isSelectModeActive?: boolean;
isLeftColumnShown?: boolean; isLeftColumnShown?: boolean;
isRightColumnShown?: boolean; isRightColumnShown?: boolean;
audioMessage?: ApiMessage; audioMessage?: ApiMessage;
chatTitleLength?: number;
chatsById?: Record<number, ApiChat>; chatsById?: Record<number, ApiChat>;
originChatId: number; originChatId: number;
messagesCount?: number; messagesCount?: number;
@ -102,13 +102,13 @@ const MiddleHeader: FC<OwnProps & StateProps & DispatchProps> = ({
pinnedMessageIds, pinnedMessageIds,
messagesById, messagesById,
canUnpin, canUnpin,
topMessageTitle, topMessageSender,
typingStatus, typingStatus,
isSelectModeActive, isSelectModeActive,
isLeftColumnShown, isLeftColumnShown,
isRightColumnShown, isRightColumnShown,
audioMessage, audioMessage,
chatTitleLength, chat,
chatsById, chatsById,
originChatId, originChatId,
messagesCount, messagesCount,
@ -123,10 +123,14 @@ const MiddleHeader: FC<OwnProps & StateProps & DispatchProps> = ({
toggleLeftColumn, toggleLeftColumn,
exitMessageSelectMode, exitMessageSelectMode,
}) => { }) => {
const lang = useLang();
const [pinnedMessageIndex, setPinnedMessageIndex] = useState(0); const [pinnedMessageIndex, setPinnedMessageIndex] = useState(0);
const pinnedMessageId = Array.isArray(pinnedMessageIds) ? pinnedMessageIds[pinnedMessageIndex] : pinnedMessageIds; const pinnedMessageId = Array.isArray(pinnedMessageIds) ? pinnedMessageIds[pinnedMessageIndex] : pinnedMessageIds;
const pinnedMessage = messagesById && pinnedMessageId ? messagesById[pinnedMessageId] : undefined; const pinnedMessage = messagesById && pinnedMessageId ? messagesById[pinnedMessageId] : undefined;
const pinnedMessagesCount = Array.isArray(pinnedMessageIds) ? pinnedMessageIds.length : (pinnedMessageIds ? 1 : 0); const pinnedMessagesCount = Array.isArray(pinnedMessageIds) ? pinnedMessageIds.length : (pinnedMessageIds ? 1 : 0);
const chatTitleLength = chat && getChatTitle(lang, chat).length;
const topMessageTitle = topMessageSender ? getSenderTitle(lang, topMessageSender) : undefined;
useEffect(() => { useEffect(() => {
if (threadId === MAIN_THREAD_ID && lastSyncTime) { if (threadId === MAIN_THREAD_ID && lastSyncTime) {
@ -201,13 +205,13 @@ const MiddleHeader: FC<OwnProps & StateProps & DispatchProps> = ({
let isActive = false; let isActive = false;
const totalCount = Object.values(chatsById).reduce((total, chat) => { const totalCount = Object.values(chatsById).reduce((total, currentChat) => {
if (isChatArchived(chat)) { if (isChatArchived(currentChat)) {
return total; return total;
} }
const count = chat.unreadCount || 0; const count = currentChat.unreadCount || 0;
if (count && (!chat.isMuted || chat.unreadMentionsCount)) { if (count && (!currentChat.isMuted || currentChat.unreadMentionsCount)) {
isActive = true; isActive = true;
} }
@ -284,8 +288,6 @@ const MiddleHeader: FC<OwnProps & StateProps & DispatchProps> = ({
} }
}, [shouldUseStackedToolsClass, canRevealTools, canToolsCollideWithChatInfo, isRightColumnShown]); }, [shouldUseStackedToolsClass, canRevealTools, canToolsCollideWithChatInfo, isRightColumnShown]);
const lang = useLang();
function renderInfo() { function renderInfo() {
return ( return (
messageListType === 'thread' && threadId === MAIN_THREAD_ID ? ( messageListType === 'thread' && threadId === MAIN_THREAD_ID ? (
@ -435,7 +437,7 @@ export default memo(withGlobal<OwnProps>(
isRightColumnShown: selectIsRightColumnShown(global), isRightColumnShown: selectIsRightColumnShown(global),
isSelectModeActive: selectIsInSelectMode(global), isSelectModeActive: selectIsInSelectMode(global),
audioMessage, audioMessage,
chatTitleLength: chat && getChatTitle(chat).length, chat,
chatsById, chatsById,
originChatId: originChat ? originChat.id : chatId, originChatId: originChat ? originChat.id : chatId,
messagesCount, messagesCount,
@ -454,14 +456,13 @@ export default memo(withGlobal<OwnProps>(
if (threadId !== MAIN_THREAD_ID) { if (threadId !== MAIN_THREAD_ID) {
const pinnedMessageId = selectThreadTopMessageId(global, chatId, threadId); const pinnedMessageId = selectThreadTopMessageId(global, chatId, threadId);
const message = pinnedMessageId ? selectChatMessage(global, chatId, pinnedMessageId) : undefined; const message = pinnedMessageId ? selectChatMessage(global, chatId, pinnedMessageId) : undefined;
const sender = message ? selectForwardedSender(global, message) : undefined; const topMessageSender = message ? selectForwardedSender(global, message) : undefined;
const topMessageTitle = sender ? getSenderTitle(sender) : undefined;
return { return {
...state, ...state,
pinnedMessageIds: pinnedMessageId, pinnedMessageIds: pinnedMessageId,
canUnpin: false, canUnpin: false,
topMessageTitle, topMessageSender,
}; };
} }

View File

@ -636,7 +636,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
let senderTitle; let senderTitle;
let senderColor; let senderColor;
if (senderPeer) { if (senderPeer) {
senderTitle = getSenderTitle(senderPeer); senderTitle = getSenderTitle(lang, senderPeer);
if (!asForwarded) { if (!asForwarded) {
senderColor = `color-${getUserColorKey(senderPeer)}`; senderColor = `color-${getUserColorKey(senderPeer)}`;

View File

@ -157,7 +157,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
if (user) { if (user) {
return ( return (
<div className={`status ${isUserOnline(user) ? 'online' : ''}`}> <div className={`status ${isUserOnline(user) ? 'online' : ''}`}>
<span className="user-status">{getUserStatus(user, lang)}</span> <span className="user-status">{getUserStatus(lang, user)}</span>
</div> </div>
); );
} }

View File

@ -13,6 +13,7 @@ import { getFirstLetters } from '../../util/textFormat';
import useMedia from '../../hooks/useMedia'; import useMedia from '../../hooks/useMedia';
import useBlurSync from '../../hooks/useBlurSync'; import useBlurSync from '../../hooks/useBlurSync';
import usePrevious from '../../hooks/usePrevious'; import usePrevious from '../../hooks/usePrevious';
import useLang from '../../hooks/useLang';
import Spinner from '../ui/Spinner'; import Spinner from '../ui/Spinner';
@ -37,6 +38,7 @@ const ProfilePhoto: FC<OwnProps> = ({
lastSyncTime, lastSyncTime,
onClick, onClick,
}) => { }) => {
const lang = useLang();
const isDeleted = user && isDeletedUser(user); const isDeleted = user && isDeletedUser(user);
function getMediaHash(size: 'normal' | 'big' = 'big', forceAvatar?: boolean) { function getMediaHash(size: 'normal' | 'big' = 'big', forceAvatar?: boolean) {
@ -80,7 +82,7 @@ const ProfilePhoto: FC<OwnProps> = ({
const userFullName = getUserFullName(user); const userFullName = getUserFullName(user);
content = userFullName ? getFirstLetters(userFullName, 2) : undefined; content = userFullName ? getFirstLetters(userFullName, 2) : undefined;
} else if (!imageSrc && chat) { } else if (!imageSrc && chat) {
const title = getChatTitle(chat); const title = getChatTitle(lang, chat);
content = title && getFirstLetters(title, isChatPrivate(chat.id) ? 2 : 1); content = title && getFirstLetters(title, isChatPrivate(chat.id) ? 2 : 1);
} else { } else {
content = ( content = (

View File

@ -100,7 +100,7 @@ const RightSearch: FC<OwnProps & StateProps & DispatchProps> = ({
const renderSearchResult = ({ const renderSearchResult = ({
message, senderUser, senderChat, onClick, message, senderUser, senderChat, onClick,
}: Result) => { }: Result) => {
const title = senderChat ? getChatTitle(senderChat) : getUserFullName(senderUser); const title = senderChat ? getChatTitle(lang, senderChat) : getUserFullName(senderUser);
const text = getMessageSummaryText(lang, message); const text = getMessageSummaryText(lang, message);
return ( return (

View File

@ -11,7 +11,6 @@ import { NotifyException, NotifySettings } from '../../types';
import { ARCHIVED_FOLDER_ID } from '../../config'; import { ARCHIVED_FOLDER_ID } from '../../config';
import { orderBy } from '../../util/iteratees'; import { orderBy } from '../../util/iteratees';
import { getUserFirstOrLastName } from './users'; import { getUserFirstOrLastName } from './users';
import { getTranslation } from '../../util/langProvider';
import { LangFn } from '../../hooks/useLang'; import { LangFn } from '../../hooks/useLang';
export function isChatPrivate(chatId: number) { export function isChatPrivate(chatId: number) {
@ -60,11 +59,11 @@ export function getPrivateChatUserId(chat: ApiChat) {
} }
// TODO Get rid of `user` // TODO Get rid of `user`
export function getChatTitle(chat: ApiChat, user?: ApiUser, isSelf = false) { export function getChatTitle(lang: LangFn, chat: ApiChat, user?: ApiUser, isSelf = false) {
if (isSelf || (user && chat.id === user.id && user.isSelf)) { if (isSelf || (user && chat.id === user.id && user.isSelf)) {
return getTranslation('SavedMessages'); return lang('SavedMessages');
} }
return chat.title || getTranslation('HiddenName'); return chat.title || lang('HiddenName');
} }
export function getChatDescription(chat: ApiChat) { export function getChatDescription(chat: ApiChat) {
@ -388,11 +387,11 @@ export function getFolderUnreadDialogs(
} }
export function getFolderDescriptionText( export function getFolderDescriptionText(
lang: LangFn,
chatsById: Record<number, ApiChat>, chatsById: Record<number, ApiChat>,
usersById: Record<number, ApiUser>, usersById: Record<number, ApiUser>,
folder: ApiChatFolder, folder: ApiChatFolder,
chatIdsCache: number[], chatIdsCache: number[],
lang: LangFn,
notifySettings: NotifySettings, notifySettings: NotifySettings,
notifyExceptions?: Record<number, NotifyException>, notifyExceptions?: Record<number, NotifyException>,
) { ) {
@ -453,13 +452,13 @@ export function isChat(chatOrUser?: ApiUser | ApiChat): chatOrUser is ApiChat {
return chatOrUser.id < 0; return chatOrUser.id < 0;
} }
export function getMessageSenderName(chatId: number, sender?: ApiUser) { export function getMessageSenderName(lang: LangFn, chatId: number, sender?: ApiUser) {
if (!sender || isChatPrivate(chatId)) { if (!sender || isChatPrivate(chatId)) {
return undefined; return undefined;
} }
if (sender.isSelf) { if (sender.isSelf) {
return getTranslation('FromYou'); return lang('FromYou');
} }
return getUserFirstOrLastName(sender); return getUserFirstOrLastName(sender);

View File

@ -197,8 +197,8 @@ export function isAnonymousOwnMessage(message: ApiMessage) {
return Boolean(message.senderId) && message.senderId! < 0 && isOwnMessage(message); return Boolean(message.senderId) && message.senderId! < 0 && isOwnMessage(message);
} }
export function getSenderTitle(sender: ApiUser | ApiChat) { export function getSenderTitle(lang: LangFn, sender: ApiUser | ApiChat) {
return sender.id > 0 ? getUserFullName(sender as ApiUser) : getChatTitle(sender as ApiChat); return sender.id > 0 ? getUserFullName(sender as ApiUser) : getChatTitle(lang, sender as ApiChat);
} }
export function getSendingState(message: ApiMessage) { export function getSendingState(message: ApiMessage) {

View File

@ -64,7 +64,7 @@ export function getUserFullName(user?: ApiUser) {
return undefined; return undefined;
} }
export function getUserStatus(user: ApiUser, lang: LangFn) { export function getUserStatus(lang: LangFn, user: ApiUser) {
if (user.id === SERVICE_NOTIFICATIONS_USER_ID) { if (user.id === SERVICE_NOTIFICATIONS_USER_ID) {
return lang('ServiceNotifications').toLowerCase(); return lang('ServiceNotifications').toLowerCase();
} }
@ -137,7 +137,7 @@ export function getUserStatus(user: ApiUser, lang: LangFn) {
return lang('LastSeen.YesterdayAt', formatTime(wasOnlineDate)); return lang('LastSeen.YesterdayAt', formatTime(wasOnlineDate));
} }
return lang('LastSeen.AtDate', formatFullDate(wasOnlineDate)); return lang('LastSeen.AtDate', formatFullDate(lang, wasOnlineDate));
} }
case 'userStatusOnline': { case 'userStatusOnline': {

View File

@ -1,4 +1,4 @@
import { getTranslation } from './langProvider'; import { LangFn } from '../hooks/useLang';
const WEEKDAYS_FULL = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; const WEEKDAYS_FULL = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
const MONTHS_FULL = [ const MONTHS_FULL = [
@ -39,7 +39,7 @@ export function formatTime(datetime: number | Date) {
return `${hours}:${minutes}`; return `${hours}:${minutes}`;
} }
export function formatPastTimeShort(datetime: number | Date) { export function formatPastTimeShort(lang: LangFn, datetime: number | Date) {
const date = typeof datetime === 'number' ? new Date(datetime) : datetime; const date = typeof datetime === 'number' ? new Date(datetime) : datetime;
const today = getDayStart(new Date()); const today = getDayStart(new Date());
@ -50,45 +50,45 @@ export function formatPastTimeShort(datetime: number | Date) {
const weekAgo = new Date(today); const weekAgo = new Date(today);
weekAgo.setDate(today.getDate() - 7); weekAgo.setDate(today.getDate() - 7);
if (date >= weekAgo) { if (date >= weekAgo) {
return getTranslation(`Weekday.Short${WEEKDAYS_FULL[date.getDay()]}`); return lang(`Weekday.Short${WEEKDAYS_FULL[date.getDay()]}`);
} }
const withYear = date.getFullYear() !== today.getFullYear(); const withYear = date.getFullYear() !== today.getFullYear();
const format = ( const format = (
getTranslation(withYear ? 'formatDateScheduleYear' : 'formatDateSchedule') lang(withYear ? 'formatDateScheduleYear' : 'formatDateSchedule')
|| (withYear ? 'd MMM yyyy' : 'd MMM') || (withYear ? 'd MMM yyyy' : 'd MMM')
); );
return formatDate(date, format); return formatDate(lang, date, format);
} }
export function formatFullDate(datetime: number | Date) { export function formatFullDate(lang: LangFn, datetime: number | Date) {
const date = typeof datetime === 'number' ? new Date(datetime) : datetime; const date = typeof datetime === 'number' ? new Date(datetime) : datetime;
const format = getTranslation('formatterYearMax') || 'dd.MM.yyyy'; const format = lang('formatterYearMax') || 'dd.MM.yyyy';
return formatDate(date, format); return formatDate(lang, date, format);
} }
export function formatMonthAndYear(date: Date, isShort = false) { export function formatMonthAndYear(lang: LangFn, date: Date, isShort = false) {
const format = getTranslation(isShort ? 'formatterMonthYear2' : 'formatterMonthYear') || 'MMM yyyy'; const format = lang(isShort ? 'formatterMonthYear2' : 'formatterMonthYear') || 'MMM yyyy';
return formatDate(date, format); return formatDate(lang, date, format);
} }
export function formatHumanDate(datetime: number | Date, isShort = false, noWeekdays = false) { export function formatHumanDate(lang: LangFn, datetime: number | Date, isShort = false, noWeekdays = false) {
const date = typeof datetime === 'number' ? new Date(datetime) : datetime; const date = typeof datetime === 'number' ? new Date(datetime) : datetime;
const today = getDayStart(new Date()); const today = getDayStart(new Date());
if (!noWeekdays) { if (!noWeekdays) {
if (toIsoString(date) === toIsoString(today)) { if (toIsoString(date) === toIsoString(today)) {
return (isShort ? lowerFirst : upperFirst)(getTranslation('Weekday.Today')); return (isShort ? lowerFirst : upperFirst)(lang('Weekday.Today'));
} }
const yesterday = new Date(today); const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1); yesterday.setDate(today.getDate() - 1);
if (toIsoString(date) === toIsoString(yesterday)) { if (toIsoString(date) === toIsoString(yesterday)) {
return (isShort ? lowerFirst : upperFirst)(getTranslation('Weekday.Yesterday')); return (isShort ? lowerFirst : upperFirst)(lang('Weekday.Yesterday'));
} }
const weekAgo = new Date(today); const weekAgo = new Date(today);
@ -98,8 +98,8 @@ export function formatHumanDate(datetime: number | Date, isShort = false, noWeek
if (date >= weekAgo && date <= weekAhead) { if (date >= weekAgo && date <= weekAhead) {
const weekDay = WEEKDAYS_FULL[date.getDay()]; const weekDay = WEEKDAYS_FULL[date.getDay()];
return isShort return isShort
? lowerFirst(getTranslation(`Weekday.Short${weekDay}`)) ? lowerFirst(lang(`Weekday.Short${weekDay}`))
: upperFirst(getTranslation(`Weekday.${weekDay}`)); : upperFirst(lang(`Weekday.${weekDay}`));
} }
} }
@ -107,29 +107,29 @@ export function formatHumanDate(datetime: number | Date, isShort = false, noWeek
const formatKey = isShort const formatKey = isShort
? (withYear ? 'formatDateScheduleYear' : 'formatDateSchedule') ? (withYear ? 'formatDateScheduleYear' : 'formatDateSchedule')
: (withYear ? 'chatFullDate' : 'chatDate'); : (withYear ? 'chatFullDate' : 'chatDate');
const format = getTranslation(formatKey) || 'd MMMM yyyy'; const format = lang(formatKey) || 'd MMMM yyyy';
return (isShort ? lowerFirst : upperFirst)(formatDate(date, format)); return (isShort ? lowerFirst : upperFirst)(formatDate(lang, date, format));
} }
function formatDate(date: Date, format: string) { function formatDate(lang: LangFn, date: Date, format: string) {
const day = date.getDate(); const day = date.getDate();
const monthIndex = date.getMonth(); const monthIndex = date.getMonth();
return format return format
.replace('LLLL', getTranslation(MONTHS_FULL[monthIndex])) .replace('LLLL', lang(MONTHS_FULL[monthIndex]))
.replace('MMMM', getTranslation(`Month.Gen${MONTHS_FULL[monthIndex]}`)) .replace('MMMM', lang(`Month.Gen${MONTHS_FULL[monthIndex]}`))
.replace('MMM', getTranslation(`Month.Short${MONTHS_FULL[monthIndex]}`)) .replace('MMM', lang(`Month.Short${MONTHS_FULL[monthIndex]}`))
.replace('MM', String(monthIndex + 1).padStart(2, '0')) .replace('MM', String(monthIndex + 1).padStart(2, '0'))
.replace('dd', String(day).padStart(2, '0')) .replace('dd', String(day).padStart(2, '0'))
.replace('d', String(day)) .replace('d', String(day))
.replace('yyyy', String(date.getFullYear())); .replace('yyyy', String(date.getFullYear()));
} }
export function formatMediaDateTime(datetime: number | Date) { export function formatMediaDateTime(lang: LangFn, datetime: number | Date) {
const date = typeof datetime === 'number' ? new Date(datetime) : datetime; const date = typeof datetime === 'number' ? new Date(datetime) : datetime;
return `${formatHumanDate(date, true)}, ${formatTime(date)}`; return `${formatHumanDate(lang, date, true)}, ${formatTime(date)}`;
} }
export function formatMediaDuration(duration: number) { export function formatMediaDuration(duration: number) {

View File

@ -239,14 +239,14 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage) {
{ asPlain: true }, { asPlain: true },
) as string; ) as string;
} else { } else {
const senderName = getMessageSenderName(chat.id, messageSender); const senderName = getMessageSenderName(getTranslation, chat.id, messageSender);
const summary = getMessageSummaryText(getTranslation, message); const summary = getMessageSummaryText(getTranslation, message);
body = senderName ? `${senderName}: ${summary}` : summary; body = senderName ? `${senderName}: ${summary}` : summary;
} }
return { return {
title: getChatTitle(chat, privateChatUser), title: getChatTitle(getTranslation, chat, privateChatUser),
body, body,
}; };
} }