From 8c0bec7a978cdb2daeb6aba5181a507b4bb27db5 Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Wed, 16 Jun 2021 09:33:40 +1000 Subject: [PATCH] invite contacts dialog functioning. --- js/background.js | 56 ++--- js/views/app_view.js | 126 +++++------ js/views/invite_contacts_dialog_view.js | 116 +++++----- .../conversation/InviteContactsDialog.tsx | 207 +++++++++--------- .../conversation/SessionConversation.tsx | 19 +- 5 files changed, 272 insertions(+), 252 deletions(-) diff --git a/js/background.js b/js/background.js index c8bbe3e69..f5c382516 100644 --- a/js/background.js +++ b/js/background.js @@ -419,34 +419,34 @@ window.setMediaPermissions(!value); }; - Whisper.events.on('updateGroupName', async groupConvo => { - if (appView) { - appView.showUpdateGroupNameDialog(groupConvo); - } - }); - Whisper.events.on('updateGroupMembers', async groupConvo => { - if (appView) { - appView.showUpdateGroupMembersDialog(groupConvo); - } - }); - - Whisper.events.on('inviteContacts', async groupConvo => { - if (appView) { - appView.showInviteContactsDialog(groupConvo); - } - }); - - Whisper.events.on('addModerators', async groupConvo => { - if (appView) { - appView.showAddModeratorsDialog(groupConvo); - } - }); - - Whisper.events.on('removeModerators', async groupConvo => { - if (appView) { - appView.showRemoveModeratorsDialog(groupConvo); - } - }); + // Whisper.events.on('updateGroupName', async groupConvo => { + // if (appView) { + // appView.showUpdateGroupNameDialog(groupConvo); + // } + // }); + // Whisper.events.on('updateGroupMembers', async groupConvo => { + // if (appView) { + // appView.showUpdateGroupMembersDialog(groupConvo); + // } + // }); + + // Whisper.events.on('inviteContacts', async groupConvo => { + // if (appView) { + // appView.showInviteContactsDialog(groupConvo); + // } + // }); + + // Whisper.events.on('addModerators', async groupConvo => { + // if (appView) { + // appView.showAddModeratorsDialog(groupConvo); + // } + // }); + + // Whisper.events.on('removeModerators', async groupConvo => { + // if (appView) { + // appView.showRemoveModeratorsDialog(groupConvo); + // } + // }); Whisper.events.on('leaveClosedGroup', async groupConvo => { if (appView) { diff --git a/js/views/app_view.js b/js/views/app_view.js index 3276f70e7..fd477ffe1 100644 --- a/js/views/app_view.js +++ b/js/views/app_view.js @@ -128,68 +128,68 @@ const theme = themeSettings === 'light' ? window.lightTheme : window.darkTheme; return theme; }, - showUpdateGroupNameDialog(groupConvo) { - // eslint-disable-next-line no-param-reassign - groupConvo.theme = this.getThemeObject(); - - const dialog = new Whisper.UpdateGroupNameDialogView(groupConvo); - this.el.append(dialog.el); - }, - showUpdateGroupMembersDialog(groupConvo) { - // eslint-disable-next-line no-param-reassign - groupConvo.theme = this.getThemeObject(); - - const dialog = new Whisper.UpdateGroupMembersDialogView(groupConvo); - this.el.append(dialog.el); - }, - showLeaveGroupDialog(groupConvo) { - if (!groupConvo.isGroup()) { - throw new Error('showLeaveGroupDialog() called with a non group convo.'); - } - - const title = i18n('leaveGroup'); - const message = i18n('leaveGroupConfirmation'); - const ourPK = window.libsession.Utils.UserUtils.getOurPubKeyStrFromCache(); - const isAdmin = (groupConvo.get('groupAdmins') || []).includes(ourPK); - const isClosedGroup = groupConvo.get('is_medium_group') || false; - - // if this is not a closed group, or we are not admin, we can just show a confirmation dialog - if (!isClosedGroup || (isClosedGroup && !isAdmin)) { - window.confirmationDialog({ - title, - message, - resolve: () => groupConvo.leaveClosedGroup(), - theme: this.getThemeObject(), - }); - } else { - // we are the admin on a closed group. We have to warn the user about the group Deletion - this.showAdminLeaveClosedGroupDialog(groupConvo); - } - }, - showInviteContactsDialog(groupConvo) { - // eslint-disable-next-line no-param-reassign - groupConvo.theme = this.getThemeObject(); - const dialog = new Whisper.InviteContactsDialogView(groupConvo); - this.el.append(dialog.el); - }, - - showAdminLeaveClosedGroupDialog(groupConvo) { - // eslint-disable-next-line no-param-reassign - groupConvo.theme = this.getThemeObject(); - const dialog = new Whisper.AdminLeaveClosedGroupDialog(groupConvo); - this.el.append(dialog.el); - }, - showAddModeratorsDialog(groupConvo) { - // eslint-disable-next-line no-param-reassign - groupConvo.theme = this.getThemeObject(); - const dialog = new Whisper.AddModeratorsDialogView(groupConvo); - this.el.append(dialog.el); - }, - showRemoveModeratorsDialog(groupConvo) { - // eslint-disable-next-line no-param-reassign - groupConvo.theme = this.getThemeObject(); - const dialog = new Whisper.RemoveModeratorsDialogView(groupConvo); - this.el.append(dialog.el); - }, + // showUpdateGroupNameDialog(groupConvo) { + // // eslint-disable-next-line no-param-reassign + // groupConvo.theme = this.getThemeObject(); + + // const dialog = new Whisper.UpdateGroupNameDialogView(groupConvo); + // this.el.append(dialog.el); + // }, + // showUpdateGroupMembersDialog(groupConvo) { + // // eslint-disable-next-line no-param-reassign + // groupConvo.theme = this.getThemeObject(); + + // const dialog = new Whisper.UpdateGroupMembersDialogView(groupConvo); + // this.el.append(dialog.el); + // }, + // showLeaveGroupDialog(groupConvo) { + // if (!groupConvo.isGroup()) { + // throw new Error('showLeaveGroupDialog() called with a non group convo.'); + // } + + // const title = i18n('leaveGroup'); + // const message = i18n('leaveGroupConfirmation'); + // const ourPK = window.libsession.Utils.UserUtils.getOurPubKeyStrFromCache(); + // const isAdmin = (groupConvo.get('groupAdmins') || []).includes(ourPK); + // const isClosedGroup = groupConvo.get('is_medium_group') || false; + + // // if this is not a closed group, or we are not admin, we can just show a confirmation dialog + // if (!isClosedGroup || (isClosedGroup && !isAdmin)) { + // window.confirmationDialog({ + // title, + // message, + // resolve: () => groupConvo.leaveClosedGroup(), + // theme: this.getThemeObject(), + // }); + // } else { + // // we are the admin on a closed group. We have to warn the user about the group Deletion + // this.showAdminLeaveClosedGroupDialog(groupConvo); + // } + // }, + // showInviteContactsDialog(groupConvo) { + // // eslint-disable-next-line no-param-reassign + // groupConvo.theme = this.getThemeObject(); + // const dialog = new Whisper.InviteContactsDialogView(groupConvo); + // this.el.append(dialog.el); + // }, + + // showAdminLeaveClosedGroupDialog(groupConvo) { + // // eslint-disable-next-line no-param-reassign + // groupConvo.theme = this.getThemeObject(); + // const dialog = new Whisper.AdminLeaveClosedGroupDialog(groupConvo); + // this.el.append(dialog.el); + // }, + // showAddModeratorsDialog(groupConvo) { + // // eslint-disable-next-line no-param-reassign + // groupConvo.theme = this.getThemeObject(); + // const dialog = new Whisper.AddModeratorsDialogView(groupConvo); + // this.el.append(dialog.el); + // }, + // showRemoveModeratorsDialog(groupConvo) { + // // eslint-disable-next-line no-param-reassign + // groupConvo.theme = this.getThemeObject(); + // const dialog = new Whisper.RemoveModeratorsDialogView(groupConvo); + // this.el.append(dialog.el); + // }, }); })(); diff --git a/js/views/invite_contacts_dialog_view.js b/js/views/invite_contacts_dialog_view.js index e00173507..4328b5cb6 100644 --- a/js/views/invite_contacts_dialog_view.js +++ b/js/views/invite_contacts_dialog_view.js @@ -1,58 +1,58 @@ -/* global Whisper */ - -// eslint-disable-next-line func-names -(function() { - 'use strict'; - - window.Whisper = window.Whisper || {}; - - Whisper.InviteContactsDialogView = Whisper.View.extend({ - className: 'loki-dialog modal', - initialize(convo) { - this.close = this.close.bind(this); - this.theme = convo.theme; - const convos = window.getConversationController().getConversations(); - - this.contacts = convos.filter( - d => !!d && !d.isBlocked() && d.isPrivate() && !d.isMe() && !!d.get('active_at') - ); - if (!convo.isPublic()) { - // filter our zombies and current members from the list of contact we can add - - const members = convo.get('members') || []; - const zombies = convo.get('zombies') || []; - this.contacts = this.contacts.filter( - d => !members.includes(d.id) && !zombies.includes(d.id) - ); - } - - this.chatName = convo.get('name'); - this.chatServer = convo.get('server'); - this.channelId = convo.get('channelId'); - this.isPublic = !!convo.isPublic(); - this.convo = convo; - - this.$el.focus(); - this.render(); - }, - render() { - const view = new Whisper.ReactWrapperView({ - className: 'invite-friends-dialog', - Component: window.Signal.Components.InviteContactsDialog, - props: { - contactList: this.contacts, - onClose: this.close, - chatName: this.chatName, - theme: this.theme, - convo: this.convo, - }, - }); - - this.$el.append(view.el); - return this; - }, - close() { - this.remove(); - }, - }); -})(); +// /* global Whisper */ + +// // eslint-disable-next-line func-names +// (function() { +// 'use strict'; + +// window.Whisper = window.Whisper || {}; + +// Whisper.InviteContactsDialogView = Whisper.View.extend({ +// className: 'loki-dialog modal', +// initialize(convo) { +// this.close = this.close.bind(this); +// this.theme = convo.theme; +// const convos = window.getConversationController().getConversations(); + +// this.contacts = convos.filter( +// d => !!d && !d.isBlocked() && d.isPrivate() && !d.isMe() && !!d.get('active_at') +// ); +// if (!convo.isPublic()) { +// // filter our zombies and current members from the list of contact we can add + +// const members = convo.get('members') || []; +// const zombies = convo.get('zombies') || []; +// this.contacts = this.contacts.filter( +// d => !members.includes(d.id) && !zombies.includes(d.id) +// ); +// } + +// this.chatName = convo.get('name'); +// this.chatServer = convo.get('server'); +// this.channelId = convo.get('channelId'); +// this.isPublic = !!convo.isPublic(); +// this.convo = convo; + +// this.$el.focus(); +// this.render(); +// }, +// render() { +// const view = new Whisper.ReactWrapperView({ +// className: 'invite-friends-dialog', +// Component: window.Signal.Components.InviteContactsDialog, +// props: { +// contactList: this.contacts, +// onClose: this.close, +// chatName: this.chatName, +// theme: this.theme, +// convo: this.convo, +// }, +// }); + +// this.$el.append(view.el); +// return this; +// }, +// close() { +// this.remove(); +// }, +// }); +// })(); diff --git a/ts/components/conversation/InviteContactsDialog.tsx b/ts/components/conversation/InviteContactsDialog.tsx index 287e34520..799a63756 100644 --- a/ts/components/conversation/InviteContactsDialog.tsx +++ b/ts/components/conversation/InviteContactsDialog.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { SessionModal } from '../session/SessionModal'; import { SessionButton, SessionButtonColor } from '../session/SessionButton'; @@ -10,11 +10,12 @@ import { initiateGroupUpdate } from '../../session/group'; import { ConversationModel, ConversationTypeEnum } from '../../models/conversation'; import { getCompleteUrlForV2ConvoId } from '../../interactions/conversation'; import _ from 'lodash'; -import autoBind from 'auto-bind'; import { VALIDATION } from '../../session/constants'; +import { SessionWrapperModal } from '../session/SessionWrapperModal'; + + interface Props { - contactList: Array; - chatName: string; + // contactList: Array; onClose: any; theme: DefaultTheme; convo: ConversationModel; @@ -24,22 +25,38 @@ interface State { contactList: Array; } -class InviteContactsDialogInner extends React.Component { - constructor(props: any) { - super(props); - autoBind(this); +const InviteContactsDialogInner = (props: Props) => { + + const { convo, onClose, theme } = props; + + let contacts = ConversationController.getInstance().getConversations().filter( + d => !!d && !d.isBlocked() && d.isPrivate() && !d.isMe() && !!d.get('active_at') + ); + if (!convo.isPublic()) { + // filter our zombies and current members from the list of contact we can add + + const members = convo.get('members') || []; + const zombies = convo.get('zombies') || []; + contacts = contacts.filter( + d => !members.includes(d.id) && !zombies.includes(d.id) + ); + } - let contacts = this.props.contactList; + const chatName = convo.get('name'); + // const chatServer = convo.get('server'); + // const channelId = convo.get('channelId'); + const isPublicConvo = convo.isPublic(); - contacts = contacts.map(d => { + const [contactList, setContactList] = useState( + contacts.map((d: ConversationModel) => { const lokiProfile = d.getLokiProfile(); const nickname = d.getNickname(); const name = nickname ? nickname : lokiProfile - ? lokiProfile.displayName - : window.i18n('anonymous'); + ? lokiProfile.displayName + : window.i18n('anonymous'); // TODO: should take existing members into account const existingMember = false; @@ -48,58 +65,58 @@ class InviteContactsDialogInner extends React.Component { id: d.id, authorPhoneNumber: d.id, authorProfileName: name, - authorAvatarPath: d?.getAvatarPath(), + authorAvatarPath: d?.getAvatarPath() || '', selected: false, authorName: name, checkmarked: false, existingMember, }; - }); + }) + ) - this.state = { - contactList: contacts, - }; - window.addEventListener('keyup', this.onKeyUp); + const closeDialog = () => { + window.removeEventListener('keyup', onKeyUp); + onClose(); } - public render() { - const titleText = `${window.i18n('addingContacts')} ${this.props.chatName}`; - const cancelText = window.i18n('cancel'); - const okText = window.i18n('ok'); - - const hasContacts = this.state.contactList.length !== 0; - - return ( - -
- -
{this.renderMemberList()}
- {hasContacts ? null : ( - <> -
-

{window.i18n('noContactsToAdd')}

-
- - )} - -
- -
- - -
- - ); + const onClickOK = () => { + const selectedContacts = contactList.filter((d: ContactType) => d.checkmarked).map((d: ContactType) => d.id); + + if (selectedContacts.length > 0) { + if (isPublicConvo) { + void submitForOpenGroup(selectedContacts); + } else { + void submitForClosedGroup(selectedContacts); + } + } + + closeDialog(); } - private async submitForOpenGroup(pubkeys: Array) { - const { convo } = this.props; + const onKeyUp = (event: any) => { + switch (event.key) { + case 'Enter': + onClickOK(); + break; + case 'Esc': + case 'Escape': + closeDialog(); + break; + default: + } + } + window.addEventListener('keyup', onKeyUp); + + const titleText = `${window.i18n('addingContacts')} ${chatName}`; + const cancelText = window.i18n('cancel'); + const okText = window.i18n('ok'); + + const hasContacts = contactList.length !== 0; + + + const submitForOpenGroup = async (pubkeys: Array) => { + const { convo } = props; if (convo.isOpenGroupV1()) { const v1 = convo.toOpenGroupV1(); const groupInvitation = { @@ -137,8 +154,7 @@ class InviteContactsDialogInner extends React.Component { } } - private async submitForClosedGroup(pubkeys: Array) { - const { convo } = this.props; + const submitForClosedGroup = async (pubkeys: Array) => { // closed group chats const ourPK = UserUtils.getOurPubKeyStrFromCache(); @@ -178,23 +194,10 @@ class InviteContactsDialogInner extends React.Component { } } - private onClickOK() { - const selectedContacts = this.state.contactList.filter(d => d.checkmarked).map(d => d.id); - if (selectedContacts.length > 0) { - if (this.props.convo.isPublic()) { - void this.submitForOpenGroup(selectedContacts); - } else { - void this.submitForClosedGroup(selectedContacts); - } - } - - this.closeDialog(); - } - - private renderMemberList() { - const members = this.state.contactList; - const selectedContacts = this.state.contactList.filter(d => d.checkmarked).map(d => d.id); + const renderMemberList = () => { + const members = contactList; + const selectedContacts = contactList.filter((d: ContactType) => d.checkmarked).map((d: ContactType) => d.id); return members.map((member: ContactType, index: number) => ( { index={index} isSelected={selectedContacts.some(m => m === member.id)} onSelect={(selectedMember: ContactType) => { - this.onMemberClicked(selectedMember); + onMemberClicked(selectedMember); }} onUnselect={(selectedMember: ContactType) => { - this.onMemberClicked(selectedMember); + onMemberClicked(selectedMember); }} - theme={this.props.theme} + theme={theme} /> )); } - private onKeyUp(event: any) { - switch (event.key) { - case 'Enter': - this.onClickOK(); - break; - case 'Esc': - case 'Escape': - this.closeDialog(); - break; - default: - } - } - private onMemberClicked(clickedMember: ContactType) { - const updatedContacts = this.state.contactList.map(member => { + const onMemberClicked = (clickedMember: ContactType) => { + const updatedContacts = contactList.map((member: ContactType) => { if (member.id === clickedMember.id) { return { ...member, checkmarked: !member.checkmarked }; } else { return member; } }); - - this.setState(state => { - return { - ...state, - contactList: updatedContacts, - }; - }); + setContactList(updatedContacts); } - private closeDialog() { - window.removeEventListener('keyup', this.onKeyUp); - this.props.onClose(); - } + + return ( + +
+ +
{renderMemberList()}
+ {hasContacts ? null : ( + <> +
+

{window.i18n('noContactsToAdd')}

+
+ + )} + +
+ +
+ + +
+ + ); + } export const InviteContactsDialog = InviteContactsDialogInner; diff --git a/ts/components/session/conversation/SessionConversation.tsx b/ts/components/session/conversation/SessionConversation.tsx index 66d6551d3..84c353262 100644 --- a/ts/components/session/conversation/SessionConversation.tsx +++ b/ts/components/session/conversation/SessionConversation.tsx @@ -38,6 +38,7 @@ import { UpdateGroupNameDialog } from '../../conversation/UpdateGroupNameDialog' import { UpdateGroupMembersDialog } from '../../conversation/UpdateGroupMembersDialog'; import { getOurNumber } from '../../../state/selectors/user'; import { useSelector } from 'react-redux'; +import { InviteContactsDialog } from '../../conversation/InviteContactsDialog'; @@ -508,9 +509,10 @@ export class SessionConversation extends React.Component { let isAdmin = true; let titleText; + titleText = window.i18n('updateGroupDialogTitle', groupName); + if (isPublic) { // fix the title - titleText = window.i18n('updateGroupDialogTitle', groupName); // I'd much prefer to integrate mods with groupAdmins // but lets discuss first... isAdmin = conversation.isAdmin(window.storage.get('primaryDevicePubKey')); @@ -692,7 +694,16 @@ export class SessionConversation extends React.Component { // warrick: delete old code }, onInviteContacts: () => { - window.Whisper.events.trigger('inviteContacts', conversation); + this.setState({ + ...this.state, + modal: ( + this.setState({ ...this.state, modal: null })} + theme={this.props.theme} + />) + }) + }, onLeaveGroup: () => { window.Whisper.events.trigger('leaveClosedGroup', conversation); @@ -706,7 +717,7 @@ export class SessionConversation extends React.Component { convo={conversation} onClose={() => this.setState({ ...this.state, modal: null })} theme={this.props.theme} - >) + />) }) }, @@ -719,7 +730,7 @@ export class SessionConversation extends React.Component { convo={conversation} onClose={() => this.setState({ ...this.state, modal: null })} theme={this.props.theme} - >) + />) }) }, onShowLightBox: (lightBoxOptions = {}) => {