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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -104,7 +104,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps & DispatchProps> = ({
return (
<div className={`status ${isUserOnline(user) ? 'online' : ''}`}>
{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>
);
}

View File

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

View File

@ -46,7 +46,7 @@ export function renderActionMessageText(
text,
'%action_origin%',
actionOrigin
? (!options.isEmbedded && renderOriginContent(actionOrigin, options.asPlain)) || NBSP
? (!options.isEmbedded && renderOriginContent(lang, actionOrigin, options.asPlain)) || NBSP
: '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)
? renderChatContent(origin, asPlain)
? renderChatContent(lang, 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>;
}
function renderChatContent(chat: ApiChat, asPlain?: boolean): string | TextPart | undefined {
const text = trimText(getChatTitle(chat));
function renderChatContent(lang: LangFn, chat: ApiChat, asPlain?: boolean): string | TextPart | undefined {
const text = trimText(getChatTitle(lang, chat));
if (asPlain) {
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 (
<p className="last-message">
@ -257,7 +257,7 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
/>
<div className="info">
<div className="title">
<h3>{renderText(getChatTitle(chat, privateChatUser))}</h3>
<h3>{renderText(getChatTitle(lang, chat, privateChatUser))}</h3>
{chat.isVerified && <VerifiedIcon />}
{isMuted && <i className="icon-muted-chat" />}
{chat.lastMessage && (

View File

@ -88,13 +88,13 @@ const AudioResults: FC<OwnProps & StateProps & DispatchProps> = ({
key={message.id}
>
{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
key={message.id}
message={message}
renderingFor="searchResult"
senderTitle={getSenderName(message, chatsById, usersById)}
senderTitle={getSenderName(lang, message, chatsById, usersById)}
date={message.date}
lastSyncTime={lastSyncTime}
className="scroll-item"

View File

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

View File

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

View File

@ -84,14 +84,14 @@ const FileResults: FC<OwnProps & StateProps & DispatchProps> = ({
key={message.id}
>
{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
message={message}
withDate
datetime={message.date}
smaller
sender={getSenderName(message, chatsById, usersById)}
sender={getSenderName(lang, message, chatsById, usersById)}
className="scroll-item"
onDateClick={handleMessageFocus}
/>

View File

@ -81,12 +81,12 @@ const LinkResults: FC<OwnProps & StateProps & DispatchProps> = ({
key={message.id}
>
{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
key={message.id}
message={message}
senderTitle={getSenderName(message, chatsById, usersById)}
senderTitle={getSenderName(lang, message, chatsById, usersById)}
onMessageClick={handleMessageFocus}
/>
</div>

View File

@ -5,9 +5,10 @@ import {
isChatPrivate,
isChatGroup,
} from '../../../../modules/helpers';
import { LangFn } from '../../../../hooks/useLang';
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;
if (!senderId) {
@ -16,14 +17,14 @@ export function getSenderName(
const sender = isChatPrivate(senderId) ? usersById[senderId] : chatsById[senderId];
let senderName = getSenderTitle(sender);
let senderName = getSenderTitle(lang, sender);
const chat = chatsById[message.chatId];
if (chat) {
if (isChatPrivate(senderId) && (sender as ApiUser).isSelf) {
senderName = `You → ${getChatTitle(chat)}`;
senderName = `${lang('FromYou')}${getChatTitle(lang, 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">
<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="subtitle black tight">{getDeviceEnvironment(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} />
<div className="contact-info">
<h3>{renderText((isPrivate ? getUserFullName(user) : getChatTitle(chat!)) || '')}</h3>
<h3>{renderText((isPrivate ? getUserFullName(user) : getChatTitle(lang, chat!)) || '')}</h3>
{user && user.phoneNumber && (
<div className="contact-phone">{formatPhoneNumberWithCode(user.phoneNumber)}</div>
)}

View File

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

View File

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

View File

@ -103,7 +103,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
id: folder.id,
title: folder.title,
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
const inputRef = useRef<HTMLInputElement>(null);
const lang = useLang();
useEffect(() => {
if (isOpen) {
if (!IS_MOBILE_SCREEN) {
@ -101,10 +103,10 @@ const ForwardPicker: FC<OwnProps & StateProps & DispatchProps> = ({
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);
}, [activeListIds, archivedListIds, chatsById, currentUserId, filter]);
}, [activeListIds, archivedListIds, chatsById, currentUserId, filter, lang]);
const [viewportIds, getMore] = useInfiniteScroll(loadMoreChats, chatIds, Boolean(filter));
@ -112,8 +114,6 @@ const ForwardPicker: FC<OwnProps & StateProps & DispatchProps> = ({
setFilter(e.currentTarget.value);
}, []);
const lang = useLang();
const modalHeader = (
<div className="modal-header">
<Button

View File

@ -48,7 +48,7 @@ const SenderInfo: FC<OwnProps & StateProps & DispatchProps> = ({
}
const isFromChat = sender.id < 0;
const senderTitle = getSenderTitle(sender);
const senderTitle = getSenderTitle(lang, sender);
return (
<div className="SenderInfo" onClick={handleFocusMessage}>
@ -62,7 +62,7 @@ const SenderInfo: FC<OwnProps & StateProps & DispatchProps> = ({
{senderTitle && renderText(senderTitle)}
</div>
<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>

View File

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

View File

@ -707,9 +707,9 @@ function renderMessages(
lang('MessageScheduledUntilOnline')
)}
{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>
</div>
{flatten(senderGroups)}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -64,7 +64,7 @@ export function getUserFullName(user?: ApiUser) {
return undefined;
}
export function getUserStatus(user: ApiUser, lang: LangFn) {
export function getUserStatus(lang: LangFn, user: ApiUser) {
if (user.id === SERVICE_NOTIFICATIONS_USER_ID) {
return lang('ServiceNotifications').toLowerCase();
}
@ -137,7 +137,7 @@ export function getUserStatus(user: ApiUser, lang: LangFn) {
return lang('LastSeen.YesterdayAt', formatTime(wasOnlineDate));
}
return lang('LastSeen.AtDate', formatFullDate(wasOnlineDate));
return lang('LastSeen.AtDate', formatFullDate(lang, wasOnlineDate));
}
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 MONTHS_FULL = [
@ -39,7 +39,7 @@ export function formatTime(datetime: number | Date) {
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 today = getDayStart(new Date());
@ -50,45 +50,45 @@ export function formatPastTimeShort(datetime: number | Date) {
const weekAgo = new Date(today);
weekAgo.setDate(today.getDate() - 7);
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 format = (
getTranslation(withYear ? 'formatDateScheduleYear' : 'formatDateSchedule')
lang(withYear ? 'formatDateScheduleYear' : 'formatDateSchedule')
|| (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 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) {
const format = getTranslation(isShort ? 'formatterMonthYear2' : 'formatterMonthYear') || 'MMM yyyy';
export function formatMonthAndYear(lang: LangFn, date: Date, isShort = false) {
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 today = getDayStart(new Date());
if (!noWeekdays) {
if (toIsoString(date) === toIsoString(today)) {
return (isShort ? lowerFirst : upperFirst)(getTranslation('Weekday.Today'));
return (isShort ? lowerFirst : upperFirst)(lang('Weekday.Today'));
}
const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1);
if (toIsoString(date) === toIsoString(yesterday)) {
return (isShort ? lowerFirst : upperFirst)(getTranslation('Weekday.Yesterday'));
return (isShort ? lowerFirst : upperFirst)(lang('Weekday.Yesterday'));
}
const weekAgo = new Date(today);
@ -98,8 +98,8 @@ export function formatHumanDate(datetime: number | Date, isShort = false, noWeek
if (date >= weekAgo && date <= weekAhead) {
const weekDay = WEEKDAYS_FULL[date.getDay()];
return isShort
? lowerFirst(getTranslation(`Weekday.Short${weekDay}`))
: upperFirst(getTranslation(`Weekday.${weekDay}`));
? lowerFirst(lang(`Weekday.Short${weekDay}`))
: upperFirst(lang(`Weekday.${weekDay}`));
}
}
@ -107,29 +107,29 @@ export function formatHumanDate(datetime: number | Date, isShort = false, noWeek
const formatKey = isShort
? (withYear ? 'formatDateScheduleYear' : 'formatDateSchedule')
: (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 monthIndex = date.getMonth();
return format
.replace('LLLL', getTranslation(MONTHS_FULL[monthIndex]))
.replace('MMMM', getTranslation(`Month.Gen${MONTHS_FULL[monthIndex]}`))
.replace('MMM', getTranslation(`Month.Short${MONTHS_FULL[monthIndex]}`))
.replace('LLLL', lang(MONTHS_FULL[monthIndex]))
.replace('MMMM', lang(`Month.Gen${MONTHS_FULL[monthIndex]}`))
.replace('MMM', lang(`Month.Short${MONTHS_FULL[monthIndex]}`))
.replace('MM', String(monthIndex + 1).padStart(2, '0'))
.replace('dd', String(day).padStart(2, '0'))
.replace('d', String(day))
.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;
return `${formatHumanDate(date, true)}, ${formatTime(date)}`;
return `${formatHumanDate(lang, date, true)}, ${formatTime(date)}`;
}
export function formatMediaDuration(duration: number) {

View File

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