fix: toast strings are stripped as we don't render html there

pull/3206/head
Audric Ackermann 9 months ago
parent f7b21ab262
commit b48bfef58d

@ -30,7 +30,11 @@
"adminRemoveAsAdmin": "Remove as Admin", "adminRemoveAsAdmin": "Remove as Admin",
"adminRemoveCommunityNone": "There are no Admins in this Community.", "adminRemoveCommunityNone": "There are no Admins in this Community.",
"adminRemoveFailed": "Failed to remove {name} as Admin.", "adminRemoveFailed": "Failed to remove {name} as Admin.",
"adminRemoveFailedMultiple": "Failed to remove {name} and {count} others as Admin.",
"adminRemoveFailedOther": "Failed to remove {name} and {other_name} as Admin.",
"adminRemovedUser": "<b>{name}</b> was removed as Admin.", "adminRemovedUser": "<b>{name}</b> was removed as Admin.",
"adminRemovedUserMultiple": "<b>{name}</b> and <b>{count} others</b> were removed as Admin.",
"adminRemovedUserOther": "<b>{name}</b> and <b>{other_name}</b> were removed as Admin.",
"adminSendingPromotion": "Sending admin promotion", "adminSendingPromotion": "Sending admin promotion",
"adminSettings": "Admin Settings", "adminSettings": "Admin Settings",
"adminTwoPromotedToAdmin": "<b>{name}</b> and <b>{other_name}</b> were promoted to Admin.", "adminTwoPromotedToAdmin": "<b>{name}</b> and <b>{other_name}</b> were promoted to Admin.",
@ -67,6 +71,7 @@
"attachmentsDownload": "Download Attachment", "attachmentsDownload": "Download Attachment",
"attachmentsDuration": "Duration:", "attachmentsDuration": "Duration:",
"attachmentsErrorLoad": "Error attaching file", "attachmentsErrorLoad": "Error attaching file",
"attachmentsErrorMediaSelection": "Failed to select attachment",
"attachmentsErrorNoApp": "Can't find an app to select media.", "attachmentsErrorNoApp": "Can't find an app to select media.",
"attachmentsErrorNotSupported": "This file type is not supported.", "attachmentsErrorNotSupported": "This file type is not supported.",
"attachmentsErrorNumber": "Unable to send more than 32 image and video files at once.", "attachmentsErrorNumber": "Unable to send more than 32 image and video files at once.",
@ -143,7 +148,7 @@
"callsPermissionsRequiredDescription": "You can enable the \"Voice and Video Calls\" permission in Privacy Settings.", "callsPermissionsRequiredDescription": "You can enable the \"Voice and Video Calls\" permission in Privacy Settings.",
"callsReconnecting": "Reconnecting…", "callsReconnecting": "Reconnecting…",
"callsRinging": "Ringing...", "callsRinging": "Ringing...",
"callsSessionCall": "Session Call", "callsSessionCall": "{app_name} Call",
"callsSettings": "Calls (Beta)", "callsSettings": "Calls (Beta)",
"callsVoiceAndVideo": "Voice and Video Calls", "callsVoiceAndVideo": "Voice and Video Calls",
"callsVoiceAndVideoBeta": "Voice and Video Calls (Beta)", "callsVoiceAndVideoBeta": "Voice and Video Calls (Beta)",
@ -243,7 +248,7 @@
"create": "Create", "create": "Create",
"cut": "Cut", "cut": "Cut",
"databaseErrorGeneric": "A database error occurred.<br/><br/>Export your application logs to share for troubleshooting. If this is unsuccessful, reinstall {app_name} and restore your account.<br/><br/>Warning: This will result in loss of all messages, attachments, and account data older than two weeks.", "databaseErrorGeneric": "A database error occurred.<br/><br/>Export your application logs to share for troubleshooting. If this is unsuccessful, reinstall {app_name} and restore your account.<br/><br/>Warning: This will result in loss of all messages, attachments, and account data older than two weeks.",
"databaseErrorTimeout": "We've noticed {app_name} is taking a long time to start.<br/><br/>You can continue to wait, export your device logs to share for troubleshooting, or try restarting Session.", "databaseErrorTimeout": "We've noticed {app_name} is taking a long time to start.<br/><br/>You can continue to wait, export your device logs to share for troubleshooting, or try restarting {app_name}.",
"databaseErrorUpdate": "Your app database is incompatible with this version of {app_name}. Reinstall the app and restore your account to generate a new database and continue using {app_name}.<br/><br/>Warning: This will result in the loss of all messages and attachments older than two weeks.", "databaseErrorUpdate": "Your app database is incompatible with this version of {app_name}. Reinstall the app and restore your account to generate a new database and continue using {app_name}.<br/><br/>Warning: This will result in the loss of all messages and attachments older than two weeks.",
"databaseOptimizing": "Optimizing Database", "databaseOptimizing": "Optimizing Database",
"debugLog": "Debug Log", "debugLog": "Debug Log",
@ -261,6 +266,8 @@
"deleteAfterLegacyDisappearingMessagesLegacy": "Legacy", "deleteAfterLegacyDisappearingMessagesLegacy": "Legacy",
"deleteAfterLegacyDisappearingMessagesOriginal": "Original version of disappearing messages.", "deleteAfterLegacyDisappearingMessagesOriginal": "Original version of disappearing messages.",
"deleteAfterLegacyDisappearingMessagesTheyChangedTimer": "<b>{name}</b> set the disappearing message timer to <b>{time}</b>", "deleteAfterLegacyDisappearingMessagesTheyChangedTimer": "<b>{name}</b> set the disappearing message timer to <b>{time}</b>",
"deleteAfterLegacyGroupsGroupCreation": "Please wait while the group is created...",
"deleteAfterLegacyGroupsGroupUpdateErrorTitle": "Failed to Update Group",
"deleteMessage": "Delete Message", "deleteMessage": "Delete Message",
"deleteMessageConfirm": "Are you sure you want to delete this message?", "deleteMessageConfirm": "Are you sure you want to delete this message?",
"deleteMessageDeleted": "Message deleted", "deleteMessageDeleted": "Message deleted",
@ -325,27 +332,27 @@
"downloading": "Downloading...", "downloading": "Downloading...",
"draft": "Draft", "draft": "Draft",
"edit": "Edit", "edit": "Edit",
"emojiAndSymbols": "Emoji & Symbols", "emojiAndSymbols": "Emoji and Symbols",
"emojiCategoryActivities": "Activities", "emojiCategoryActivities": "Activities",
"emojiCategoryAnimals": "Animals & Nature", "emojiCategoryAnimals": "Animals and Nature",
"emojiCategoryFlags": "Flags", "emojiCategoryFlags": "Flags",
"emojiCategoryFood": "Food & Drink", "emojiCategoryFood": "Food and Drink",
"emojiCategoryObjects": "Objects", "emojiCategoryObjects": "Objects",
"emojiCategoryRecentlyUsed": "Recently Used", "emojiCategoryRecentlyUsed": "Recently Used",
"emojiCategorySmileys": "Smileys & People", "emojiCategorySmileys": "Smileys and People",
"emojiCategorySymbols": "Symbols", "emojiCategorySymbols": "Symbols",
"emojiCategoryTravel": "Travel & Places", "emojiCategoryTravel": "Travel and Places",
"emojiReactsClearAll": "Are you sure you want to clear all {emoji}?", "emojiReactsClearAll": "Are you sure you want to clear all {emoji}?",
"emojiReactsCoolDown": "Slow down! You've sent too many emoji reacts. Try again soon", "emojiReactsCoolDown": "Slow down! You've sent too many emoji reacts. Try again soon",
"emojiReactsCountOthers": "{count, plural, one {And # other has reacted {emoji} to this message.} other {And # others have reacted {emoji} to this message.}}", "emojiReactsCountOthers": "{count, plural, one {And # other has reacted {emoji} to this message.} other {And # others have reacted {emoji} to this message.}}",
"emojiReactsHoverNameDesktop": "{name} reacted with <emoji/>", "emojiReactsHoverNameDesktop": "{name} reacted with <emoji/>",
"emojiReactsHoverTwoNameDesktop": "{name} & {other_name} reacted with <emoji/>", "emojiReactsHoverTwoNameDesktop": "{name} and {other_name} reacted with <emoji/>",
"emojiReactsHoverTwoNameMultipleDesktop": "{name}, {other_name} and <span>{count} others</span> reacted with <emoji/>", "emojiReactsHoverTwoNameMultipleDesktop": "{name}, {other_name} and <span>{count} others</span> reacted with <emoji/>",
"emojiReactsHoverTwoNameOneDesktop": "{name}, {other_name} and <span>1 other</span> reacted with <emoji/>", "emojiReactsHoverTwoNameOneDesktop": "{name}, {other_name} and <span>1 other</span> reacted with <emoji/>",
"emojiReactsHoverYouDesktop": "You reacted with <emoji/>", "emojiReactsHoverYouDesktop": "You reacted with <emoji/>",
"emojiReactsHoverYouNameDesktop": "You & {name} reacted with <emoji/>", "emojiReactsHoverYouNameDesktop": "You and {name} reacted with <emoji/>",
"emojiReactsHoverYouNameMultipleDesktop": "You, {name} & <span>{count} others</span> reacted with <emoji/>", "emojiReactsHoverYouNameMultipleDesktop": "You, {name} and <span>{count} others</span> reacted with <emoji/>",
"emojiReactsHoverYouNameOneDesktop": "You, {name} & <span>1 other</span> reacted with <emoji/>", "emojiReactsHoverYouNameOneDesktop": "You, {name} and <span>1 other</span> reacted with <emoji/>",
"emojiReactsNotification": "Reacted to your message {emoji}", "emojiReactsNotification": "Reacted to your message {emoji}",
"enable": "Enable", "enable": "Enable",
"errorConnection": "Please check your internet connection and try again.", "errorConnection": "Please check your internet connection and try again.",
@ -381,8 +388,10 @@
"groupInviteSending": "Sending invite", "groupInviteSending": "Sending invite",
"groupInviteSent": "Invite sent", "groupInviteSent": "Invite sent",
"groupInviteSuccessful": "Group invite successful", "groupInviteSuccessful": "Group invite successful",
"groupInviteVersion": "Users must have version {version} or higher to receive invitations", "groupInviteVersion": "Users must have the latest release to receive invitations",
"groupInviteYou": "<b>You</b> were invited to join the group.", "groupInviteYou": "<b>You</b> were invited to join the group.",
"groupInviteYouAndMoreNew": "<b>You</b> and <b>{count} others</b> were invited to join the group.",
"groupInviteYouAndOtherNew": "<b>You</b> and <b>{other_name}</b> were invited to join the group.",
"groupLeave": "Leave Group", "groupLeave": "Leave Group",
"groupLeaveDescription": "Are you sure you want to leave {group_name}?", "groupLeaveDescription": "Are you sure you want to leave {group_name}?",
"groupLeaveDescriptionAdmin": "Are you sure you want to leave {group_name}? This will deactivate the group for all members.", "groupLeaveDescriptionAdmin": "Are you sure you want to leave {group_name}? This will deactivate the group for all members.",
@ -392,7 +401,7 @@
"groupMemberLeftMore": "<b>{name}</b> and <b>{count} others</b> left the group.", "groupMemberLeftMore": "<b>{name}</b> and <b>{count} others</b> left the group.",
"groupMemberLeftTwo": "<b>{name}</b> and <b>{other_name}</b> left the group.", "groupMemberLeftTwo": "<b>{name}</b> and <b>{other_name}</b> left the group.",
"groupMemberMoreNew": "<b>{name}</b> and <b>{count} others</b> joined the group.", "groupMemberMoreNew": "<b>{name}</b> and <b>{count} others</b> joined the group.",
"groupMemberNew": "<b>{name}</b> joined the group.", "groupMemberNew": "<b>{name}</b> was invited to join the group.",
"groupMemberNewHistory": "<b>{name}</b> was invited to join the group. Chat history was shared.", "groupMemberNewHistory": "<b>{name}</b> was invited to join the group. Chat history was shared.",
"groupMemberNewHistoryMultiple": "<b>{name}</b> and <b>{count} others</b> were invited to join the group. Chat history was shared.", "groupMemberNewHistoryMultiple": "<b>{name}</b> and <b>{count} others</b> were invited to join the group. Chat history was shared.",
"groupMemberNewHistoryTwo": "<b>{name}</b> and <b>{other_name}</b> were invited to join the group. Chat history was shared.", "groupMemberNewHistoryTwo": "<b>{name}</b> and <b>{other_name}</b> were invited to join the group. Chat history was shared.",
@ -401,12 +410,11 @@
"groupMemberNewYouHistory": " <b>{name}</b> was invited to join the group. Chat history was shared.", "groupMemberNewYouHistory": " <b>{name}</b> was invited to join the group. Chat history was shared.",
"groupMemberNewYouHistoryMultiple": "<b>You</b> and <b>{count} others</b> were invited to join the group. Chat history was shared.", "groupMemberNewYouHistoryMultiple": "<b>You</b> and <b>{count} others</b> were invited to join the group. Chat history was shared.",
"groupMemberNewYouHistoryTwo": "<b>You</b> and <b>{name}</b> were invited to join the group. Chat history was shared.", "groupMemberNewYouHistoryTwo": "<b>You</b> and <b>{name}</b> were invited to join the group. Chat history was shared.",
"groupMemberNewYouMultiple": "<b>You</b> and <b>{count} others</b> were invited to join the group.", "groupMemberNewYouMultiple": "<b>You</b> and <b>{count} others</b> joined the group.",
"groupMemberNewYouTwo": "<b>You</b> and <b>{name}</b> were invited to join the group.", "groupMemberNewYouOther": "<b>You</b> and <b>{other_name}</b> joined the group.",
"groupMemberTwoNew": "<b>{name}</b> and <b>{other_name}</b> joined the group.", "groupMemberTwoNew": "<b>{name}</b> and <b>{other_name}</b> joined the group.",
"groupMemberYouAndMoreNew": "<b>You</b> and <b>{count} others</b> joined the group.",
"groupMemberYouAndOtherNew": "<b>You</b> and <b>{other_name}</b> joined the group.",
"groupMemberYouLeft": "<b>You</b> left the group.", "groupMemberYouLeft": "<b>You</b> left the group.",
"groupMemberYouNew": "<b>You</b> joined the group.",
"groupMembers": "Group Members", "groupMembers": "Group Members",
"groupMembersNone": "There are no other members in this group.", "groupMembersNone": "There are no other members in this group.",
"groupName": "Group Name", "groupName": "Group Name",
@ -582,8 +590,8 @@
"onboardingAccountCreate": "Create account", "onboardingAccountCreate": "Create account",
"onboardingAccountCreated": "Account Created", "onboardingAccountCreated": "Account Created",
"onboardingAccountExists": "I have an account", "onboardingAccountExists": "I have an account",
"onboardingBackAccountCreation": "You cannot go back further. In order to cancel your account creation, Session needs to quit.", "onboardingBackAccountCreation": "You cannot go back further. In order to cancel your account creation, {app_name} needs to quit.",
"onboardingBackLoadAccount": "You cannot go back further. In order to stop loading your account, Session needs to quit.", "onboardingBackLoadAccount": "You cannot go back further. In order to stop loading your account, {app_name} needs to quit.",
"onboardingBubbleCreatingAnAccountIsEasy": "Creating an account is instant, free, and anonymous ", "onboardingBubbleCreatingAnAccountIsEasy": "Creating an account is instant, free, and anonymous ",
"onboardingBubbleNoPhoneNumber": "You don't even need a phone number to sign up.", "onboardingBubbleNoPhoneNumber": "You don't even need a phone number to sign up.",
"onboardingBubblePrivacyInYourPocket": "Privacy in your pocket.", "onboardingBubblePrivacyInYourPocket": "Privacy in your pocket.",
@ -670,6 +678,7 @@
"recoveryPasswordBannerTitle": "Save your recovery password", "recoveryPasswordBannerTitle": "Save your recovery password",
"recoveryPasswordDescription": "Use your recovery password to load your account on new devices.<br/><br/>Your account cannot be recovered without your recovery password. Make sure it's stored somewhere safe and secure — and don't share it with anyone.", "recoveryPasswordDescription": "Use your recovery password to load your account on new devices.<br/><br/>Your account cannot be recovered without your recovery password. Make sure it's stored somewhere safe and secure — and don't share it with anyone.",
"recoveryPasswordEnter": "Enter your recovery password", "recoveryPasswordEnter": "Enter your recovery password",
"recoveryPasswordErrorLoad": "An error occurred when trying to load your recovery password.<br/><br/>Please export your logs, then upload the file though Session's Help Desk to help resolve this issue.",
"recoveryPasswordErrorMessageGeneric": "Please check your recovery password and try again.", "recoveryPasswordErrorMessageGeneric": "Please check your recovery password and try again.",
"recoveryPasswordErrorMessageIncorrect": "Some of the words in your Recovery Password are incorrect. Please check and try again.", "recoveryPasswordErrorMessageIncorrect": "Some of the words in your Recovery Password are incorrect. Please check and try again.",
"recoveryPasswordErrorMessageShort": "The Recovery Password you entered is not long enough. Please check and try again.", "recoveryPasswordErrorMessageShort": "The Recovery Password you entered is not long enough. Please check and try again.",
@ -729,7 +738,7 @@
"set": "Set", "set": "Set",
"settingsRestartDescription": "You must restart {app_name} to apply your new settings.", "settingsRestartDescription": "You must restart {app_name} to apply your new settings.",
"share": "Share", "share": "Share",
"shareAccountIdDescription": "Invite your friend to chat with you on Session by sharing your Account ID with them.", "shareAccountIdDescription": "Invite your friend to chat with you on {app_name} by sharing your Account ID with them.",
"shareAccountIdDescriptionCopied": "Share with your friends wherever you usually speak with them — then move the conversation here.", "shareAccountIdDescriptionCopied": "Share with your friends wherever you usually speak with them — then move the conversation here.",
"shareExtensionDatabaseError": "There is an issue opening the database. Please restart the app and try again.", "shareExtensionDatabaseError": "There is an issue opening the database. Please restart the app and try again.",
"shareToSession": "Share to {app_Name}", "shareToSession": "Share to {app_Name}",

