Merge pull request #1607 from Bilb/redesign-group-invitation

Redesign group invitation and ask user confirmation before accepting one
pull/1611/head
Audric Ackermann 5 years ago committed by GitHub
commit 1d22c12bf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -289,6 +289,27 @@
"incomingError": { "incomingError": {
"message": "Error handling incoming message" "message": "Error handling incoming message"
}, },
"openGroupInvitation": {
"message": "Open group invitation"
},
"joinOpenGroupAfterInvitationConfirmationTitle": {
"message": "Join $roomName$?",
"placeholders": {
"count": {
"roomName": "$1",
"example": "Main"
}
}
},
"joinOpenGroupAfterInvitationConfirmationDesc": {
"message": "Are you sure you wan to join the $roomName$ open group?",
"placeholders": {
"count": {
"roomName": "$1",
"example": "Main"
}
}
},
"media": { "media": {
"message": "Media", "message": "Media",
"description": "Header of the default pane in the media gallery, showing images and videos", "description": "Header of the default pane in the media gallery, showing images and videos",
@ -1587,9 +1608,6 @@
"message": "Add", "message": "Add",
"androidKey": "fragment_add_public_chat_add_button_title_1" "androidKey": "fragment_add_public_chat_add_button_title_1"
}, },
"groupInvitation": {
"message": "Group Invitation"
},
"addingContacts": { "addingContacts": {
"message": "Adding contacts to" "message": "Adding contacts to"
}, },

@ -187,7 +187,6 @@ message DataMessage {
message GroupInvitation { message GroupInvitation {
optional string serverAddress = 1; optional string serverAddress = 1;
optional uint32 channelId = 2;
optional string serverName = 3; optional string serverName = 3;
} }

@ -31,7 +31,6 @@
} }
.check-box-container { .check-box-container {
// background-color: blue;
align-items: center; align-items: center;
flex-direction: row; flex-direction: row;
display: inline-flex; display: inline-flex;
@ -52,15 +51,19 @@
} }
.group-invitation { .group-invitation {
background-color: #f4f4f0; @include themify($themes) {
background: themed('receivedMessageBackground');
}
&.invitation-outgoing {
@include themify($themes) {
background: themed('sentMessageBackground');
}
}
display: inline-block; display: inline-block;
margin: 4px 16px; margin: 4px 16px;
padding: 4px; padding: 4px;
border: solid; border-radius: 15px;
border-width: 0.5px;
border-radius: 4px;
border-color: #e0e0e0;
align-self: flex-start; align-self: flex-start;
@ -87,11 +90,11 @@
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;
padding: 8px; padding: 0px 12px;
.group-name { .group-name {
font-weight: lighter; font-weight: bold;
padding-bottom: 4px; font-size: 18px;
} }
.group-address { .group-address {
@ -99,19 +102,18 @@
} }
} }
.join-btn { .session-icon-button {
background-color: #00f782; @include themify($themes) {
color: white; background-color: themed('accent');
padding: 6px 10px;
margin-inline-start: 6px;
border-radius: 2px;
box-shadow: none; box-shadow: none;
user-select: none; }
cursor: pointer; filter: brightness(1.05);
svg path {
transition: $session-transition-duration; transition: $session-transition-duration;
opacity: 0.6;
&:hover { @include themify($themes) {
background-color: #00d672; fill: themed('textColorOpposite');
}
} }
} }
} }

@ -1,41 +1,43 @@
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { SessionButton, SessionButtonType } from '../session/SessionButton';
import { SessionIconButton, SessionIconSize, SessionIconType } from '../session/icon';
import { useTheme } from 'styled-components';
interface Props { type Props = {
serverName: string; serverName: string;
serverAddress: string; serverAddress: string;
direction: string; direction: string;
onClick: any; onJoinClick: () => void;
} };
export class GroupInvitation extends React.Component<Props> { export const GroupInvitation = (props: Props) => {
public render() { const theme = useTheme();
const classes = ['group-invitation']; const classes = ['group-invitation'];
if (this.props.direction === 'outgoing') { if (props.direction === 'outgoing') {
classes.push('invitation-outgoing'); classes.push('invitation-outgoing');
} }
const openGroupInvitation = window.i18n('openGroupInvitation');
return ( return (
<div className={'group-invitation-container'}> <div className="group-invitation-container">
<div className={classNames(classes)}> <div className={classNames(classes)}>
<div className="title">Group invitation</div>
<div className="contents"> <div className="contents">
<img <SessionIconButton
alt="group-avatar" iconType={SessionIconType.Plus}
src="images/session/session_chat_icon.png" iconColor={theme.colors.accent}
className="invite-group-avatar" theme={theme}
iconSize={SessionIconSize.Large}
onClick={props.onJoinClick}
/> />
<span className="group-details"> <span className="group-details">
<span className="group-name">{this.props.serverName}</span> <span className="group-name">{props.serverName}</span>
<span className="group-address">{this.props.serverAddress}</span> <span className="group-type">{openGroupInvitation}</span>
</span> <span className="group-address">{props.serverAddress}</span>
<span role="button" className="join-btn" onClick={this.props.onClick}>
Join
</span> </span>
</div> </div>
</div> </div>
</div> </div>
); );
} };
}

