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: Add ability to create guest links with password (SQSERVICES-1975) #15014

Merged
merged 65 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
2753f4d
feat: Add ability to create guest links with password (SQSERVICES-1975)
thisisamir98 Apr 14, 2023
07e394c
feat: Add GuestLinkPasswordModal
thisisamir98 Apr 19, 2023
3479d34
join with login
thisisamir98 Apr 21, 2023
3aa2a77
sso password
thisisamir98 Apr 21, 2023
768b9d1
joing with deeplink
thisisamir98 Apr 24, 2023
02138ec
clean up copies
thisisamir98 Apr 24, 2023
8314b82
Merge branch 'dev' of github.com:wireapp/wire-webapp into feat/SQSERV…
thisisamir98 Apr 24, 2023
d16bc8f
fix error
thisisamir98 Apr 24, 2023
3a63325
Merge branch 'dev' of github.com:wireapp/wire-webapp into feat/SQSERV…
thisisamir98 Apr 24, 2023
73c5ec0
Update src/script/auth/component/GuestLinkPasswordModal.tsx
thisisamir98 Apr 25, 2023
c1a393a
backward compatibility
thisisamir98 Apr 25, 2023
8019074
Merge branch 'feat/SQSERVICES-1975' of github.com:wireapp/wire-webapp…
thisisamir98 Apr 25, 2023
fc0df1a
Update src/script/auth/component/GuestLinkPasswordModal.tsx
thisisamir98 Apr 26, 2023
4cab38d
use on if
thisisamir98 Apr 26, 2023
498955a
Merge branch 'feat/SQSERVICES-1975' of github.com:wireapp/wire-webapp…
thisisamir98 Apr 26, 2023
9f0be47
fix backenderror
thisisamir98 Apr 26, 2023
a28c71c
fix type error
thisisamir98 Apr 26, 2023
d865548
Merge branch 'dev' of github.com:wireapp/wire-webapp into feat/SQSERV…
thisisamir98 Apr 27, 2023
d61bd21
BackendErrorlabel
thisisamir98 Apr 27, 2023
e6999db
fix type error
thisisamir98 Apr 27, 2023
eee35b8
fix state bug
thisisamir98 Apr 28, 2023
911156b
add loading state
thisisamir98 May 2, 2023
6fdc56b
bump core
thisisamir98 May 3, 2023
1f33a06
use api-client supportsGuestLinksWithPassword
thisisamir98 May 3, 2023
92112cb
feat: conversation name
thisisamir98 May 8, 2023
96b2824
Merge branch 'dev' of github.com:wireapp/wire-webapp into feat/SQSERV…
thisisamir98 May 8, 2023
656afbc
add URL_LEARN_MORE_ABOUT_GUEST_LINKS
thisisamir98 May 8, 2023
ef65588
throw error
thisisamir98 May 8, 2023
155c129
Merge branch 'dev' of github.com:wireapp/wire-webapp into feat/SQSERV…
thisisamir98 Jun 19, 2023
5ec9259
runfix: password not secure option
thisisamir98 Jun 19, 2023
d0ab5ea
copy your password reminder message
thisisamir98 Jun 21, 2023
2625b8f
The text inputs wrong labels
thisisamir98 Jun 21, 2023
0927ef5
disabled state
thisisamir98 Jun 21, 2023
80adff0
design review for joining on desktop
thisisamir98 Jun 23, 2023
cabae78
remove log
thisisamir98 Jun 27, 2023
ac71cc1
Merge branch 'dev' of github.com:wireapp/wire-webapp into feat/SQSERV…
thisisamir98 Jun 27, 2023
d2f4adc
disabled copy to clipboard
thisisamir98 Jun 27, 2023
2d7f09f
border color on focus
thisisamir98 Jun 27, 2023
55a9aed
rename
thisisamir98 Jun 27, 2023
c399830
fix redirect bug
thisisamir98 Jun 27, 2023
7646ede
invalid password error handling
thisisamir98 Jun 30, 2023
a8c6d0d
login and single sign on password error handling
thisisamir98 Jun 30, 2023
1e853c9
fix broken tests
thisisamir98 Jun 30, 2023
39475fb
fix ts error
thisisamir98 Jun 30, 2023
f3b2127
add password conditions check
thisisamir98 Jul 24, 2023
ac430c1
add modal close button
thisisamir98 Jul 24, 2023
351d3ec
add close button to login
thisisamir98 Jul 24, 2023
f25adf8
close modal on sso with link pass
thisisamir98 Jul 24, 2023
a2835f5
fix primary button disabled
thisisamir98 Jul 25, 2023
2f6c9d7
password copy
thisisamir98 Jul 25, 2023
d500b7f
chore: merge with dev
tlebon Oct 23, 2023
224a75b
feat: update guest link passwords for new convo join page
tlebon Oct 24, 2023
e8967d0
fix: issue with some edge cases not working
tlebon Oct 26, 2023
c02ea4b
chore: merge dev
tlebon Nov 9, 2023
0c84609
chore: remove error throw
tlebon Nov 9, 2023
5e70571
fix: remove form param submission bug
tlebon Nov 9, 2023
72a0561
chore: remove password from error on L132
tlebon Nov 13, 2023
c643e5f
chore: missing error catch
tlebon Nov 13, 2023
ccfc429
feat: possible fix for open issues
tlebon Nov 13, 2023
c94981f
fix: password verification
tlebon Nov 14, 2023
45c45db
chore: condense password regex
tlebon Nov 20, 2023
772e337
feat: update primary modal
tlebon Nov 20, 2023
6f16f41
chore: disable guest link passwords feature
tlebon Nov 27, 2023
6164354
chore: merge dev
tlebon Dec 19, 2023
59d4ab0
chore: merge dev
tlebon Dec 19, 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 @@ -5,7 +5,7 @@
"@emotion/react": "11.10.6",
"@types/eslint": "8.37.0",
"@wireapp/avs": "9.1.13",
"@wireapp/core": "40.1.9",
"@wireapp/core": "40.1.10",
"@wireapp/lru-cache": "3.8.1",
"@wireapp/react-ui-kit": "9.6.0",
"@wireapp/store-engine-dexie": "2.0.5",
Expand Down
1 change: 1 addition & 0 deletions server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ const config = {
LEGAL_HOLD_BLOCK: process.env.URL_SUPPORT_LEGAL_HOLD_BLOCK,
MICROPHONE_ACCESS_DENIED: process.env.URL_SUPPORT_MICROPHONE_ACCESS_DENIED,
PRIVACY_VERIFY_FINGERPRINT: process.env.URL_SUPPORT_PRIVACY_VERIFY_FINGERPRINT,
LEARN_MORE_ABOUT_GUEST_LINKS: process.env.URL_SUPPORT_LEARN_MORE_ABOUT_GUEST_LINKS,
SCREEN_ACCESS_DENIED: process.env.URL_SUPPORT_SCREEN_ACCESS_DENIED,
},
TEAMS_BASE: process.env.URL_TEAMS_BASE,
Expand Down
24 changes: 24 additions & 0 deletions src/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -572,8 +572,25 @@
"guestOptionsCreateLink": "Create link",
"guestOptionsInfoHeader": "Invite others with a link",
"guestOptionsInfoText": "Invite others with a link to this conversation. Anyone with the link can join the conversation, even if they don’t have {{brandName}}.",
"guestOptionsInfoPasswordSecured": "Link is password secured",
"guestOptionsInfoTextWithPassword": "Users are asked to enter the password before they can join the conversation with a guest link.",
"guestOptionsInfoTextForgetPassword": "Forgot password? Revoke the link and create a new one.",
"guestOptionsInfoModalTitle": "Create password secured link",
"guestOptionsInfoModalTitleSubTitle": "People who want to join the conversation via the guest link need to enter this password first.",
"guestOptionsInfoModalCancel": "Cancel",
"guestOptionsInfoModalFormLabel": "Guest link password",
"guestOptionsInfoModalAction": "Create Link",
"guestOptionsInfoModalTitleBoldSubTitle": "You can't change the password later. Make sure to copy and store it.",
"guestOptionsInfoTextSecureWithPassword": "You can also secure the link with a password.",
"guestOptionsPasswordRadioLabel": "Guest link password",
"guestOptionsPasswordRadioOptionSecured": "Password secured",
"guestOptionsPasswordRadioOptionNotSecured": "Not password secured",
"guestOptionsPasswordCopyToClipboard": "Copy Password",
"guestOptionsPasswordCopyToClipboardSuccess": "Password Copied!",
"guestOptionsPasswordForceToCopy": "You need to copy the password so that you can store and share it with people you want to invite.",
"guestOptionsRevokeLink": "Revoke link",
"guestOptionsTitle": "Guests",
"generatePassword": "Generate password",
"guestRoomConversationBadge": "[bold]Guests[/bold] are present",
"guestRoomConversationBadgeExternal": "[bold]Externals[/bold] are present",
"guestRoomConversationBadgeExternalAndGuest": "[bold]Externals[/bold] and [bold]guests[/bold] are present",
Expand All @@ -596,6 +613,11 @@
"guestRoomToggleInfoDisabled": "You can't disable the guest option in this conversation, as it has been created by someone from another team.",
"guestRoomToggleInfoExtended": "Open this conversation to people outside your team. You can always change it later.",
"guestRoomToggleInfoHead": "Guest Links",
"guestLinkPasswordModal.headline": "[Group Conversation] \n Enter password",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this \n really working 🤔 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah if you do white-space: pre or pre-line it works.