@ -17,7 +17,6 @@ export const SessionHtmlRenderer = ({ tag = 'div', key, html, className }: Recei
return createElement(tag, { return createElement(tag, {
key, key,
className, className,
dangerouslySetInnerHTML: { __html: clean }, dangerouslySetInnerHTML: { __html: clean },
}); });
}; };

@ -398,7 +398,7 @@ export class SessionConversation extends Component<Props, State> {
}); });
if (blob.blob.size > MAX_ATTACHMENT_FILESIZE_BYTES) { if (blob.blob.size > MAX_ATTACHMENT_FILESIZE_BYTES) {
ToastUtils.pushFileSizeErrorAsByte(MAX_ATTACHMENT_FILESIZE_BYTES); ToastUtils.pushFileSizeErrorAsByte();
return; return;
} }
} catch (error) { } catch (error) {

@ -291,7 +291,7 @@ export class SessionRecording extends Component<Props, State> {
// Is the audio file > attachment filesize limit // Is the audio file > attachment filesize limit
if (this.audioBlobMp3.size > MAX_ATTACHMENT_FILESIZE_BYTES) { if (this.audioBlobMp3.size > MAX_ATTACHMENT_FILESIZE_BYTES) {
ToastUtils.pushFileSizeErrorAsByte(MAX_ATTACHMENT_FILESIZE_BYTES); ToastUtils.pushFileSizeErrorAsByte();
return; return;
} }

@ -50,8 +50,11 @@ export const AddModeratorsDialog = (props: Props) => {
ToastUtils.pushFailedToAddAsModerator(); ToastUtils.pushFailedToAddAsModerator();
} else { } else {
const userDisplayName =
getConversationController().get(pubkey.key)?.getNicknameOrRealUsernameOrPlaceholder() ||
window.i18n('unknown');
window?.log?.info(`${pubkey.key} added as moderator...`); window?.log?.info(`${pubkey.key} added as moderator...`);
ToastUtils.pushUserAddedToModerators(); ToastUtils.pushUserAddedToModerators(userDisplayName);
// clear input box // clear input box
setInputBoxValue(''); setInputBoxValue('');

@ -25,22 +25,27 @@ async function removeMods(convoId: string, modsToRemove: Array<string>) {
return false; return false;
} }
window?.log?.info(`asked to remove moderators: ${modsToRemove}`); window?.log?.info(`asked to remove moderators: ${modsToRemove}`);
const modsToRemovePubkey = compact(modsToRemove.map(m => PubKey.from(m)));
const modsToRemoveNames = modsToRemovePubkey.map(
m =>
getConversationController().get(m.key)?.getNicknameOrRealUsernameOrPlaceholder() ||
window.i18n('unknown')
);
try { try {
const convo = getConversationController().get(convoId); const convo = getConversationController().get(convoId);
const roomInfos = convo.toOpenGroupV2(); const roomInfos = convo.toOpenGroupV2();
const modsToRemovePubkey = compact(modsToRemove.map(m => PubKey.from(m)));
const res = await sogsV3RemoveAdmins(modsToRemovePubkey, roomInfos); const res = await sogsV3RemoveAdmins(modsToRemovePubkey, roomInfos);
if (!res) { if (!res) {
window?.log?.warn('failed to remove moderators:', res); window?.log?.warn('failed to remove moderators:', res);
ToastUtils.pushFailedToRemoveFromModerator(); ToastUtils.pushFailedToRemoveFromModerator(modsToRemoveNames);
return false; return false;
} }
window?.log?.info(`${modsToRemove} removed from moderators...`); window?.log?.info(`${modsToRemove} removed from moderators...`);
ToastUtils.pushUserRemovedFromModerators(); ToastUtils.pushUserRemovedFromModerators(modsToRemoveNames);
return true; return true;
} catch (e) { } catch (e) {
window?.log?.error('Got error while removing moderator:', e); window?.log?.error('Got error while removing moderator:', e);

@ -78,10 +78,10 @@ export async function removeSenderFromModerator(sender: string, convoId: string)
if (!res) { if (!res) {
window?.log?.warn('failed to remove moderator:', res); window?.log?.warn('failed to remove moderator:', res);
ToastUtils.pushFailedToRemoveFromModerator(userDisplayName); ToastUtils.pushFailedToRemoveFromModerator([userDisplayName]);
} else { } else {
window?.log?.info(`${pubKeyToRemove.key} removed from moderators...`); window?.log?.info(`${pubKeyToRemove.key} removed from moderators...`);
ToastUtils.pushUserRemovedFromModerators(userDisplayName); ToastUtils.pushUserRemovedFromModerators([userDisplayName]);
} }
} catch (e) { } catch (e) {
window?.log?.error('Got error while removing moderator:', e); window?.log?.error('Got error while removing moderator:', e);

@ -89,9 +89,9 @@ export function getJoinedGroupUpdateChangeStr(
case 0: case 0:
return getString('groupMemberNew', { name: window.i18n('you') }); return getString('groupMemberNew', { name: window.i18n('you') });
case 1: case 1:
return getString('groupMemberYouAndOtherNew', { other_name: othersNames[0] }); return getString('groupMemberNewYouOther', { other_name: othersNames[0] });
default: default:
return getString('groupMemberYouAndMoreNew', { count: othersNames.length }); return getString('groupMemberNewYouMultiple', { count: othersNames.length });
} }
} }
switch (others.length) { switch (others.length) {

@ -35,6 +35,12 @@ export function pushToastInfo(
); );
} }
/**
* We are rendering a toast. A toast is only rendering a string and no html at all.
* We have to strip the html tags from the strings we are given.
*/
const getStrippedI18n = window.i18n.stripped;
export function pushToastSuccess(id: string, title: string, description?: string) { export function pushToastSuccess(id: string, title: string, description?: string) {
toast.success( toast.success(
<SessionToast title={title} description={description} type={SessionToastType.Success} />, <SessionToast title={title} description={description} type={SessionToastType.Success} />,
@ -44,84 +50,72 @@ export function pushToastSuccess(id: string, title: string, description?: string
export function pushLoadAttachmentFailure(message?: string) { export function pushLoadAttachmentFailure(message?: string) {
if (message) { if (message) {
pushToastError('unableToLoadAttachment', `${window.i18n('attachmentsErrorLoad')} ${message}`);
} else {
pushToastError('unableToLoadAttachment', window.i18n('attachmentsErrorLoad'));
}
}
export function pushFileSizeError(limit: number, units: string) {
pushToastError( pushToastError(
'fileSizeWarning', 'unableToLoadAttachment',
window.i18n('attachmentsErrorSize'), `${getStrippedI18n('attachmentsErrorLoad')} ${message}`
`Max size: ${limit} ${units}`
); );
} else {
pushToastError('unableToLoadAttachment', getStrippedI18n('attachmentsErrorLoad'));
}
} }
export function pushFileSizeErrorAsByte(bytesCount: number) { export function pushFileSizeErrorAsByte() {
const units = ['kB', 'MB', 'GB']; pushToastError('fileSizeWarning', getStrippedI18n('attachmentsErrorSize'));
let u = -1;
let limit = bytesCount;
do {
limit /= 1000;
u += 1;
} while (limit >= 1000 && u < units.length - 1);
pushFileSizeError(limit, units[u]);
} }
export function pushMultipleNonImageError() { export function pushMultipleNonImageError() {
pushToastError('cannotMixImageAndNonImageAttachments', window.i18n('attachmentsErrorTypes')); pushToastError('attachmentsErrorTypes', getStrippedI18n('attachmentsErrorTypes'));
} }
export function pushCannotMixError() { export function pushCannotMixError() {
pushToastError('oneNonImageAtATimeToast', window.i18n('attachmentsErrorTypes')); pushToastError('attachmentsErrorTypes', getStrippedI18n('attachmentsErrorTypes'));
} }
export function pushMaximumAttachmentsError() { export function pushMaximumAttachmentsError() {
pushToastError('maximumAttachments', window.i18n('attachmentsErrorNumber')); pushToastError('attachmentsErrorNumber', getStrippedI18n('attachmentsErrorNumber'));
} }
export function pushCopiedToClipBoard() { export function pushCopiedToClipBoard() {
pushToastInfo('copiedToClipboard', window.i18n('copied')); pushToastInfo('copiedToClipboard', getStrippedI18n('copied'));
} }
export function pushRestartNeeded() { export function pushRestartNeeded() {
pushToastInfo('restartNeeded', window.i18n('settingsRestartDescription')); pushToastInfo('restartNeeded', getStrippedI18n('settingsRestartDescription'));
} }
export function pushAlreadyMemberOpenGroup() { export function pushAlreadyMemberOpenGroup() {
pushToastInfo('publicChatExists', window.i18n('communityJoinedAlready')); pushToastInfo('publicChatExists', getStrippedI18n('communityJoinedAlready'));
} }
export function pushUserBanSuccess() { export function pushUserBanSuccess() {
pushToastSuccess('userBanned', window.i18n('banUserBanned')); pushToastSuccess('userBanned', getStrippedI18n('banUserBanned'));
} }
export function pushUserBanFailure() { export function pushUserBanFailure() {
pushToastError('userBanFailed', window.i18n('banErrorFailed')); pushToastError('userBanFailed', getStrippedI18n('banErrorFailed'));
} }
export function pushUserUnbanSuccess() { export function pushUserUnbanSuccess() {
pushToastSuccess('userUnbanned', window.i18n('banUnbanUserUnbanned')); pushToastSuccess('userUnbanned', getStrippedI18n('banUnbanUserUnbanned'));
} }
export function pushUserUnbanFailure() { export function pushUserUnbanFailure() {
pushToastError('userUnbanFailed', window.i18n('banUnbanErrorFailed')); pushToastError('userUnbanFailed', getStrippedI18n('banUnbanErrorFailed'));
} }
export function pushMessageDeleteForbidden() { export function pushMessageDeleteForbidden() {
pushToastError( pushToastError(
'messageDeletionForbidden', 'messageDeletionForbidden',
window.i18n('deleteafterMessageDeletionStandardisationmessageDeletionForbidden') getStrippedI18n('deleteafterMessageDeletionStandardisationmessageDeletionForbidden')
); );
} }
export function pushUnableToCall() { export function pushUnableToCall() {
pushToastError('unableToCall', window.i18n('callsCannotStart'), window.i18n('callsCannotStart')); pushToastError('unableToCall', getStrippedI18n('callsCannotStart'));
} }
export function pushedMissedCall(conversationName: string) { export function pushedMissedCall(userName: string) {
pushToastInfo('missedCall', window.i18n('callsMissedCallFrom', { name: conversationName })); pushToastInfo('missedCall', getStrippedI18n('callsMissedCallFrom', { name: userName }));
} }
const openPermissionsSettings = () => { const openPermissionsSettings = () => {
@ -133,8 +127,8 @@ export function pushedMissedCallCauseOfPermission(conversationName: string) {
const id = 'missedCallPermission'; const id = 'missedCallPermission';
toast.info( toast.info(
<SessionToast <SessionToast
title={window.i18n('callsMissedCallFrom', { name: conversationName })} title={getStrippedI18n('callsMissedCallFrom', { name: conversationName })}
description={window.i18n('callsYouMissedCallPermissions', { name: conversationName })} description={getStrippedI18n('callsYouMissedCallPermissions', { name: conversationName })}
type={SessionToastType.Info} type={SessionToastType.Info}
onToastClick={openPermissionsSettings} onToastClick={openPermissionsSettings}
/>, />,
@ -145,8 +139,8 @@ export function pushedMissedCallCauseOfPermission(conversationName: string) {
export function pushVideoCallPermissionNeeded() { export function pushVideoCallPermissionNeeded() {
pushToastInfo( pushToastInfo(
'videoCallPermissionNeeded', 'videoCallPermissionNeeded',
window.i18n('callsPermissionsRequired'), getStrippedI18n('callsPermissionsRequired'),
window.i18n('callsPermissionsRequiredDescription'), getStrippedI18n('callsPermissionsRequiredDescription'),
openPermissionsSettings openPermissionsSettings
); );
} }
@ -154,83 +148,123 @@ export function pushVideoCallPermissionNeeded() {
export function pushAudioPermissionNeeded() { export function pushAudioPermissionNeeded() {
pushToastInfo( pushToastInfo(
'audioPermissionNeeded', 'audioPermissionNeeded',
window.i18n('permissionsMicrophoneAccessRequiredDesktop'), getStrippedI18n('permissionsMicrophoneAccessRequiredDesktop'),
undefined, undefined,
openPermissionsSettings openPermissionsSettings
); );
} }
export function pushOriginalNotFound() { export function pushOriginalNotFound() {
pushToastError('originalMessageNotFound', window.i18n('messageErrorOriginal')); pushToastError('messageErrorOriginal', getStrippedI18n('messageErrorOriginal'));
} }
export function pushTooManyMembers() { export function pushTooManyMembers() {
pushToastError('tooManyMembers', window.i18n('groupAddMemberMaximum')); pushToastError('groupAddMemberMaximum', getStrippedI18n('groupAddMemberMaximum'));
} }
export function pushMessageRequestPending() { export function pushMessageRequestPending() {
pushToastInfo('messageRequestPending', window.i18n('messageRequestPending')); pushToastInfo('messageRequestPending', getStrippedI18n('messageRequestPending'));
} }
export function pushUnblockToSend() { export function pushUnblockToSend() {
pushToastInfo('unblockToSend', window.i18n('blockBlockedDescription')); pushToastInfo('unblockToSend', getStrippedI18n('blockBlockedDescription'));
} }
export function pushYouLeftTheGroup() { export function pushYouLeftTheGroup() {
pushToastError('youLeftTheGroup', window.i18n('groupMemberYouLeft')); pushToastError('youLeftTheGroup', getStrippedI18n('groupMemberYouLeft'));
} }
export function someDeletionsFailed() { export function someDeletionsFailed(count: number) {
pushToastWarning('deletionError', 'Deletion error'); pushToastWarning('deletionError', getStrippedI18n('deleteMessagesFailed', { count }));
} }
export function pushDeleted() { export function pushDeleted() {
pushToastSuccess('deleted', window.i18n('deleteMessagesDeleted'), undefined); pushToastSuccess('deleted', getStrippedI18n('deleteMessagesDeleted'), undefined);
} }
export function pushCannotRemoveCreatorFromGroup() { export function pushCannotRemoveCreatorFromGroup() {
pushToastWarning('adminCannotBeRemoved', window.i18n('adminCannotBeRemoved')); pushToastWarning('adminCannotBeRemoved', getStrippedI18n('adminCannotBeRemoved'));
} }
export function pushFailedToAddAsModerator() { export function pushFailedToAddAsModerator() {
pushToastWarning('adminPromotionFailed', window.i18n('adminPromotionFailed')); pushToastWarning('adminPromotionFailed', getStrippedI18n('adminPromotionFailed'));
} }
export function pushFailedToRemoveFromModerator(name: string) { export function pushFailedToRemoveFromModerator(names: Array<string>) {
pushToastWarning( let localizedString: string = '';
'adminRemoveFailed', switch (names.length) {
window.i18n('adminRemoveFailed', { case 0:
name, throw new Error('pushFailedToRemoveFromModerator invalid case error');
}) case 1:
); localizedString = getStrippedI18n('adminRemoveFailed', {
name: names[0],
});
break;
case 2:
localizedString = getStrippedI18n('adminRemoveFailedOther', {
name: names[0],
other_name: names[1],
});
break;
default:
localizedString = getStrippedI18n('adminRemoveFailedMultiple', {
name: names[0],
count: names.length - 1,
});
break;
}
pushToastWarning('adminRemoveFailed', localizedString);
} }
export function pushUserAddedToModerators(name: string) { export function pushUserAddedToModerators(name: string) {
pushToastSuccess('adminPromotedToAdmin', window.i18n('adminPromotedToAdmin', { name })); pushToastSuccess('adminPromotedToAdmin', getStrippedI18n('adminPromotedToAdmin', { name }));
} }
export function pushUserRemovedFromModerators(names: Array<string>) {
let localizedString: string = '';
switch (names.length) {
case 0:
throw new Error('pushUserRemovedFromModerators invalid case error');
case 1:
localizedString = getStrippedI18n('adminRemovedUser', {
name: names[0],
});
break;
case 2:
localizedString = getStrippedI18n('adminRemovedUserOther', {
name: names[0],
other_name: names[1],
});
break;
default:
localizedString = getStrippedI18n('adminRemovedUserMultiple', {
name: names[0],
count: names.length - 1,
});
break;
}
export function pushUserRemovedFromModerators(name: string) { pushToastSuccess('adminRemovedUser', localizedString);
pushToastSuccess('adminRemovedUser', window.i18n('adminRemovedUser', { name }));
} }
export function pushInvalidPubKey() { export function pushInvalidPubKey() {
pushToastSuccess('invalidPubKey', window.i18n('accountIdErrorInvalid')); pushToastSuccess('accountIdErrorInvalid', getStrippedI18n('accountIdErrorInvalid'));
} }
export function pushNoCameraFound() { export function pushNoCameraFound() {
pushToastWarning('noCameraFound', window.i18n('cameraErrorNotFound')); pushToastWarning('noCameraFound', getStrippedI18n('cameraErrorNotFound'));
} }
export function pushNoAudioInputFound() { export function pushNoAudioInputFound() {
pushToastWarning('noAudioInputFound', window.i18n('audioNoInput')); pushToastWarning('noAudioInputFound', getStrippedI18n('audioNoInput'));
} }
export function pushNoAudioOutputFound() { export function pushNoAudioOutputFound() {
pushToastWarning('noAudioOutputFound', window.i18n('audioNoOutput')); pushToastWarning('noAudioOutputFound', getStrippedI18n('audioNoOutput'));
} }
export function pushNoMediaUntilApproved() { export function pushNoMediaUntilApproved() {
pushToastError('noMediaUntilApproved', window.i18n('messageRequestPendingDescription')); pushToastError('noMediaUntilApproved', getStrippedI18n('messageRequestPendingDescription'));
} }
export function pushRateLimitHitReactions() { export function pushRateLimitHitReactions() {

@ -26,7 +26,6 @@ import { hasValidIncomingRequestValues } from '../../models/conversation';
import { isOpenOrClosedGroup } from '../../models/conversationAttributes'; import { isOpenOrClosedGroup } from '../../models/conversationAttributes';
import { getConversationController } from '../../session/conversations'; import { getConversationController } from '../../session/conversations';
import { UserUtils } from '../../session/utils'; import { UserUtils } from '../../session/utils';
import { LocalizerType } from '../../types/Util';
import { BlockedNumberController } from '../../util'; import { BlockedNumberController } from '../../util';
import { Storage } from '../../util/storage'; import { Storage } from '../../util/storage';
import { getIntl } from './user'; import { getIntl } from './user';
@ -220,23 +219,20 @@ export const getSortedMessagesTypesOfSelectedConversation = createSelector(
} }
); );
function getConversationTitle( function getConversationTitle(conversation: ReduxConversationType): string {
conversation: ReduxConversationType,
testingi18n?: LocalizerType
): string {
if (conversation.displayNameInProfile) { if (conversation.displayNameInProfile) {
return conversation.displayNameInProfile; return conversation.displayNameInProfile;
} }
if (isOpenOrClosedGroup(conversation.type)) { if (isOpenOrClosedGroup(conversation.type)) {
return (testingi18n || window.i18n)('unknown'); return window.i18n('unknown');
} }
return conversation.id; return conversation.id;
} }
const collator = new Intl.Collator(); const collator = new Intl.Collator();
export const _getConversationComparator = (testingi18n?: LocalizerType) => { export const _getConversationComparator = () => {
return (left: ReduxConversationType, right: ReduxConversationType): number => { return (left: ReduxConversationType, right: ReduxConversationType): number => {
// Pin is the first criteria to check // Pin is the first criteria to check
const leftPriority = left.priority || 0; const leftPriority = left.priority || 0;
@ -259,8 +255,8 @@ export const _getConversationComparator = (testingi18n?: LocalizerType) => {
if (leftActiveAt && rightActiveAt && leftActiveAt !== rightActiveAt) { if (leftActiveAt && rightActiveAt && leftActiveAt !== rightActiveAt) {
return rightActiveAt - leftActiveAt; return rightActiveAt - leftActiveAt;
} }
const leftTitle = getConversationTitle(left, testingi18n).toLowerCase(); const leftTitle = getConversationTitle(left).toLowerCase();
const rightTitle = getConversationTitle(right, testingi18n).toLowerCase(); const rightTitle = getConversationTitle(right).toLowerCase();
return collator.compare(leftTitle, rightTitle); return collator.compare(leftTitle, rightTitle);
}; };

@ -1,22 +1,22 @@
import { assert } from 'chai'; import { assert } from 'chai';
import Sinon from 'sinon';
import { CONVERSATION_PRIORITIES, ConversationTypeEnum } from '../../../../models/types';
import { ConversationLookupType } from '../../../../state/ducks/conversations'; import { ConversationLookupType } from '../../../../state/ducks/conversations';
import { import {
_getConversationComparator, _getConversationComparator,
_getSortedConversations, _getSortedConversations,
} from '../../../../state/selectors/conversations'; } from '../../../../state/selectors/conversations';
import { ConversationTypeEnum, CONVERSATION_PRIORITIES } from '../../../../models/types'; import { TestUtils } from '../../../test-utils';
import type {
GetMessageArgs,
LocalizerDictionary,
LocalizerToken,
} from '../../../../types/Localizer';
const i18n = <T extends LocalizerToken, R extends LocalizerDictionary[T]>(
...[token]: GetMessageArgs<T>
) => token as any as R;
describe('state/selectors/conversations', () => { describe('state/selectors/conversations', () => {
beforeEach(() => {
TestUtils.stubWindowLog();
TestUtils.stubI18n();
});
afterEach(() => {
Sinon.restore();
});
describe('#getSortedConversationsList', () => { describe('#getSortedConversationsList', () => {
it('sorts conversations based on timestamp then by intl-friendly title', () => { it('sorts conversations based on timestamp then by intl-friendly title', () => {
const data: ConversationLookupType = { const data: ConversationLookupType = {
@ -138,7 +138,7 @@ describe('state/selectors/conversations', () => {
priority: CONVERSATION_PRIORITIES.default, priority: CONVERSATION_PRIORITIES.default,
}, },
}; };
const comparator = _getConversationComparator(i18n); const comparator = _getConversationComparator();
const conversations = _getSortedConversations(data, comparator); const conversations = _getSortedConversations(data, comparator);
assert.strictEqual(conversations[0].displayNameInProfile, 'First!'); assert.strictEqual(conversations[0].displayNameInProfile, 'First!');
@ -277,7 +277,7 @@ describe('state/selectors/conversations', () => {
isPublic: false, isPublic: false,
}, },
}; };
const comparator = _getConversationComparator(i18n); const comparator = _getConversationComparator();
const conversations = _getSortedConversations(data, comparator); const conversations = _getSortedConversations(data, comparator);
assert.strictEqual(conversations[0].displayNameInProfile, 'Á'); assert.strictEqual(conversations[0].displayNameInProfile, 'Á');

@ -13,6 +13,9 @@ import {
} from 'date-fns'; } from 'date-fns';
import timeLocales from 'date-fns/locale'; import timeLocales from 'date-fns/locale';
import { isUndefined } from 'lodash'; import { isUndefined } from 'lodash';
import { GetNetworkTime } from '../session/apis/snode_api/getNetworkTime';
import { DURATION_SECONDS, LOCALE_DEFAULTS } from '../session/constants';
import { updateLocale } from '../state/ducks/dictionary';
import { import {
DictionaryWithoutPluralStrings, DictionaryWithoutPluralStrings,
GetMessageArgs, GetMessageArgs,
@ -21,10 +24,6 @@ import {
PluralKey, PluralKey,
PluralString, PluralString,
} from '../types/Localizer'; } from '../types/Localizer';
import { DURATION_SECONDS, LOCALE_DEFAULTS } from '../session/constants';
import { updateLocale } from '../state/ducks/dictionary';
import { GetNetworkTime } from '../session/apis/snode_api/getNetworkTime';
import { Dictionary } from '../localization/locales';
export function loadDictionary(locale: Locale) { export function loadDictionary(locale: Locale) {
return import(`../../_locales/${locale}/messages.json`) as Promise<LocalizerDictionary>; return import(`../../_locales/${locale}/messages.json`) as Promise<LocalizerDictionary>;
@ -232,7 +231,7 @@ export const setupi18n = (locale: Locale, dictionary: LocalizerDictionary) => {
return token as R; return token as R;
} }
localizedString = pluralString.replaceAll('#', num) as R; localizedString = pluralString.replaceAll('#', `${num}`) as R;
} }
} }

Loading…
Cancel
Save