@ -241,17 +241,21 @@ async function acceptOpenGroupInvitationV1(serverAddress: string) {
} }
} }
const acceptOpenGroupInvitationV2 = async (completeUrl: string) => { const acceptOpenGroupInvitationV2 = (completeUrl: string, roomName?: string) => {
window.confirmationDialog({
title: window.i18n('joinOpenGroupAfterInvitationConfirmationTitle', roomName),
message: window.i18n('joinOpenGroupAfterInvitationConfirmationDesc', roomName),
resolve: () => joinOpenGroupV2WithUIEvents(completeUrl, true),
});
// this function does not throw, and will showToasts if anything happens // this function does not throw, and will showToasts if anything happens
await joinOpenGroupV2WithUIEvents(completeUrl, true);
}; };
/** /**
* Accepts a v1 (channelid defaults to 1) url or a v2 url (with pubkey) * Accepts a v1 (channelid defaults to 1) url or a v2 url (with pubkey)
*/ */
export const acceptOpenGroupInvitation = async (completeUrl: string) => { export const acceptOpenGroupInvitation = async (completeUrl: string, roomName?: string) => {
if (completeUrl.match(openGroupV2CompleteURLRegex)) { if (completeUrl.match(openGroupV2CompleteURLRegex)) {
await acceptOpenGroupInvitationV2(completeUrl); acceptOpenGroupInvitationV2(completeUrl, roomName);
} else { } else {
await acceptOpenGroupInvitationV1(completeUrl); await acceptOpenGroupInvitationV1(completeUrl);
} }

@ -670,7 +670,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
identifier: id, identifier: id,
timestamp: sentAt, timestamp: sentAt,
serverName: groupInvitation.serverName, serverName: groupInvitation.serverName,
channelId: 1,
serverAddress: groupInvitation.serverAddress, serverAddress: groupInvitation.serverAddress,
expireTimer: this.get('expireTimer'), expireTimer: this.get('expireTimer'),
}); });

@ -186,7 +186,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
return window.i18n('incomingError'); return window.i18n('incomingError');
} }
if (this.isGroupInvitation()) { if (this.isGroupInvitation()) {
return `<${window.i18n('groupInvitation')}>`; return `😎 ${window.i18n('openGroupInvitation')}`;
} }
return this.get('body'); return this.get('body');
} }
@ -281,16 +281,21 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
if (!direction) { if (!direction) {
direction = this.get('type') === 'outgoing' ? 'outgoing' : 'incoming'; direction = this.get('type') === 'outgoing' ? 'outgoing' : 'incoming';
} }
const serverAddress = invitation.serverAddress?.length
? `${invitation.serverAddress.slice(0, 30)}...` let serverAddress = '';
: ''; try {
const url = new URL(invitation.serverAddress);
serverAddress = url.origin;
} catch (e) {
window.log.warn('failed to get hostname from opengroupv2 invitation', invitation);
}
return { return {
serverName: invitation.serverName, serverName: invitation.serverName,
serverAddress, serverAddress,
direction, direction,
onClick: () => { onJoinClick: () => {
void acceptOpenGroupInvitation(invitation.serverAddress); void acceptOpenGroupInvitation(invitation.serverAddress, invitation.serverName);
}, },
}; };
} }