"guestLinkPasswordModal.description": "Please enter the password you have received with the access link for this conversation.",
"guestLinkPasswordModal.passwordInputLabel": "Conversation password",
"guestLinkPasswordModal.learnMoreLink": "Learn more about guest links",
"guestLinkPasswordModal.joinConversation": "Join Conversation",
"guestRoomToggleName": "Allow Guests",
"historyInfo.learnMore": "Learn more",
"historyInfo.noHistoryHeadline": "It’s the first time you’re using {brandName} on this device.",
Expand Down Expand Up @@ -814,6 +836,8 @@
"modalConversationGuestOptionsGetCodeMessage": "Could not get access link.",
"modalConversationGuestOptionsRequestCodeMessage": "Could not request access link. Please try again.",
"modalConversationGuestOptionsRevokeCodeMessage": "Could not revoke access link. Please try again.",
"modalGuestLinkJoinPlaceholder": "Password Input",
"modalGuestLinkJoinHelperText": "Must have at least one: uppercase letter, lowercase letter, number, symbol",
"modalConversationJoinConfirm": "Join",
"modalConversationJoinFullHeadline": "You could not join the conversation",
"modalConversationJoinFullMessage": "The conversation is full.",
Expand Down
2 changes: 2 additions & 0 deletions src/script/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export class Configuration {
MICROPHONE_ACCESS_DENIED: 'https://support.wire.com/hc/articles/202590081',
PRIVACY_VERIFY_FINGERPRINT: 'https://support.wire.com/hc/en-us/articles/207692235',
SCREEN_ACCESS_DENIED: 'https://support.wire.com/hc/articles/202935412',
LEARN_MORE_ABOUT_GUEST_LINKS:
'https://support.wire.com/hc/en-us/articles/360000574069-Share-a-link-with-a-person-without-a-Wire-account-to-join-a-guest-room-conversation-in-my-team',
},
TEAMS_BASE: 'https://teams.wire.com',
TEAMS_BILLING: 'https://teams.wire.com/billing',
Expand Down
65 changes: 65 additions & 0 deletions src/script/auth/component/GuestLinkPasswordModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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 {fireEvent, render} from '@testing-library/react';

