fix: group updates localization shares logic with component

pull/3206/head
Audric Ackermann 8 months ago
parent cd3dccea02
commit 0c015b4ea7

@ -158,6 +158,7 @@
"cameraGrantAccessDescription": "{app_name} needs camera access to take photos and videos, or scan QR codes.",
"cameraGrantAccessQr": "{app_name} needs camera access to scan QR codes",
"cancel": "Cancel",
"changePasswordFail": "Failed to change password",
"clear": "Clear",
"clearAll": "Clear All",
"clearDataAll": "Clear All Data",
@ -188,7 +189,7 @@
"communityInvitation": "Community Invitation",
"communityJoin": "Join Community",
"communityJoinDescription": "Are you sure you want to join {community_name}?",
"communityJoinError": "Failed to join {community_name}",
"communityJoinError": "Failed to join community",
"communityJoinOfficial": "Or join one of these...",
"communityJoined": "Joined Community",
"communityJoinedAlready": "You are already a member of this community.",
@ -277,6 +278,7 @@
"deleteMessagesDescriptionDevice": "Are you sure you want to delete these messages from this device only?",
"deleteMessagesDescriptionEveryone": "Are you sure you want to delete these messages for everyone?",
"deleteMessagesFailed": "{count, plural, one {Failed to delete message} other {Failed to delete messages}}",
"deleteafterMessageDeletionStandardisationmessageDeletionForbidden": "You dont have permission to delete others messages",
"deleting": "Deleting",
"developerToolsToggle": "Toggle Developer Tools",
"dictationStart": "Start Dictation...",
@ -300,8 +302,8 @@
"disappearingMessagesOnlyAdmins": "Only group admins can change this setting.",
"disappearingMessagesSent": "Sent",
"disappearingMessagesSet": "<b>{name}</b> has set messages to disappear {time} after they have been {disappearing_messages_type}.",
"disappearingMessagesTimer": "Timer",
"disappearingMessagesSetYou": "<b>You</b> set messages to disappear {time} after they have been {disappearing_messages_type}.",
"disappearingMessagesTimer": "Timer",
"disappearingMessagesTurnedOff": "<b>{name}</b> has turned disappearing messages off. Messages they send will no longer disappear.",
"disappearingMessagesTurnedOffYou": "<b>You</b> turned <b>off</b> disappearing messages. Messages you send will no longer disappear.",
"disappearingMessagesTypeRead": "read",
@ -391,7 +393,19 @@
"groupMemberLeftTwo": "<b>{name}</b> and <b>{other_name}</b> left the group.",
"groupMemberMoreNew": "<b>{name}</b> and <b>{count} others</b> joined the group.",
"groupMemberNew": "<b>{name}</b> joined the group.",
"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.",
"groupMemberNewHistoryTwo": "<b>{name}</b> and <b>{other_name}</b> were invited to join the group. Chat history was shared.",
"groupMemberNewMultiple": "<b>{name}</b> and <b>{count} others</b> were invited to join the group.",
"groupMemberNewTwo": "<b>{name}</b> and <b>{other_name}</b> were invited to join the group.",
"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.",
"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.",
"groupMemberNewYouTwo": "<b>You</b> and <b>{name}</b> were invited to join 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.",
"groupMembers": "Group Members",
"groupMembersNone": "There are no other members in this group.",
@ -404,6 +418,8 @@
"groupNoMessages": "You have no messages from <b>{group_name}</b>. Send a message to start the conversation!",
"groupOnlyAdmin": "You are the only admin in <b>{group_name}</b>.<br/><br/>Group members and settings cannot be changed without an admin.",
"groupPromotedYou": "<b>You</b> were promoted to Admin.",
"groupPromotedYouMultiple": "<b>You</b> and <b>{count} others</b> were promoted to Admin.",
"groupPromotedYouTwo": "<b>You</b> and <b>{name}</b> were promoted to Admin.",
"groupRemoveDescription": "Would you like to remove <b>{name}</b> from <b>{group_name}</b>?",
"groupRemoveMessages": "Remove user and their messages",
"groupRemoveMoreDescription": "Would you like to remove <b>{name}</b> and <b>{count} others</b> from <b>{group_name}</b>?",
@ -415,6 +431,8 @@
"groupRemovedMore": "<b>{name}</b> and <b>{count} others</b> were removed from the group.",
"groupRemovedTwo": "<b>{name}</b> and <b>{other_name}</b> were removed from the group.",
"groupRemovedYou": "You were removed from <b>{group_name}</b>.",
"groupRemovedYouMultiple": "<b>You</b> and <b>{count} others</b> were removed from the group.",
"groupRemovedYouTwo": "<b>You</b> and <b>{other_name}</b> were removed from the group.",
"groupSetDisplayPicture": "Set Group Display Picture",
"groupUnknown": "Unknown Group",
"groupUpdated": "Group updated",
@ -560,6 +578,7 @@
"notificationsVibrate": "Vibrate",
"off": "Off",
"okay": "Okay",
"on": "On",
"onboardingAccountCreate": "Create account",
"onboardingAccountCreated": "Account Created",
"onboardingAccountExists": "I have an account",
@ -666,6 +685,7 @@
"recoveryPasswordWarningSendDescription": "This is your recovery password. If you send it to someone they'll have full access to your account.",
"redo": "Redo",
"remove": "Remove",
"removePasswordFail": "Failed to remove password",
"reply": "Reply",
"resend": "Resend",
"resolving": "Loading country information...",

