add back invite contacts to opengroupv2

pull/1576/head
Audric Ackermann 4 years ago
parent e274313f23
commit 37562e11f8
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -1,4 +1,4 @@
/* global Whisper, _ */
/* global Whisper */
// eslint-disable-next-line func-names
(function() {
@ -10,7 +10,6 @@
className: 'loki-dialog modal',
initialize(convo) {
this.close = this.close.bind(this);
this.submit = this.submit.bind(this);
this.theme = convo.theme;
const convos = window.getConversationController().getConversations();
@ -37,10 +36,10 @@
Component: window.Signal.Components.InviteContactsDialog,
props: {
contactList: this.contacts,
onSubmit: this.submit,
onClose: this.close,
chatName: this.chatName,
theme: this.theme,
convo: this.convo,
},
});
@ -50,53 +49,5 @@
close() {
this.remove();
},
submit(pubkeys) {
// public group chats
if (this.isPublic) {
const serverInfos = {
address: this.chatServer,
name: this.chatName,
channelId: this.channelId,
};
pubkeys.forEach(async pubkeyStr => {
const convo = await window
.getConversationController()
.getOrCreateAndWait(pubkeyStr, 'private');
if (convo) {
convo.sendMessage('', null, null, null, serverInfos);
}
});
} else {
// private group chats
const ourPK = window.libsession.Utils.UserUtils.getOurPubKeyStrFromCache();
let existingMembers = this.convo.get('members') || [];
// at least make sure it's an array
if (!Array.isArray(existingMembers)) {
existingMembers = [];
}
existingMembers = existingMembers.filter(d => !!d);
const newMembers = pubkeys.filter(d => !existingMembers.includes(d));
if (newMembers.length > 0) {
// Do not trigger an update if there is too many members
if (
newMembers.length + existingMembers.length >
window.CONSTANTS.CLOSED_GROUP_SIZE_LIMIT
) {
window.libsession.Utils.ToastUtils.pushTooManyMembers();
return;
}
const allMembers = window.Lodash.concat(existingMembers, newMembers, [ourPK]);
const uniqMembers = _.uniq(allMembers, true, d => d);
const groupId = this.convo.get('id');
const groupName = this.convo.get('name');
window.libsession.ClosedGroup.initiateGroupUpdate(groupId, groupName, uniqMembers);
}
}
},
});
})();