@ -22,7 +22,7 @@ import autoBind from 'auto-bind';
import { sha256 } from '../../session/crypto'; import { sha256 } from '../../session/crypto';
import { fromBase64ToArrayBuffer } from '../../session/utils/String'; import { fromBase64ToArrayBuffer } from '../../session/utils/String';
const pollForEverythingInterval = SECONDS * 6; const pollForEverythingInterval = SECONDS * 4;
const pollForRoomAvatarInterval = DAYS * 1; const pollForRoomAvatarInterval = DAYS * 1;
const pollForMemberCountInterval = MINUTES * 10; const pollForMemberCountInterval = MINUTES * 10;
@ -409,7 +409,6 @@ const handleCompactPollResults = async (
serverUrl: string, serverUrl: string,
results: Array<ParsedRoomCompactPollResults> results: Array<ParsedRoomCompactPollResults>
) => { ) => {
// console.warn('compoll res', results);
await Promise.all( await Promise.all(
results.map(async res => { results.map(async res => {
const convoId = getOpenGroupV2ConversationId(serverUrl, res.roomId); const convoId = getOpenGroupV2ConversationId(serverUrl, res.roomId);

@ -643,7 +643,6 @@ function addMemberToZombies(
if (isAlreadyZombie) { if (isAlreadyZombie) {
return false; return false;
} }
// console.warn('Marking user ', userToAdd.key, ' as a zombie');
convo.set('zombies', [...zombies, userToAdd.key]); convo.set('zombies', [...zombies, userToAdd.key]);
return true; return true;
} }

@ -5,7 +5,6 @@ import { MessageParams } from '../Message';
interface GroupInvitationMessageParams extends MessageParams { interface GroupInvitationMessageParams extends MessageParams {
serverAddress: string; serverAddress: string;
channelId: number;
serverName: string; serverName: string;
// if there is an expire timer set for the conversation, we need to set it. // if there is an expire timer set for the conversation, we need to set it.
// otherwise, it will disable the expire timer on the receiving side. // otherwise, it will disable the expire timer on the receiving side.
@ -14,14 +13,12 @@ interface GroupInvitationMessageParams extends MessageParams {
export class GroupInvitationMessage extends DataMessage { export class GroupInvitationMessage extends DataMessage {
private readonly serverAddress: string; private readonly serverAddress: string;
private readonly channelId: number;
private readonly serverName: string; private readonly serverName: string;
private readonly expireTimer?: number; private readonly expireTimer?: number;
constructor(params: GroupInvitationMessageParams) { constructor(params: GroupInvitationMessageParams) {
super({ timestamp: params.timestamp, identifier: params.identifier }); super({ timestamp: params.timestamp, identifier: params.identifier });
this.serverAddress = params.serverAddress; this.serverAddress = params.serverAddress;
this.channelId = params.channelId;
this.serverName = params.serverName; this.serverName = params.serverName;
this.expireTimer = params.expireTimer; this.expireTimer = params.expireTimer;
} }
@ -29,7 +26,6 @@ export class GroupInvitationMessage extends DataMessage {
public dataProto(): SignalService.DataMessage { public dataProto(): SignalService.DataMessage {
const groupInvitation = new SignalService.DataMessage.GroupInvitation({ const groupInvitation = new SignalService.DataMessage.GroupInvitation({
serverAddress: this.serverAddress, serverAddress: this.serverAddress,
channelId: this.channelId,
serverName: this.serverName, serverName: this.serverName,
}); });

@ -443,7 +443,6 @@ const sendOnionRequest = async (
const guardUrl = `https://${nodePath[0].ip}:${nodePath[0].port}${target}`; const guardUrl = `https://${nodePath[0].ip}:${nodePath[0].port}${target}`;
// no logs for that one as we do need to call insecureNodeFetch to our guardNode // no logs for that one as we do need to call insecureNodeFetch to our guardNode
// window.log.info('insecureNodeFetch => plaintext for sendOnionRequest'); // window.log.info('insecureNodeFetch => plaintext for sendOnionRequest');
// console.warn('sendViaOnion payload: ', payload.length);
const response = await insecureNodeFetch(guardUrl, guardFetchOptions); const response = await insecureNodeFetch(guardUrl, guardFetchOptions);
return processOnionResponse(reqIdx, response, destCtx.symmetricKey, false, abortSignal); return processOnionResponse(reqIdx, response, destCtx.symmetricKey, false, abortSignal);

@ -67,8 +67,6 @@ export const forceSyncConfigurationNowIfNeeded = async (waitForMessageSent = fal
void getCurrentConfigurationMessage(allConvos) void getCurrentConfigurationMessage(allConvos)
.then(configMessage => { .then(configMessage => {
// console.warn('forceSyncConfigurationNowIfNeeded with', configMessage);
// this just adds the message to the sending queue. // this just adds the message to the sending queue.
// if waitForMessageSent is set, we need to effectively wait until then // if waitForMessageSent is set, we need to effectively wait until then
// tslint:disable-next-line: no-void-expression // tslint:disable-next-line: no-void-expression
@ -195,8 +193,6 @@ export const getCurrentConfigurationMessage = async (convos: Array<ConversationM
const activeOpenGroups = [...openGroupsV1Ids, ...opengroupV2CompleteUrls]; const activeOpenGroups = [...openGroupsV1Ids, ...opengroupV2CompleteUrls];
// console.warn('SyncConfiguration', activeOpenGroups);
return new ConfigurationMessage({ return new ConfigurationMessage({
identifier: uuid(), identifier: uuid(),
timestamp: Date.now(), timestamp: Date.now(),

@ -9,14 +9,12 @@ describe('GroupInvitationMessage', () => {
let message: GroupInvitationMessage; let message: GroupInvitationMessage;
const timestamp = Date.now(); const timestamp = Date.now();
const serverAddress = 'http://localhost'; const serverAddress = 'http://localhost';
const channelId = 1;
const serverName = 'test'; const serverName = 'test';
beforeEach(() => { beforeEach(() => {
message = new GroupInvitationMessage({ message = new GroupInvitationMessage({
timestamp, timestamp,
serverAddress, serverAddress,
channelId,
serverName, serverName,
}); });
}); });
@ -26,7 +24,6 @@ describe('GroupInvitationMessage', () => {
const decoded = SignalService.Content.decode(plainText); const decoded = SignalService.Content.decode(plainText);
expect(decoded.dataMessage?.groupInvitation).to.have.property('serverAddress', serverAddress); expect(decoded.dataMessage?.groupInvitation).to.have.property('serverAddress', serverAddress);
expect(decoded.dataMessage?.groupInvitation).to.have.property('channelId', channelId);
expect(decoded.dataMessage?.groupInvitation).to.have.property('serverName', serverName); expect(decoded.dataMessage?.groupInvitation).to.have.property('serverName', serverName);
}); });

Loading…
Cancel
Save