@ -1,5 +1,7 @@
import { useConversationsUsernameWithQuoteOrFullPubkey } from '../../../../hooks/useParamSelector';
import { arrayContainsUsOnly } from '../../../../models/message';
import {
getKickedGroupUpdateStr,
getLeftGroupUpdateChangeStr,
} from '../../../../models/groupUpdate';
import {
PropsForGroupUpdate,
PropsForGroupUpdateType,
@ -12,51 +14,38 @@ import { NotificationBubble } from './notification-bubble/NotificationBubble';
// This component is used to display group updates in the conversation view.
const ChangeItemJoined = (added: Array<string>): string => {
const groupName = useSelectedNicknameOrProfileNameOrShortenedPubkey();
if (!added.length) {
throw new Error('Group update add is missing contacts');
throw new Error('Group update added is missing details');
}
const names = useConversationsUsernameWithQuoteOrFullPubkey(added);
return window.i18n('groupMemberNew', {
name: names.join(', '),
});
// this is not ideal, but also might not be changed as part of Strings but,
// we return a string containing style tags (<b> etc) here, and a SessionHtmlRenderer is going
// to render them correctly.
return getLeftGroupUpdateChangeStr(added, groupName, false);
};
const ChangeItemKicked = (kicked: Array<string>): string => {
if (!kicked.length) {
throw new Error('Group update kicked is missing contacts');
throw new Error('Group update kicked is missing details');
}
const names = useConversationsUsernameWithQuoteOrFullPubkey(kicked);
const groupName = useSelectedNicknameOrProfileNameOrShortenedPubkey();
if (arrayContainsUsOnly(kicked)) {
return window.i18n('groupRemovedYou', { group_name: groupName });
}
// TODO - support bold
return kicked.length === 1
? window.i18n('groupRemoved', { name: names[0] })
: kicked.length === 2
? window.i18n('groupRemovedTwo', { name: names[0], other_name: names[1] })
: window.i18n('groupRemovedMore', { name: names[0], count: names.length });
// this is not ideal, but also might not be changed as part of Strings but,
// we return a string containing style tags (<b> etc) here, and a SessionHtmlRenderer is going
// to render them correctly.
return getKickedGroupUpdateStr(kicked, groupName, false);
};
const ChangeItemLeft = (left: Array<string>): string => {
if (!left.length) {
throw new Error('Group update remove is missing contacts');
}
const names = useConversationsUsernameWithQuoteOrFullPubkey(left);
const groupName = useSelectedNicknameOrProfileNameOrShortenedPubkey();
if (arrayContainsUsOnly(left)) {
return window.i18n('groupMemberYouLeft');
if (!left.length) {
throw new Error('Group update left is missing details');
}
// TODO - support bold
return left.length === 1
? window.i18n('groupMemberLeft', { name: names[0] })
: left.length === 2
? window.i18n('groupMemberLeftTwo', { name: names[0], other_name: names[1] })
: window.i18n('groupMemberLeftMore', { name: names[0], count: names.length });
// this is not ideal, but also might not be changed as part of Strings but,
// we return a string containing style tags (<b> etc) here, and a SessionHtmlRenderer is going
// to render them correctly.
return getLeftGroupUpdateChangeStr(left, groupName, false);
};
const ChangeItem = (change: PropsForGroupUpdateType): string => {

@ -2052,10 +2052,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
const roomInfo = OpenGroupData.getV2OpenGroupRoom(groupUrl);
if (!roomInfo || !roomInfo.serverPublicKey) {
ToastUtils.pushToastError(
'no-sogs-matching',
window.i18n('communityJoinError', { community_name: groupUrl })
);
ToastUtils.pushToastError('no-sogs-matching', window.i18n('communityJoinError'));
window?.log?.error('Could not find room with matching server url', groupUrl);
throw new Error(`Could not find room with matching server url: ${groupUrl}`);
}

@ -0,0 +1,104 @@
import { getConversationController } from '../session/conversations';
import { UserUtils } from '../session/utils';
// to remove after merge with groups
function usAndXOthers(arr: Array<string>) {
const us = UserUtils.getOurPubKeyStrFromCache();
if (arr.includes(us)) {
return { us: true, others: arr.filter(m => m !== us) };
}
return { us: false, others: arr };
}
export function getKickedGroupUpdateStr(
kicked: Array<string>,
groupName: string,
stripTags: boolean
) {
const { others, us } = usAndXOthers(kicked);
const othersNames = others.map(
getConversationController().getContactProfileNameOrShortenedPubKey
);
if (us) {
switch (others.length) {
case 0:
return window.i18n('groupRemovedYou', { group_name: groupName });
case 1:
return window.i18n('groupRemovedYouTwo', { other_name: othersNames[0] });
default:
return window.i18n('groupRemovedYouMultiple', { count: othersNames.length });
}
}
switch (others.length) {
case 0:
throw new Error('kicked without anyone in it.');
case 1:
return window.i18n('groupRemoved', { name: othersNames[0] });
case 2:
return window.i18n('groupRemovedTwo', {
name: othersNames[0],
other_name: othersNames[1],
});
default:
return window.i18n('groupRemovedMore', {
name: others[0],
count: othersNames.length - 1,
});
}
}
export function getLeftGroupUpdateChangeStr(
left: Array<string>,
_groupName: string,
stripTags: boolean
) {
const { others, us } = usAndXOthers(left);
if (left.length !== 1) {
throw new Error('left.length should never be more than 1');
}
return us
? window.i18n('groupMemberYouLeft')
: window.i18n('groupMemberLeft', {
name: getConversationController().getContactProfileNameOrShortenedPubKey(others[0]),
});
}
export function getJoinedGroupUpdateChangeStr(
joined: Array<string>,
_groupName: string,
stripTags: boolean
) {
const { others, us } = usAndXOthers(joined);
const othersNames = others.map(
getConversationController().getContactProfileNameOrShortenedPubKey
);
if (us) {
switch (others.length) {
case 0:
return window.i18n('groupMemberNew', { name: window.i18n('you') });
case 1:
return window.i18n('groupMemberYouAndOtherNew', { other_name: othersNames[0] });
default:
return window.i18n('groupMemberYouAndMoreNew', { count: othersNames.length });
}
}
switch (others.length) {
case 0:
throw new Error('joined without anyone in it.');
case 1:
return window.i18n('groupMemberNew', { name: othersNames[0] });
case 2:
return window.i18n('groupMemberTwoNew', {
name: othersNames[0],
other_name: othersNames[1],
});
default:
return window.i18n('groupMemberMoreNew', {
name: others[0],
count: othersNames.length - 1,
});
}
}

@ -2,16 +2,7 @@ import Backbone from 'backbone';
import autoBind from 'auto-bind';
import filesize from 'filesize';
import {
cloneDeep,
debounce,
isEmpty,
size as lodashSize,
map,
partition,
pick,
uniq,
} from 'lodash';
import { cloneDeep, debounce, isEmpty, size as lodashSize, partition, pick, uniq } from 'lodash';
import { SignalService } from '../protobuf';
import { getMessageQueue } from '../session';
import { getConversationController } from '../session/conversations';
@ -95,25 +86,14 @@ import { ConversationModel } from './conversation';
import { READ_MESSAGE_STATE } from './conversationAttributes';
import { ConversationInteractionStatus, ConversationInteractionType } from '../interactions/types';
import { LastMessageStatusType } from '../state/ducks/types';
import {
getJoinedGroupUpdateChangeStr,
getKickedGroupUpdateStr,
getLeftGroupUpdateChangeStr,
} from './groupUpdate';
// tslint:disable: cyclomatic-complexity
/**
* @returns true if the array contains only a single item being 'You', 'you' or our device pubkey
*/
export function arrayContainsUsOnly(arrayToCheck: Array<string> | undefined) {
return (
arrayToCheck &&
arrayToCheck.length === 1 &&
(arrayToCheck[0] === UserUtils.getOurPubKeyStrFromCache() ||
arrayToCheck[0].toLowerCase() === 'you')
);
}
export function arrayContainsOneItemOnly(arrayToCheck: Array<string> | undefined) {
return arrayToCheck && arrayToCheck.length === 1;
}
export class MessageModel extends Backbone.Model<MessageAttributes> {
constructor(attributes: MessageAttributesOptionals & { skipTimerInit?: boolean }) {
const filledAttrs = fillMessageAttributesWithDefaults(attributes);
@ -1289,26 +1269,9 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
if (groupUpdate) {
const groupName =
this.getConversation()?.getNicknameOrRealUsernameOrPlaceholder() || window.i18n('unknown');
if (arrayContainsUsOnly(groupUpdate.kicked)) {
return window.i18n('groupRemovedYou', { group_name: groupName });
}
if (arrayContainsUsOnly(groupUpdate.left)) {
return window.i18n('groupMemberYouLeft');
}
if (groupUpdate.left && groupUpdate.left.length === 1) {
return window.i18n('groupMemberLeft', {
name: getConversationController().getContactProfileNameOrShortenedPubKey(
groupUpdate.left[0]
),
});
}
const messages = [];
if (!groupUpdate.name && !groupUpdate.joined && !groupUpdate.kicked && !groupUpdate.kicked) {
return window.i18n('groupUpdated');
if (groupUpdate.left) {
return getLeftGroupUpdateChangeStr(groupUpdate.left, groupName, true);
}
if (groupUpdate.name) {
@ -1316,28 +1279,15 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
}
if (groupUpdate.joined && groupUpdate.joined.length) {
const names = groupUpdate.joined.map(
getConversationController().getContactProfileNameOrShortenedPubKey
);
messages.push(window.i18n('groupMemberNew', { name: names.join(', ') }));
return messages.join(' ');
return getJoinedGroupUpdateChangeStr(groupUpdate.joined, groupName, true);
}
if (groupUpdate.kicked && groupUpdate.kicked.length) {
const names = map(
groupUpdate.kicked,
getConversationController().getContactProfileNameOrShortenedPubKey
);
if (names.length > 1) {
messages.push(window.i18n('multipleKickedFromTheGroup', { name: names.join(', ') }));
} else {
messages.push(window.i18n('groupRemoved', { name: names[0] }));
}
if (groupUpdate.kicked?.length) {
return getKickedGroupUpdateStr(groupUpdate.kicked, groupName, true);
}
return messages.join(' ');
window.log.warn('did not build a specific change for getDescription of ', groupUpdate);
return window.i18n('groupUpdated');
}
if (this.isGroupInvitation()) {

@ -96,9 +96,7 @@ async function joinOpenGroupV2(
if (!conversation) {
window?.log?.warn('Failed to join open group v2');
// TODO - Check that this is the room name
throw new Error(
window.i18n('communityJoinError', { community_name: roomId || window.i18n('unknown') })
);
throw new Error(window.i18n('communityJoinError'));
}
// here we managed to connect to the group.
@ -183,19 +181,10 @@ export async function joinOpenGroupV2WithUIEvents(
}
if (showToasts) {
// TODO - Check that this is the room name
ToastUtils.pushToastError(
'communityJoinError',
window.i18n('communityJoinError', {
community_name: parsedRoom.roomId || window.i18n('unknown'),
})
);
ToastUtils.pushToastError('communityJoinError', window.i18n('communityJoinError'));
}
if (errorHandler) {
errorHandler(
window.i18n('communityJoinError', {
community_name: parsedRoom.roomId || window.i18n('unknown'),
})
);
errorHandler(window.i18n('communityJoinError'));
}
uiCallback?.({ loadingState: 'failed', conversationKey: conversationID });
@ -203,15 +192,10 @@ export async function joinOpenGroupV2WithUIEvents(
window?.log?.warn('got error while joining open group:', error.message);
if (showToasts) {
// TODO - Check that this is the room name
ToastUtils.pushToastError(
'communityJoinError',
window.i18n('communityJoinError', { community_name: window.i18n('unknown') })
);
ToastUtils.pushToastError('communityJoinError', window.i18n('communityJoinError'));
}
if (errorHandler) {
errorHandler(
window.i18n('communityJoinError', { community_name: window.i18n('unknown') })
); // we don't have a parsed room, so let's show the whole url in this case
errorHandler(window.i18n('communityJoinError'));
}
uiCallback?.({ loadingState: 'failed', conversationKey: null });
}

@ -110,7 +110,10 @@ export function pushUserUnbanFailure() {
}
export function pushMessageDeleteForbidden() {
pushToastError('messageDeletionForbidden', window.i18n('messageDeletionForbidden'));
pushToastError(
'messageDeletionForbidden',
window.i18n('deleteafterMessageDeletionStandardisationmessageDeletionForbidden')
);
}
export function pushUnableToCall() {
@ -139,7 +142,6 @@ export function pushedMissedCallCauseOfPermission(conversationName: string) {
);
}
export function pushVideoCallPermissionNeeded() {
pushToastInfo(
'videoCallPermissionNeeded',
@ -187,16 +189,16 @@ export function pushDeleted() {
}
export function pushCannotRemoveCreatorFromGroup() {
pushToastWarning('cannotRemoveCreatorFromGroup', window.i18n('adminCannotBeRemoved'));
pushToastWarning('adminCannotBeRemoved', window.i18n('adminCannotBeRemoved'));
}
export function pushFailedToAddAsModerator() {
pushToastWarning('failedToAddAsModerator', window.i18n('adminPromotionFailed'));
pushToastWarning('adminPromotionFailed', window.i18n('adminPromotionFailed'));
}
export function pushFailedToRemoveFromModerator(name: string) {
pushToastWarning(
'failedToRemoveFromModerator',
'adminRemoveFailed',
window.i18n('adminRemoveFailed', {
name,
})

3
ts/window.d.ts vendored

@ -4,10 +4,7 @@ import {} from 'styled-components/cssprop';
import { Store } from '@reduxjs/toolkit';
import { Persistor } from 'redux-persist/es/types';
import { LocalizerType } from './types/Util';
import { ConversationCollection } from './models/conversation';
import type { StateType } from './state/reducer';
import { PrimaryColorStateType, ThemeStateType } from './themes/constants/colors';
import type { GetMessageArgs, LocalizerDictionary, LocalizerToken } from './types/Localizer';
import type { Locale } from './util/i18n';

Loading…
Cancel
Save