import {GuestLinkPasswordModal, GuestLinkPasswordModalProps} from './GuestLinkPasswordModal';

import {withIntl, withTheme} from '../util/test/TestUtil';

describe('GuestLinkPasswordModal', () => {
const onSubmitPasswordMock = jest.fn();
const props: GuestLinkPasswordModalProps = {
onSubmitPassword: onSubmitPasswordMock,
};

beforeEach(() => {
onSubmitPasswordMock.mockClear();
});

it('should call onSubmitPassword with the password value when the form is submitted', () => {
const {getByTestId} = render(withTheme(withIntl(<GuestLinkPasswordModal {...props} />)));
const input = getByTestId('guest-link-join-password-input') as HTMLInputElement;
const joinConversationButton = getByTestId('guest-link-join-submit-button') as HTMLButtonElement;
fireEvent.change(input, {target: {value: 'password'}});
joinConversationButton.click();
expect(onSubmitPasswordMock).toHaveBeenCalledWith('password');
});

it('should disable the join conversation button when the password input is empty', () => {
const {getByTestId} = render(withTheme(withIntl(<GuestLinkPasswordModal {...props} />)));
const joinConversationButton = getByTestId('guest-link-join-submit-button') as HTMLButtonElement;
expect(joinConversationButton.disabled).toBe(true);
});

it('should enable the join conversation button when the password input is not empty', () => {
const {getByTestId} = render(withTheme(withIntl(<GuestLinkPasswordModal {...props} />)));
const input = getByTestId('guest-link-join-password-input');
const joinConversationButton = getByTestId('guest-link-join-submit-button') as HTMLButtonElement;
fireEvent.change(input, {target: {value: 'password'}});
expect(joinConversationButton.disabled).toBe(false);
});

it('should not call onSubmitPassword with an empty string when the form is submitted with an empty password input', () => {
const {getByTestId} = render(withTheme(withIntl(<GuestLinkPasswordModal {...props} />)));
const joinConversationButton = getByTestId('guest-link-join-submit-button') as HTMLButtonElement;
joinConversationButton.click();
expect(onSubmitPasswordMock).toHaveBeenCalledTimes(0);
});
});
89 changes: 89 additions & 0 deletions src/script/auth/component/GuestLinkPasswordModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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 React, {useState} from 'react';

