Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: MLS 1:1 conversations [WPB-2183] #15508

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
940a57a
feat: self user supported protocols
PatrykBuniX Jul 20, 2023
6a10d0f
runfix: initialise supported protocols with null
PatrykBuniX Jul 24, 2023
fa4ac9f
test: uncomment scenarios
PatrykBuniX Jul 24, 2023
04ee8d9
refactor: move self supported protocols to self repository
PatrykBuniX Jul 26, 2023
1758521
feat: initial work on 1:1 mls conversations
PatrykBuniX Jul 24, 2023
73fdb41
feat: move all conversation events to mls conversation
PatrykBuniX Jul 27, 2023
c4f110b
feat: replace proteus 1:1 with mls 1:1
PatrykBuniX Jul 28, 2023
b96f6b1
feat: delete proteus 1:1 after moving events to mls 1:1
PatrykBuniX Jul 31, 2023
26369e7
runfix: fetch all known users on app load to get fresh supported prot…
PatrykBuniX Aug 2, 2023
fd90931
feat: establish mls group when both users support mls
PatrykBuniX Aug 2, 2023
249bf0d
feat: if proteus 1:1 was active conversation, switch to mls
PatrykBuniX Aug 2, 2023
33428e8
init 1:1 conversation when navigating between conversations
PatrykBuniX Aug 4, 2023
1cb5700
runfix: always use mls 1:1 if its already known by client
PatrykBuniX Aug 7, 2023
e3ab1ab
runfix: add missing return statement
PatrykBuniX Aug 7, 2023
2a0fa92
feat: get 1to1 team conversation
PatrykBuniX Aug 14, 2023
c37d96e
feat: assign user connection to conversation
PatrykBuniX Aug 16, 2023
6943db7
chore: add todo
PatrykBuniX Aug 16, 2023
24166c5
feat: hide proteus 1:1 after receiving welcome to mls 1:1
PatrykBuniX Aug 17, 2023
fe6f198
chore: remove mls 1:1 feature flags
PatrykBuniX Aug 17, 2023
4e67c80
refactor: find 1:1 by uid and protocol
PatrykBuniX Aug 17, 2023
a352ef1
runfix: insert group creation message to 1:1 mls after replcing
PatrykBuniX Aug 17, 2023
c4acf48
test: fix conversation repo test
PatrykBuniX Aug 28, 2023
0eb2339
test: update loadUsers case
PatrykBuniX Aug 29, 2023
bab52ca
chore: add todo comment
PatrykBuniX Aug 30, 2023
f73ac13
test: fix test
PatrykBuniX Aug 31, 2023
61cf752
feat: get1to1Conversation
PatrykBuniX Sep 1, 2023
3fcf24b
runfix: do not insert group creation message
PatrykBuniX Sep 1, 2023
1a53274
feat: update participating user ids and entities after establishing
PatrykBuniX Sep 1, 2023
30241ac
refactor: move group establishment part to core
PatrykBuniX Sep 4, 2023
d268251
chore: bump core
PatrykBuniX Sep 4, 2023
99dc47d
runfix: is mls 1:1 already established
PatrykBuniX Sep 5, 2023
00a5d69
feat: add delay to the user that has received connection accepted event
PatrykBuniX Sep 5, 2023
0c0d560
chore: add comment to establishment delay
PatrykBuniX Sep 6, 2023
3b14647
chore: todo comment
PatrykBuniX Sep 7, 2023
d0aefcc
refactor: get initialised conversation
PatrykBuniX Sep 7, 2023
70cde62
runfix: put history state back to sethistoryparam call
PatrykBuniX Sep 7, 2023
4098454
chore: improve comments
PatrykBuniX Sep 7, 2023
68326e8
chore: improve comments
PatrykBuniX Sep 7, 2023
7ffa7f6
chore: improve comments
PatrykBuniX Sep 7, 2023
bad06de
test: refactor get1to1conversation tests
PatrykBuniX Sep 7, 2023
19d3837
test: get1To1Conversation
PatrykBuniX Sep 7, 2023
e66b8c9
chore: fix log
PatrykBuniX Sep 7, 2023
d9f6847
refactor: replace proteus as a part of init mls
PatrykBuniX Sep 7, 2023
5fa9e81
refactor: simplify mls init for unconnected connections
PatrykBuniX Sep 7, 2023
1adea1f
refactor: pass quid instead of the whole user entity
PatrykBuniX Sep 8, 2023
cfe78c3
feat: migrate conversation's local properties
PatrykBuniX Sep 8, 2023
13f8dc6
refactor: move establishment delay to establish method
PatrykBuniX Sep 11, 2023
27f0dd2
test: establish 1:1 mls conversation
PatrykBuniX Sep 11, 2023
df0cfab
test: replacing proteus 1:1 with mls 1:1
PatrykBuniX Sep 11, 2023
96b1833
test: move conversation events
PatrykBuniX Sep 11, 2023
6a3723f
feat: add readOnly flag for conversation (#15759)
arjita-mitra Sep 12, 2023
ce6454b
runfix: improve styles of readonly warning message
PatrykBuniX Sep 13, 2023
1d3f182
runfix: change info svg icon color
PatrykBuniX Sep 13, 2023
86b0ad1
runfix: hide proteus conversation before establishing a group
PatrykBuniX Sep 14, 2023
4349d41
runfix: show conversation before establishment delay
PatrykBuniX Sep 14, 2023
45cc295
Merge branch 'feat/mls-1to1-conversations' into feat/WPB-2183-select-…
PatrykBuniX Sep 19, 2023
86efcb7
refactor: init 1to1 conversation on app load
PatrykBuniX Sep 20, 2023
470968b
feat: handle case for multiple team 1:1 with the same user
PatrykBuniX Sep 21, 2023
98b52c8
refactor: remove unused type
PatrykBuniX Sep 21, 2023
95b0451
feat: reevaluate the list of supported protocols on mls feature confi…
PatrykBuniX Sep 22, 2023
3524e6c
runfix: reevaluate slef protocols only on team protocols update
PatrykBuniX Sep 25, 2023
8c9f557
chore: bump core
PatrykBuniX Sep 25, 2023
81987d8
Merge branch 'feat/mls-1to1-conversations' into feat/WPB-2183-select-…
PatrykBuniX Sep 27, 2023
61285e3
refactor: remove unused method after merge
PatrykBuniX Sep 27, 2023
c221d30
Merge branch 'feat/mls-1to1-conversations' into feat/WPB-2183-select-…
PatrykBuniX Oct 3, 2023
578a0bb
feat: re-evaluate 1:1 converstions after updating self-supported prot…
PatrykBuniX Oct 3, 2023
9315d8f
Merge branch 'feat/mls-1to1-conversations' into feat/WPB-2183-select-…
PatrykBuniX Oct 5, 2023
05c9eb6
Merge branch 'feat/mls-1to1-conversations' into feat/WPB-2183-select-…
PatrykBuniX Oct 5, 2023
2f85593
runfix: read otheruserid from participants list on welcome message
PatrykBuniX Oct 6, 2023
a182d54
runfix: add connect type to is1to1conversation proteus search
PatrykBuniX Oct 6, 2023
b736bcb
runfix: establish mls group before showing it
PatrykBuniX Oct 6, 2023
7edc7ef
chore: remove unused translation
PatrykBuniX Oct 10, 2023
343250e
chore: removed unrelated code
PatrykBuniX Oct 10, 2023
3f489b5
refactor: improve naming
PatrykBuniX Oct 10, 2023
631a3ac
chore: bump core
PatrykBuniX Oct 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"@datadog/browser-rum": "^4.50.0",
"@emotion/react": "11.11.1",
"@wireapp/avs": "9.4.14",
"@wireapp/core": "42.9.4",
"@wireapp/core": "42.11.0",
"@wireapp/lru-cache": "3.8.1",
"@wireapp/react-ui-kit": "9.9.9",
"@wireapp/store-engine-dexie": "2.1.6",
Expand Down
2 changes: 2 additions & 0 deletions src/__mocks__/@wireapp/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export class Account extends EventEmitter {
mlsGroupExistsLocally: jest.fn(),
joinByExternalCommit: jest.fn(),
addUsersToMLSConversation: jest.fn(),
isMLSGroupEstablishedLocally: jest.fn(),
establishMLS1to1Conversation: jest.fn(),
messageTimer: {
setConversationLevelTimer: jest.fn(),
},
Expand Down
6 changes: 5 additions & 1 deletion src/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -727,9 +727,13 @@
"messageFailedToSendWillNotReceiveSingular": "won't get your message.",
"messageFailedToSendWillReceivePlural": "will get your message later.",
"messageFailedToSendWillReceiveSingular": "will get your message later.",
"mlsConversationRecovered": "You havent used this device for a while, or an issue has occurred. Some older messages may not appear here.",
"mlsConversationRecovered": "You haven't used this device for a while, or an issue has occurred. Some older messages may not appear here.",
"mlsToggleInfo": "When this is on, conversation will use the new messaging layer security (MLS) protocol.",
"mlsToggleName": "MLS",
"selfNotSupportMLSMsgPart1": "You can't communicate with [bold]{{selfUserName}}[/bold] anymore, as your device doesn't support the suitable protocol.",
"downloadLatestMLS": "Download the latest MLS Wire version",
"selfNotSupportMLSMsgPart2": "to call, and send messages and files again.",
"otherUserNotSupportMLSMsg": "You can't communicate with [bold]{{participantName}}[/bold] anymore, as you two now use different protocols. When [bold]{{participantName}}[/bold] gets an update, you can call and send messages and files again.",
"modalAccountCreateAction": "OK",
"modalAccountCreateHeadline": "Create an account?",
"modalAccountCreateMessage": "By creating an account you will lose the conversation history in this guest room.",
Expand Down
3 changes: 2 additions & 1 deletion src/script/components/ConnectRequests/ConnectionRequests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,14 @@ export const ConnectRequests: FC<ConnectRequestsProps> = ({

const onAcceptClick = async (userEntity: User) => {
await actionsViewModel.acceptConnectionRequest(userEntity);

const conversationEntity = await actionsViewModel.getOrCreate1to1Conversation(userEntity);

if (connectionRequests.length === 1) {
/**
* In the connect request view modal, we show an overview of all incoming connection requests. When there are multiple open connection requests, we want that the user sees them all and can accept them one-by-one. When the last open connection request gets accepted, we want the user to switch to this conversation.
*/
actionsViewModel.open1to1Conversation(conversationEntity);
return actionsViewModel.open1to1Conversation(conversationEntity);
}
};

Expand Down
54 changes: 37 additions & 17 deletions src/script/components/Conversation/Conversation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {showWarningModal} from 'Components/Modals/utils/showWarningModal';
import {TitleBar} from 'Components/TitleBar';
import {CallState} from 'src/script/calling/CallState';
import {Config} from 'src/script/Config';
import {CONVERSATION_READONLY_STATE} from 'src/script/conversation/ConversationRepository';
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
import {allowsAllFiles, getFileExtensionOrName, hasAllowedExtension} from 'Util/FileTypeUtil';
import {isHittingUploadLimit} from 'Util/isHittingUploadLimit';
Expand All @@ -44,6 +45,7 @@ import {safeMailOpen, safeWindowOpen} from 'Util/SanitizationUtil';
import {formatBytes, incomingCssClass, removeAnimationsClass} from 'Util/util';

import {useReadReceiptSender} from './hooks/useReadReceipt';
import {ReadOnlyConversationMessage} from './ReadOnlyConversationMessage';
import {checkFileSharingPermission} from './utils/checkFileSharingPermission';

import {ConversationState} from '../../conversation/ConversationState';
Expand Down Expand Up @@ -71,6 +73,7 @@ interface ConversationProps {
readonly userState: UserState;
openRightSidebar: (panelState: PanelState, params: RightSidebarParams, compareEntityId?: boolean) => void;
isRightSidebarOpen?: boolean;
reloadApp: () => void;
}

const CONFIG = Config.getConfig();
Expand All @@ -81,6 +84,7 @@ export const Conversation: FC<ConversationProps> = ({
userState,
openRightSidebar,
isRightSidebarOpen = false,
reloadApp,
}) => {
const messageListLogger = getLogger('ConversationList');

Expand All @@ -99,7 +103,18 @@ export const Conversation: FC<ConversationProps> = ({
'classifiedDomains',
'isFileSharingSendingEnabled',
]);
const {is1to1, isRequest} = useKoSubscribableChildren(activeConversation!, ['is1to1', 'isRequest']);
const {
is1to1,
isRequest,
readOnlyState,
display_name: displayName,
} = useKoSubscribableChildren(activeConversation!, ['is1to1', 'isRequest', 'readOnlyState', 'display_name']);
const showReadOnlyConversationMessage =
readOnlyState !== null &&
[
CONVERSATION_READONLY_STATE.READONLY_ONE_TO_ONE_OTHER_UNSUPPORTED_MLS,
CONVERSATION_READONLY_STATE.READONLY_ONE_TO_ONE_SELF_UNSUPPORTED_MLS,
].includes(readOnlyState);
const {self: selfUser} = useKoSubscribableChildren(userState, ['self']);
const {inTeam} = useKoSubscribableChildren(selfUser, ['inTeam']);

Expand Down Expand Up @@ -472,6 +487,7 @@ export const Conversation: FC<ConversationProps> = ({
callActions={mainViewModel.calling.callActions}
openRightSidebar={openRightSidebar}
isRightSidebarOpen={isRightSidebarOpen}
isReadOnlyConversation={showReadOnlyConversationMessage}
/>

{activeCalls.map(call => {
Expand Down Expand Up @@ -520,22 +536,26 @@ export const Conversation: FC<ConversationProps> = ({
setMsgElementsFocusable={setMsgElementsFocusable}
/>

<InputBar
conversationEntity={activeConversation}
conversationRepository={repositories.conversation}
eventRepository={repositories.event}
messageRepository={repositories.message}
openGiphy={openGiphy}
propertiesRepository={repositories.properties}
searchRepository={repositories.search}
storageRepository={repositories.storage}
teamState={teamState}
selfUser={selfUser}
onShiftTab={() => setMsgElementsFocusable(false)}
uploadDroppedFiles={uploadDroppedFiles}
uploadImages={uploadImages}
uploadFiles={uploadFiles}
/>
{showReadOnlyConversationMessage ? (
<ReadOnlyConversationMessage state={readOnlyState} handleMLSUpdate={reloadApp} displayName={displayName} />
) : (
<InputBar
conversationEntity={activeConversation}
conversationRepository={repositories.conversation}
eventRepository={repositories.event}
messageRepository={repositories.message}
openGiphy={openGiphy}
propertiesRepository={repositories.properties}
searchRepository={repositories.search}
storageRepository={repositories.storage}
teamState={teamState}
selfUser={selfUser}
onShiftTab={() => setMsgElementsFocusable(false)}
uploadDroppedFiles={uploadDroppedFiles}
uploadImages={uploadImages}
uploadFiles={uploadFiles}
/>
)}

<div className="conversation-loading">
<div className="icon-spinner spin accent-text"></div>
Expand Down
77 changes: 77 additions & 0 deletions src/script/components/Conversation/ReadOnlyConversationMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import {FC} from 'react';

import {Link, LinkVariant} from '@wireapp/react-ui-kit';

import {Icon} from 'Components/Icon';
import {CONVERSATION_READONLY_STATE} from 'src/script/conversation/ConversationRepository';
import {t} from 'Util/LocalizerUtil';

interface ReadOnlyConversationMessageProps {
state: CONVERSATION_READONLY_STATE;
handleMLSUpdate: () => void;
displayName: string;
}
export const ReadOnlyConversationMessage: FC<ReadOnlyConversationMessageProps> = ({
state,
handleMLSUpdate,
displayName,
}) => {
const mlsCompatibilityMessage =
state === CONVERSATION_READONLY_STATE.READONLY_ONE_TO_ONE_OTHER_UNSUPPORTED_MLS
PatrykBuniX marked this conversation as resolved.
Show resolved Hide resolved
? t('otherUserNotSupportMLSMsg', displayName)
: t('selfNotSupportMLSMsgPart1', displayName);

return (
<div className="readonly-message-header readonly-message-container">
<div className="readonly-message-header-icon readonly-message-header-icon--svg">
<div>
<Icon.Info />
</div>
</div>
<p className="readonly-message-header-label" data-uie-name="element-readonly-conversation">
<span
dangerouslySetInnerHTML={{
__html: mlsCompatibilityMessage,
}}
/>
{state === CONVERSATION_READONLY_STATE.READONLY_ONE_TO_ONE_SELF_UNSUPPORTED_MLS && (
PatrykBuniX marked this conversation as resolved.
Show resolved Hide resolved
<>
{' '}
<Link
css={{fontSize: 'var(--font-size-small)', fontWeight: 600}}
onClick={handleMLSUpdate}
variant={LinkVariant.PRIMARY}
data-uie-name="do-update-mls"
>
{t('downloadLatestMLS')}
</Link>{' '}
<span
dangerouslySetInnerHTML={{
__html: t('selfNotSupportMLSMsgPart2'),
}}
/>
</>
)}
</p>
</div>
);
};
4 changes: 4 additions & 0 deletions src/script/components/TitleBar/TitleBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export interface TitleBarProps {
teamState: TeamState;
isRightSidebarOpen?: boolean;
callState?: CallState;
isReadOnlyConversation?: boolean;
}

export const TitleBar: React.FC<TitleBarProps> = ({
Expand All @@ -71,6 +72,7 @@ export const TitleBar: React.FC<TitleBarProps> = ({
isRightSidebarOpen = false,
callState = container.resolve(CallState),
teamState = container.resolve(TeamState),
isReadOnlyConversation = false,
}) => {
const {calling: callingRepository} = repositories;
const {
Expand Down Expand Up @@ -297,6 +299,7 @@ export const TitleBar: React.FC<TitleBarProps> = ({
showStartedCallAlert(isGroup, true);
}}
data-uie-name="do-video-call"
disabled={isReadOnlyConversation}
>
<Icon.Camera />
</button>
Expand All @@ -313,6 +316,7 @@ export const TitleBar: React.FC<TitleBarProps> = ({
showStartedCallAlert(isGroup);
}}
data-uie-name="do-call"
disabled={isReadOnlyConversation}
>
<Icon.Pickup />
</button>
Expand Down
2 changes: 2 additions & 0 deletions src/script/conversation/ConversationFilter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ describe('ConversationFilter', () => {
accessRoleV2: undefined,
access_role: undefined,
archived_state: false,
readonly_state: null,
archived_timestamp: 0,
cipher_suite: 1,
cleared_timestamp: 0,
Expand Down Expand Up @@ -92,6 +93,7 @@ describe('ConversationFilter', () => {
accessRoleV2: undefined,
access_role: CONVERSATION_LEGACY_ACCESS_ROLE.PRIVATE,
archived_state: false,
readonly_state: null,
archived_timestamp: 0,
cipher_suite: 1,
cleared_timestamp: 0,
Expand Down
7 changes: 0 additions & 7 deletions src/script/conversation/ConversationFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,10 @@
*
*/

import {matchQualifiedIds} from 'Util/QualifiedId';

import type {Conversation} from '../entity/Conversation';
import type {User} from '../entity/User';

export class ConversationFilter {
static is1To1WithUser(conversationEntity: Conversation, userEntity: User): boolean {
const [user] = conversationEntity.participating_user_ids();
return matchQualifiedIds(userEntity, user);
}

static isInTeam(conversationEntity: Conversation, userEntity: User): boolean {
return userEntity.teamId === conversationEntity.team_id && conversationEntity.domain === userEntity.domain;
}
Expand Down
Loading