diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index cb565b89d..7a054b1e2 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -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 don’t 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": "{name} has set messages to disappear {time} after they have been {disappearing_messages_type}.",
- "disappearingMessagesTimer": "Timer",
"disappearingMessagesSetYou": "You set messages to disappear {time} after they have been {disappearing_messages_type}.",
+ "disappearingMessagesTimer": "Timer",
"disappearingMessagesTurnedOff": "{name} has turned disappearing messages off. Messages they send will no longer disappear.",
"disappearingMessagesTurnedOffYou": "You turned off disappearing messages. Messages you send will no longer disappear.",
"disappearingMessagesTypeRead": "read",
@@ -391,7 +393,19 @@
"groupMemberLeftTwo": "{name} and {other_name} left the group.",
"groupMemberMoreNew": "{name} and {count} others joined the group.",
"groupMemberNew": "{name} joined the group.",
+ "groupMemberNewHistory": "{name} was invited to join the group. Chat history was shared.",
+ "groupMemberNewHistoryMultiple": "{name} and {count} others were invited to join the group. Chat history was shared.",
+ "groupMemberNewHistoryTwo": "{name} and {other_name} were invited to join the group. Chat history was shared.",
+ "groupMemberNewMultiple": "{name} and {count} others were invited to join the group.",
+ "groupMemberNewTwo": "{name} and {other_name} were invited to join the group.",
+ "groupMemberNewYouHistory": " {name} was invited to join the group. Chat history was shared.",
+ "groupMemberNewYouHistoryMultiple": "You and {count} others were invited to join the group. Chat history was shared.",
+ "groupMemberNewYouHistoryTwo": "You and {name} were invited to join the group. Chat history was shared.",
+ "groupMemberNewYouMultiple": "You and {count} others were invited to join the group.",
+ "groupMemberNewYouTwo": "You and {name} were invited to join the group.",
"groupMemberTwoNew": "{name} and {other_name} joined the group.",
+ "groupMemberYouAndMoreNew": "You and {count} others joined the group.",
+ "groupMemberYouAndOtherNew": "You and {other_name} joined the group.",
"groupMemberYouLeft": "You 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 {group_name}. Send a message to start the conversation!",
"groupOnlyAdmin": "You are the only admin in {group_name}.
Group members and settings cannot be changed without an admin.",
"groupPromotedYou": "You were promoted to Admin.",
+ "groupPromotedYouMultiple": "You and {count} others were promoted to Admin.",
+ "groupPromotedYouTwo": "You and {name} were promoted to Admin.",
"groupRemoveDescription": "Would you like to remove {name} from {group_name}?",
"groupRemoveMessages": "Remove user and their messages",
"groupRemoveMoreDescription": "Would you like to remove {name} and {count} others from {group_name}?",
@@ -415,6 +431,8 @@
"groupRemovedMore": "{name} and {count} others were removed from the group.",
"groupRemovedTwo": "{name} and {other_name} were removed from the group.",
"groupRemovedYou": "You were removed from {group_name}.",
+ "groupRemovedYouMultiple": "You and {count} others were removed from the group.",
+ "groupRemovedYouTwo": "You and {other_name} 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...",
diff --git a/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx b/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx
index 67bb5b053..5ed237ae8 100644
--- a/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx
+++ b/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx
@@ -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 => {
+ 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 ( etc) here, and a SessionHtmlRenderer is going
+ // to render them correctly.
+ return getLeftGroupUpdateChangeStr(added, groupName, false);
};
const ChangeItemKicked = (kicked: Array): 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 ( etc) here, and a SessionHtmlRenderer is going
+ // to render them correctly.
+ return getKickedGroupUpdateStr(kicked, groupName, false);
};
const ChangeItemLeft = (left: Array): 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 ( etc) here, and a SessionHtmlRenderer is going
+ // to render them correctly.
+ return getLeftGroupUpdateChangeStr(left, groupName, false);
};
const ChangeItem = (change: PropsForGroupUpdateType): string => {
diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts
index 4c02a547f..d7a292dc4 100644
--- a/ts/models/conversation.ts
+++ b/ts/models/conversation.ts
@@ -2052,10 +2052,7 @@ export class ConversationModel extends Backbone.Model {
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}`);
}
diff --git a/ts/models/groupUpdate.ts b/ts/models/groupUpdate.ts
new file mode 100644
index 000000000..168001634
--- /dev/null
+++ b/ts/models/groupUpdate.ts
@@ -0,0 +1,104 @@
+import { getConversationController } from '../session/conversations';
+import { UserUtils } from '../session/utils';
+
+// to remove after merge with groups
+function usAndXOthers(arr: Array) {
+ 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,
+ 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,
+ _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,
+ _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,
+ });
+ }
+}
diff --git a/ts/models/message.ts b/ts/models/message.ts
index bbb4c27c7..be8a4c27a 100644
--- a/ts/models/message.ts
+++ b/ts/models/message.ts
@@ -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 | undefined) {
- return (
- arrayToCheck &&
- arrayToCheck.length === 1 &&
- (arrayToCheck[0] === UserUtils.getOurPubKeyStrFromCache() ||
- arrayToCheck[0].toLowerCase() === 'you')
- );
-}
-
-export function arrayContainsOneItemOnly(arrayToCheck: Array | undefined) {
- return arrayToCheck && arrayToCheck.length === 1;
-}
-
export class MessageModel extends Backbone.Model {
constructor(attributes: MessageAttributesOptionals & { skipTimerInit?: boolean }) {
const filledAttrs = fillMessageAttributesWithDefaults(attributes);
@@ -1289,26 +1269,9 @@ export class MessageModel extends Backbone.Model {
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 {
}
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()) {
diff --git a/ts/session/apis/open_group_api/opengroupV2/JoinOpenGroupV2.ts b/ts/session/apis/open_group_api/opengroupV2/JoinOpenGroupV2.ts
index 68b2cb408..bc3190e9b 100644
--- a/ts/session/apis/open_group_api/opengroupV2/JoinOpenGroupV2.ts
+++ b/ts/session/apis/open_group_api/opengroupV2/JoinOpenGroupV2.ts
@@ -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 });
}
diff --git a/ts/session/utils/Toast.tsx b/ts/session/utils/Toast.tsx
index cb35fd754..7b7596054 100644
--- a/ts/session/utils/Toast.tsx
+++ b/ts/session/utils/Toast.tsx
@@ -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,
})
diff --git a/ts/window.d.ts b/ts/window.d.ts
index aca545956..36701d6a6 100644
--- a/ts/window.d.ts
+++ b/ts/window.d.ts
@@ -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';