@ -3,14 +3,19 @@ import React from 'react';
import { SessionModal } from '../session/SessionModal';
import { SessionButton, SessionButtonColor } from '../session/SessionButton';
import { ContactType, SessionMemberListItem } from '../session/SessionMemberListItem';
import { DefaultTheme, withTheme } from 'styled-components';
import { DefaultTheme } from 'styled-components';
import { ConversationController } from '../../session/conversations';
import { ToastUtils, UserUtils } from '../../session/utils';
import { initiateGroupUpdate } from '../../session/group';
import { ConversationModel, ConversationType } from '../../models/conversation';
import { getCompleteUrlForV2ConvoId } from '../../interactions/conversation';
import _ from 'lodash';
interface Props {
contactList: Array<any>;
chatName: string;
onSubmit: any;
onClose: any;
theme: DefaultTheme;
convo: ConversationModel;
}
interface State {
@ -25,6 +30,8 @@ class InviteContactsDialogInner extends React.Component<Props, State> {
this.closeDialog = this.closeDialog.bind(this);
this.onClickOK = this.onClickOK.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
this.submitForOpenGroup = this.submitForOpenGroup.bind(this);
this.submitForClosedGroup = this.submitForClosedGroup.bind(this);
let contacts = this.props.contactList;
@ -89,11 +96,91 @@ class InviteContactsDialogInner extends React.Component<Props, State> {
);
}
private async submitForOpenGroup(pubkeys: Array<string>) {
const { convo } = this.props;
if (convo.isOpenGroupV1()) {
const v1 = convo.toOpenGroupV1();
const groupInvitation = {
serverAddress: v1.server,
serverName: convo.getName(),
channelId: 1, // always 1
};
pubkeys.forEach(async pubkeyStr => {
const privateConvo = await ConversationController.getInstance().getOrCreateAndWait(
pubkeyStr,
ConversationType.PRIVATE
);
if (privateConvo) {
void privateConvo.sendMessage('', null, null, null, groupInvitation);
}
});
} else if (convo.isOpenGroupV2()) {
const v2 = convo.toOpenGroupV2();
const completeUrl = await getCompleteUrlForV2ConvoId(convo.id);
const groupInvitation = {
serverAddress: completeUrl,
serverName: convo.getName(),
};
pubkeys.forEach(async pubkeyStr => {
const privateConvo = await ConversationController.getInstance().getOrCreateAndWait(
pubkeyStr,
ConversationType.PRIVATE
);
if (privateConvo) {
void privateConvo.sendMessage('', null, null, null, groupInvitation);
}
});
}
}
private async submitForClosedGroup(pubkeys: Array<string>) {
const { convo } = this.props;
// FIXME audric is this dialog still used for closed groups? I think
// public group chats
// private group chats
const ourPK = UserUtils.getOurPubKeyStrFromCache();
let existingMembers = convo.get('members') || [];
// at least make sure it's an array
if (!Array.isArray(existingMembers)) {
existingMembers = [];
}
existingMembers = _.compact(existingMembers);
const newMembers = pubkeys.filter(d => !existingMembers.includes(d));
if (newMembers.length > 0) {
// Do not trigger an update if there is too many members
if (newMembers.length + existingMembers.length > window.CONSTANTS.CLOSED_GROUP_SIZE_LIMIT) {
ToastUtils.pushTooManyMembers();
return;
}
const allMembers = _.concat(existingMembers, newMembers, [ourPK]);
const uniqMembers = _.uniq(allMembers);
const groupId = convo.get('id');
const groupName = convo.get('name');
await initiateGroupUpdate(
groupId,
groupName || window.i18n('unknown'),
uniqMembers,
undefined
);
}
}
private onClickOK() {
const selectedContacts = this.state.contactList.filter(d => d.checkmarked).map(d => d.id);
if (selectedContacts.length > 0) {
this.props.onSubmit(selectedContacts);
if (this.props.convo.isPublic()) {
void this.submitForOpenGroup(selectedContacts);
} else {
void this.submitForClosedGroup(selectedContacts);
}
}
this.closeDialog();

@ -29,7 +29,7 @@ import {
getOpenGroupV2ConversationId,
openGroupV2CompleteURLRegex,
} from '../../opengroup/utils/OpenGroupUtils';
import { joinOpenGroupV2, parseOpenGroupV2 } from '../../opengroup/opengroupV2/JoinOpenGroupV2';
import { joinOpenGroupV2WithUIEvents } from '../../opengroup/opengroupV2/JoinOpenGroupV2';
export interface Props {
searchTerm: string;
@ -423,34 +423,12 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
}
private async handleOpenGroupJoinV2(serverUrlV2: string) {
const parsedRoom = parseOpenGroupV2(serverUrlV2);
if (!parsedRoom) {
ToastUtils.pushToastError('connectToServer', window.i18n('invalidOpenGroupUrl'));
return;
}
try {
const conversationID = getOpenGroupV2ConversationId(parsedRoom.serverUrl, parsedRoom.roomId);
ToastUtils.pushToastInfo('connectingToServer', window.i18n('connectingToServer'));
this.setState({ loading: true });
await joinOpenGroupV2(parsedRoom, false);
const loadingCallback = (loading: boolean) => {
this.setState({ loading });
};
const joinSuccess = await joinOpenGroupV2WithUIEvents(serverUrlV2, true, loadingCallback);
const isConvoCreated = ConversationController.getInstance().get(conversationID);
if (isConvoCreated) {
ToastUtils.pushToastSuccess(
'connectToServerSuccess',
window.i18n('connectToServerSuccess')
);
return true;
} else {
ToastUtils.pushToastError('connectToServerFail', window.i18n('connectToServerFail'));
}
} catch (error) {
window.log.warn('got error while joining open group:', error);
ToastUtils.pushToastError('connectToServerFail', window.i18n('connectToServerFail'));
} finally {
this.setState({ loading: false });
}
return false;
return joinSuccess;
}
private async handleJoinChannelButtonClick(serverUrl: string) {

@ -11,15 +11,27 @@ import { ApiV2 } from '../opengroup/opengroupV2';
import _ from 'lodash';
export const getCompleteUrlForV2ConvoId = async (convoId: string) => {
if (convoId.match(openGroupV2ConversationIdRegex)) {
// this is a v2 group, just build the url
const roomInfos = await getV2OpenGroupRoom(convoId);
if (roomInfos) {
const fullUrl = getCompleteUrlFromRoom(roomInfos);
return fullUrl;
}
}
return undefined;
};
export async function copyPublicKey(convoId: string) {
if (convoId.match(openGroupPrefixRegex)) {
// open group v1 or v2
if (convoId.match(openGroupV2ConversationIdRegex)) {
// this is a v2 group, just build the url
const roomInfos = await getV2OpenGroupRoom(convoId);
if (roomInfos) {
const fullUrl = getCompleteUrlFromRoom(roomInfos);
window.clipboard.writeText(fullUrl);
const completeUrl = await getCompleteUrlForV2ConvoId(convoId);
if (completeUrl) {
window.clipboard.writeText(completeUrl);
ToastUtils.pushCopiedToClipBoard();
return;

@ -1,11 +1,17 @@
import _ from 'lodash';
import { getV2OpenGroupRoom } from '../data/opengroups';
import { ConversationModel } from '../models/conversation';
import { ConversationModel, ConversationType } from '../models/conversation';
import { OpenGroup } from '../opengroup/opengroupV1/OpenGroup';
import { ApiV2 } from '../opengroup/opengroupV2';
import { isOpenGroupV2 } from '../opengroup/utils/OpenGroupUtils';
import {
joinOpenGroupV2WithUIEvents,
parseOpenGroupV2,
} from '../opengroup/opengroupV2/JoinOpenGroupV2';
import { isOpenGroupV2, openGroupV2CompleteURLRegex } from '../opengroup/utils/OpenGroupUtils';
import { ConversationController } from '../session/conversations';
import { PubKey } from '../session/types';
import { ToastUtils } from '../session/utils';
import { openConversationExternal } from '../state/ducks/conversations';
export function banUser(userToBan: string, conversation?: ConversationModel) {
let pubKeyToBan: PubKey;
@ -190,3 +196,66 @@ export async function addSenderAsModerator(sender: string, convoId: string) {
window.log.error('Got error while adding moderator:', e);
}
}
async function acceptOpenGroupInvitationV1(serverAddress: string) {
try {
if (serverAddress.length === 0 || !OpenGroup.validate(serverAddress)) {
ToastUtils.pushToastError('connectToServer', window.i18n('invalidOpenGroupUrl'));
return;
}
// Already connected?
if (OpenGroup.getConversation(serverAddress)) {
ToastUtils.pushToastError('publicChatExists', window.i18n('publicChatExists'));
return;
}
// To some degree this has been copy-pasted from LeftPaneMessageSection
const rawServerUrl = serverAddress.replace(/^https?:\/\//i, '').replace(/[/\\]+$/i, '');
const sslServerUrl = `https://${rawServerUrl}`;
const conversationId = `publicChat:1@${rawServerUrl}`;
const conversationExists = ConversationController.getInstance().get(conversationId);
if (conversationExists) {
window.log.warn('We are already a member of this public chat');
ToastUtils.pushAlreadyMemberOpenGroup();
return;
}
const conversation = await ConversationController.getInstance().getOrCreateAndWait(
conversationId,
ConversationType.GROUP
);
await conversation.setPublicSource(sslServerUrl, 1);
const channelAPI = await window.lokiPublicChatAPI.findOrCreateChannel(
sslServerUrl,
1,
conversationId
);
if (!channelAPI) {
window.log.warn(`Could not connect to ${serverAddress}`);
return;
}
openConversationExternal(conversationId);
} catch (e) {
window.log.warn('failed to join opengroupv1 from invitation', e);
ToastUtils.pushToastError('connectToServerFail', window.i18n('connectToServerFail'));
}
}
const acceptOpenGroupInvitationV2 = async (completeUrl: string) => {
// 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)
*/
export const acceptOpenGroupInvitation = async (completeUrl: string) => {
if (completeUrl.match(openGroupV2CompleteURLRegex)) {
await acceptOpenGroupInvitationV2(completeUrl);
} else {
await acceptOpenGroupInvitationV1(completeUrl);
}
};

@ -664,9 +664,9 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
const groupInvitMessage = new GroupInvitationMessage({
identifier: id,
timestamp: sentAt,
serverName: groupInvitation.name,
channelId: groupInvitation.channelId,
serverAddress: groupInvitation.address,
serverName: groupInvitation.serverName,
channelId: 1,
serverAddress: groupInvitation.serverAddress,
expireTimer: this.get('expireTimer'),
});
// we need the return await so that errors are caught in the catch {}
@ -706,7 +706,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
attachments: any,
quote: any,
preview: any,
groupInvitation = null
groupInvitation: any = null
) {
this.clearTypingTimers();
@ -770,6 +770,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
return null;
}
this.queueJob(async () => {
console.warn('sending groupinvi', messageModel);
await this.sendMessageJob(messageModel, expireTimer);
});
return null;

@ -30,6 +30,7 @@ import {
uploadLinkPreviewsV2,
uploadQuoteThumbnailsV2,
} from '../session/utils/AttachmentsV2';
import { acceptOpenGroupInvitation } from '../interactions/message';
export class MessageModel extends Backbone.Model<MessageAttributes> {
public propsForTimerNotification: any;
public propsForGroupNotification: any;
@ -280,17 +281,16 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
if (!direction) {
direction = this.get('type') === 'outgoing' ? 'outgoing' : 'incoming';
}
const serverAddress = invitation.serverAddress?.length
? `${invitation.serverAddress.slice(0, 30)}...`
: '';
return {
serverName: invitation.serverName,
serverAddress: invitation.serverAddress,
serverAddress,
direction,
onClick: () => {
window.Whisper.events.trigger(
'publicChatInvitationAccepted',
invitation.serverAddress,
invitation.channelId
);
void acceptOpenGroupInvitation(invitation.serverAddress);
},
};
}

@ -4,7 +4,7 @@ import {
removeV2OpenGroupRoom,
} from '../../data/opengroups';
import { ConversationController } from '../../session/conversations';
import { PromiseUtils } from '../../session/utils';
import { PromiseUtils, ToastUtils } from '../../session/utils';
import { forceSyncConfigurationNowIfNeeded } from '../../session/utils/syncUtils';
import {
getOpenGroupV2ConversationId,
@ -58,10 +58,7 @@ export function parseOpenGroupV2(urlWithPubkey: string): OpenGroupV2Room | undef
* @param room The room id to join
* @param publicKey The server publicKey. It comes from the joining link. (or is already here for the default open group server)
*/
export async function joinOpenGroupV2(
room: OpenGroupV2Room,
fromSyncMessage: boolean
): Promise<void> {
async function joinOpenGroupV2(room: OpenGroupV2Room, fromSyncMessage: boolean): Promise<void> {
if (!room.serverUrl || !room.roomId || room.roomId.length < 2 || !room.serverPublicKey) {
return;
}
@ -110,3 +107,72 @@ export async function joinOpenGroupV2(
throw new Error(e);
}
}
/**
* This function does not throw
* This function can be used to join an opengroupv2 server, from a user initiated click or from a syncMessage.
* If the user made the request, the UI callback needs to be set.
* the callback will be called on loading events (start and stop joining). Also, this callback being set defines if we will trigger a sync message or not.
*
* Basically,
* - user invitation click => uicallback set
* - user join manually from the join open group field => uicallback set
* - joining from a sync message => no uicallback
*
*
* return true if the room did not exist before, and we join it correctly
*/
export async function joinOpenGroupV2WithUIEvents(
completeUrl: string,
showToasts: boolean,
uiCallback?: (loading: boolean) => void
): Promise<boolean> {
const parsedRoom = parseOpenGroupV2(completeUrl);
if (!parsedRoom) {
if (showToasts) {
ToastUtils.pushToastError('connectToServer', window.i18n('invalidOpenGroupUrl'));
}
return false;
}
try {
const conversationID = getOpenGroupV2ConversationId(parsedRoom.serverUrl, parsedRoom.roomId);
if (ConversationController.getInstance().get(conversationID)) {
if (showToasts) {
ToastUtils.pushToastError('publicChatExists', window.i18n('publicChatExists'));
}
return false;
}
if (showToasts) {
ToastUtils.pushToastInfo('connectingToServer', window.i18n('connectingToServer'));
}
if (uiCallback) {
uiCallback(true);
}
await joinOpenGroupV2(parsedRoom, showToasts);
const isConvoCreated = ConversationController.getInstance().get(conversationID);
if (isConvoCreated) {
if (showToasts) {
ToastUtils.pushToastSuccess(
'connectToServerSuccess',
window.i18n('connectToServerSuccess')
);
}
return true;
} else {
if (showToasts) {
ToastUtils.pushToastError('connectToServerFail', window.i18n('connectToServerFail'));
}
}
} catch (error) {
window.log.warn('got error while joining open group:', error);
if (showToasts) {
ToastUtils.pushToastError('connectToServerFail', window.i18n('connectToServerFail'));
}
} finally {
if (uiCallback) {
uiCallback(false);
}
}
return false;
}

Loading…
Cancel
Save