import {useIntl} from 'react-intl';

import {Button, COLOR, Container, Form, H2, Input, Link, Modal, Text} from '@wireapp/react-ui-kit';

import {Config} from '../../Config';
import {guestLinkPasswordModalStrings} from '../../strings';

export interface GuestLinkPasswordModalProps {
onSubmitPassword: (password: string) => void;
}

const GuestLinkPasswordModal: React.FC<GuestLinkPasswordModalProps> = ({onSubmitPassword}) => {
const {formatMessage: _} = useIntl();
const [passwordValue, setPasswordValue] = useState<string>('');

const onSubmit = () => {
onSubmitPassword(passwordValue);
};

return (
<Modal>
<Container style={{maxWidth: '400px'}}>
<H2 style={{whiteSpace: 'break-spaces', fontWeight: 500, marginTop: '10px', textAlign: 'center'}}>
{_(guestLinkPasswordModalStrings.headline)}
</H2>
<Text block fontSize="16px" style={{marginBottom: 24}}>
thisisamir98 marked this conversation as resolved.
Show resolved Hide resolved
{_(guestLinkPasswordModalStrings.description)}
</Text>
<Form
name="guest-password-join-form"
data-uie-name="guest-password-join-form"
onSubmit={onSubmit}
autoComplete="off"
>
<Input
data-uie-name="guest-link-join-password-input"
name="guest-join-password"
required
placeholder={_(guestLinkPasswordModalStrings.passwordInputLabel)}
label={_(guestLinkPasswordModalStrings.passwordInputLabel)}
id="guest_link_join_password"
className="modal__input"
type="password"
autoComplete="off"
value={passwordValue}
onChange={event => setPasswordValue(event.currentTarget.value)}
/>
</Form>
<Link href={Config.getConfig().URL.SUPPORT.LEARN_MORE_ABOUT_GUEST_LINKS} target="_blank">
<Text block color={COLOR.BLUE} style={{textDecoration: 'underline', marginBottom: 24}}>
{_(guestLinkPasswordModalStrings.learnMoreLink)}
</Text>
</Link>
<Button
block
type="button"
disabled={passwordValue === ''}
thisisamir98 marked this conversation as resolved.
Show resolved Hide resolved
onClick={onSubmit}
data-uie-name="guest-link-join-submit-button"
>
{_(guestLinkPasswordModalStrings.joinConversation)}
</Button>
</Container>
</Modal>
);
};

export {GuestLinkPasswordModal};
3 changes: 2 additions & 1 deletion src/script/auth/module/action/AuthAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class AuthAction {
code: string,
uri?: string,
getEntropy?: () => Promise<Uint8Array>,
password?: string,
): ThunkAction => {
const onBeforeLogin: LoginLifecycleFunction = async (dispatch, getState, {actions: {authAction}}) =>
dispatch(authAction.doSilentLogout());
Expand All @@ -74,7 +75,7 @@ export class AuthAction {
getState,
{actions: {localStorageAction, conversationAction}},
) => {
const conversation = await dispatch(conversationAction.doJoinConversationByCode(key, code, uri));
const conversation = await dispatch(conversationAction.doJoinConversationByCode(key, code, uri, password));
const domain = conversation?.qualified_conversation?.domain;
return (
conversation &&
Expand Down
1 change: 1 addition & 0 deletions src/script/auth/module/action/BackendError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class BackendError extends Error {
CONVERSATION_CODE_NOT_FOUND: 'no-conversation-code',
CONVERSATION_NOT_FOUND: 'no-conversation',
CONVERSATION_TOO_MANY_MEMBERS: 'too-many-members',
INVALID_CONVERSATION_PASSWORD: 'invalid-conversation-password',
};

static GENERAL_ERRORS = {
Expand Down
9 changes: 7 additions & 2 deletions src/script/auth/module/action/ConversationAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,16 @@ export class ConversationAction {
};
};

doJoinConversationByCode = (key: string, code: string, uri?: string): ThunkAction<Promise<ConversationEvent>> => {
doJoinConversationByCode = (
key: string,
code: string,
uri?: string,
password?: string,
): ThunkAction<Promise<ConversationEvent>> => {
return async (dispatch, getState, {apiClient}) => {
dispatch(ConversationActionCreator.startJoinConversationByCode());
try {
const conversationEvent = await apiClient.api.conversation.postJoinByCode({code, key, uri});
const conversationEvent = await apiClient.api.conversation.postJoinByCode({code, key, uri, password});
dispatch(ConversationActionCreator.successfulJoinConversationByCode(conversationEvent));
return conversationEvent;
} catch (error) {
Expand Down
Loading