Refactor confirm modal to redux.

pull/1665/head^2
Warrick Corfe-Tan 4 years ago
parent 1bfdbc5c93
commit e55cae697d

@ -39,7 +39,6 @@ export class MessageView extends React.Component {
async function createClosedGroup( async function createClosedGroup(
groupName: string, groupName: string,
groupMembers: Array<ContactType>, groupMembers: Array<ContactType>,
setModal: () => void
): Promise<boolean> { ): Promise<boolean> {
// Validate groupName and groupMembers length // Validate groupName and groupMembers length
if (groupName.length === 0) { if (groupName.length === 0) {
@ -64,7 +63,7 @@ async function createClosedGroup(
const groupMemberIds = groupMembers.map(m => m.id); const groupMemberIds = groupMembers.map(m => m.id);
await createClosedGroupV2(groupName, groupMemberIds, setModal); await createClosedGroupV2(groupName, groupMemberIds);
return true; return true;
} }

@ -34,6 +34,8 @@ export type StatusLightType = {
const OnionPathModalInner = (props: any) => { const OnionPathModalInner = (props: any) => {
const onionNodes = useSelector((state: StateType) => state.onionPaths.snodePath); const onionNodes = useSelector((state: StateType) => state.onionPaths.snodePath);
const confirmModalState = useSelector((state: StateType) => state);
console.log('onion path: ', confirmModalState);
const onionPath = onionNodes.path; const onionPath = onionNodes.path;
// including the device and destination in calculation // including the device and destination in calculation
const glowDuration = onionPath.length + 2; const glowDuration = onionPath.length + 2;

@ -2,52 +2,53 @@ import React from 'react';
import { SessionModal } from '../session/SessionModal'; import { SessionModal } from '../session/SessionModal';
import { SessionButton, SessionButtonColor } from '../session/SessionButton'; import { SessionButton, SessionButtonColor } from '../session/SessionButton';
import { DefaultTheme } from 'styled-components'; import { DefaultTheme, useTheme } from 'styled-components';
import { SessionWrapperModal } from '../session/SessionWrapperModal';
interface Props { interface Props {
groupName: string; groupName: string;
onSubmit: any; onSubmit: () => any;
onClose: any; onClose: any;
theme: DefaultTheme; theme: DefaultTheme;
} }
class AdminLeaveClosedGroupDialogInner extends React.Component<Props> { const AdminLeaveClosedGroupDialogInner = (props: Props) => {
constructor(props: any) {
super(props);
this.closeDialog = this.closeDialog.bind(this); const { groupName, theme, onSubmit, onClose } = props;
this.onClickOK = this.onClickOK.bind(this);
}
public render() { const titleText = `${window.i18n('leaveGroup')} ${groupName}`;
const titleText = `${window.i18n('leaveGroup')} ${this.props.groupName}`; const warningAsAdmin = `${window.i18n('leaveGroupConfirmationAdmin')}`;
const warningAsAdmin = `${window.i18n('leaveGroupConfirmationAdmin')}`; const okText = window.i18n('leaveAndRemoveForEveryone');
const okText = window.i18n('leaveAndRemoveForEveryone'); const cancelText = window.i18n('cancel');
return (
<SessionModal title={titleText} onClose={this.closeDialog} theme={this.props.theme}>
<div className="spacer-lg" />
<p>{warningAsAdmin}</p>
<div className="session-modal__button-group">
<SessionButton
text={okText}
onClick={this.onClickOK}
buttonColor={SessionButtonColor.Danger}
/>
</div>
</SessionModal>
);
}
private onClickOK() { const onClickOK = () => {
this.props.onSubmit(); void onSubmit();
this.closeDialog(); closeDialog();
} }
private closeDialog() { const closeDialog = () => {
this.props.onClose(); onClose();
} }
return (
<SessionWrapperModal title={titleText} onClose={closeDialog} theme={theme}>
<div className="spacer-lg" />
<p>{warningAsAdmin}</p>
<div className="session-modal__button-group">
<SessionButton
text={okText}
onClick={onClickOK}
buttonColor={SessionButtonColor.Danger}
/>
<SessionButton
text={cancelText}
onClick={closeDialog}
/>
</div>
</SessionWrapperModal>
);
} }
export const AdminLeaveClosedGroupDialog = AdminLeaveClosedGroupDialogInner; export const AdminLeaveClosedGroupDialog = AdminLeaveClosedGroupDialogInner;

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton'; import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton';
import { PubKey } from '../../session/types'; import { PubKey } from '../../session/types';
import { ToastUtils } from '../../session/utils'; import { ToastUtils } from '../../session/utils';
@ -8,6 +8,7 @@ import { SessionSpinner } from '../session/SessionSpinner';
import { Flex } from '../basic/Flex'; import { Flex } from '../basic/Flex';
import { ConversationModel } from '../../models/conversation'; import { ConversationModel } from '../../models/conversation';
import { ApiV2 } from '../../opengroup/opengroupV2'; import { ApiV2 } from '../../opengroup/opengroupV2';
import { processDecrypted } from '../../receiver/dataMessage';
interface Props { interface Props {
convo: ConversationModel; convo: ConversationModel;
onClose: any; onClose: any;
@ -20,33 +21,144 @@ interface State {
firstLoading: boolean; firstLoading: boolean;
} }
export class AddModeratorsDialog extends React.Component<Props, State> { // export class AddModeratorsDialog extends React.Component<Props, State> {
private channelAPI: any; // private channelAPI: any;
constructor(props: Props) { // constructor(props: Props) {
super(props); // super(props);
this.addAsModerator = this.addAsModerator.bind(this); // this.addAsModerator = this.addAsModerator.bind(this);
this.onPubkeyBoxChanges = this.onPubkeyBoxChanges.bind(this); // this.onPubkeyBoxChanges = this.onPubkeyBoxChanges.bind(this);
this.state = { // this.state = {
inputBoxValue: '', // inputBoxValue: '',
addingInProgress: false, // addingInProgress: false,
firstLoading: true, // firstLoading: true,
}; // };
} // }
public async componentDidMount() { // public async componentDidMount() {
if (this.props.convo.isOpenGroupV1()) { // if (this.props.convo.isOpenGroupV1()) {
this.channelAPI = await this.props.convo.getPublicSendData(); // this.channelAPI = await this.props.convo.getPublicSendData();
// }
// this.setState({ firstLoading: false });
// }
// public async addAsModerator() {
// // if we don't have valid data entered by the user
// const pubkey = PubKey.from(this.state.inputBoxValue);
// if (!pubkey) {
// window.log.info('invalid pubkey for adding as moderator:', this.state.inputBoxValue);
// ToastUtils.pushInvalidPubKey();
// return;
// }
// window.log.info(`asked to add moderator: ${pubkey.key}`);
// try {
// this.setState({
// addingInProgress: true,
// });
// let isAdded: any;
// if (this.props.convo.isOpenGroupV1()) {
// isAdded = await this.channelAPI.serverAPI.addModerator([pubkey.key]);
// } else {
// // this is a v2 opengroup
// const roomInfos = this.props.convo.toOpenGroupV2();
// isAdded = await ApiV2.addModerator(pubkey, roomInfos);
// }
// if (!isAdded) {
// window.log.warn('failed to add moderators:', isAdded);
// ToastUtils.pushUserNeedsToHaveJoined();
// } else {
// window.log.info(`${pubkey.key} added as moderator...`);
// ToastUtils.pushUserAddedToModerators();
// // clear input box
// this.setState({
// inputBoxValue: '',
// });
// }
// } catch (e) {
// window.log.error('Got error while adding moderator:', e);
// } finally {
// this.setState({
// addingInProgress: false,
// });
// }
// }
// public render() {
// const { i18n } = window;
// const { addingInProgress, inputBoxValue, firstLoading } = this.state;
// const chatName = this.props.convo.get('name');
// const title = `${i18n('addModerators')}: ${chatName}`;
// const renderContent = !firstLoading;
// return (
// <SessionModal title={title} onClose={() => this.props.onClose()} theme={this.props.theme}>
// <Flex container={true} flexDirection="column" alignItems="center">
// {renderContent && (
// <>
// <p>Add Moderator:</p>
// <input
// type="text"
// className="module-main-header__search__input"
// placeholder={i18n('enterSessionID')}
// dir="auto"
// onChange={this.onPubkeyBoxChanges}
// disabled={addingInProgress}
// value={inputBoxValue}
// />
// <SessionButton
// buttonType={SessionButtonType.Brand}
// buttonColor={SessionButtonColor.Primary}
// onClick={this.addAsModerator}
// text={i18n('add')}
// disabled={addingInProgress}
// />
// </>
// )}
// <SessionSpinner loading={addingInProgress || firstLoading} />
// </Flex>
// </SessionModal>
// );
// }
// private onPubkeyBoxChanges(e: any) {
// const val = e.target.value;
// this.setState({ inputBoxValue: val });
// }
// }
export const AddModeratorsDialog = (props: any) => {
const { convo, onClose, theme } = props;
const [inputBoxValue, setInputBoxValue] = useState('');
const [addingInProgress, setAddingInProgress] = useState(false);
const [firstLoading, setFirstLoading] = useState(true);
let channelAPI: any;
useEffect(() => {
async function getPublicSendData {
if (props.convo.isOpenGroupV1()) {
channelAPI = await convo.getPublicSendData();
}
setFirstLoading(false);
} }
}, [])
this.setState({ firstLoading: false });
}
public async addAsModerator() { const addAsModerator = async () => {
// if we don't have valid data entered by the user // if we don't have valid data entered by the user
const pubkey = PubKey.from(this.state.inputBoxValue); const pubkey = PubKey.from(inputBoxValue);
if (!pubkey) { if (!pubkey) {
window.log.info('invalid pubkey for adding as moderator:', this.state.inputBoxValue); window.log.info('invalid pubkey for adding as moderator:', this.state.inputBoxValue);
ToastUtils.pushInvalidPubKey(); ToastUtils.pushInvalidPubKey();
@ -56,15 +168,13 @@ export class AddModeratorsDialog extends React.Component<Props, State> {
window.log.info(`asked to add moderator: ${pubkey.key}`); window.log.info(`asked to add moderator: ${pubkey.key}`);
try { try {
this.setState({ setAddingInProgress(true);
addingInProgress: true,
});
let isAdded: any; let isAdded: any;
if (this.props.convo.isOpenGroupV1()) { if (convo.isOpenGroupV1()) {
isAdded = await this.channelAPI.serverAPI.addModerator([pubkey.key]); isAdded = await channelAPI.serverAPI.addModerator([pubkey.key]);
} else { } else {
// this is a v2 opengroup // this is a v2 opengroup
const roomInfos = this.props.convo.toOpenGroupV2(); const roomInfos = props.convo.toOpenGroupV2();
isAdded = await ApiV2.addModerator(pubkey, roomInfos); isAdded = await ApiV2.addModerator(pubkey, roomInfos);
} }
if (!isAdded) { if (!isAdded) {
@ -76,60 +186,57 @@ export class AddModeratorsDialog extends React.Component<Props, State> {
ToastUtils.pushUserAddedToModerators(); ToastUtils.pushUserAddedToModerators();
// clear input box // clear input box
this.setState({ setInputBoxValue('');
inputBoxValue: '',
});
} }
} catch (e) { } catch (e) {
window.log.error('Got error while adding moderator:', e); window.log.error('Got error while adding moderator:', e);
} finally { } finally {
this.setState({ setAddingInProgress(false);
addingInProgress: false,
});
} }
}
public render() {
const { i18n } = window;
const { addingInProgress, inputBoxValue, firstLoading } = this.state;
const chatName = this.props.convo.get('name');
const title = `${i18n('addModerators')}: ${chatName}`;
const renderContent = !firstLoading;
return (
<SessionModal title={title} onClose={() => this.props.onClose()} theme={this.props.theme}>
<Flex container={true} flexDirection="column" alignItems="center">
{renderContent && (
<>
<p>Add Moderator:</p>
<input
type="text"
className="module-main-header__search__input"
placeholder={i18n('enterSessionID')}
dir="auto"
onChange={this.onPubkeyBoxChanges}
disabled={addingInProgress}
value={inputBoxValue}
/>
<SessionButton
buttonType={SessionButtonType.Brand}
buttonColor={SessionButtonColor.Primary}
onClick={this.addAsModerator}
text={i18n('add')}
disabled={addingInProgress}
/>
</>
)}
<SessionSpinner loading={addingInProgress || firstLoading} />
</Flex>
</SessionModal>
);
} }
private onPubkeyBoxChanges(e: any) { const { i18n } = window;
// const { addingInProgress, inputBoxValue, firstLoading } = this.state;
const chatName = props.convo.get('name');
const title = `${i18n('addModerators')}: ${chatName}`;
const renderContent = !firstLoading;
const onPubkeyBoxChanges = (e: any) => {
const val = e.target.value; const val = e.target.value;
this.setState({ inputBoxValue: val }); setInputBoxValue(val);
} }
return (
<SessionModal title={title} onClose={() => onClose()} theme={theme}>
<Flex container={true} flexDirection="column" alignItems="center">
{renderContent && (
<>
<p>Add Moderator:</p>
<input
type="text"
className="module-main-header__search__input"
placeholder={i18n('enterSessionID')}
dir="auto"
onChange={onPubkeyBoxChanges}
disabled={addingInProgress}
value={inputBoxValue}
/>
<SessionButton
buttonType={SessionButtonType.Brand}
buttonColor={SessionButtonColor.Primary}
onClick={addAsModerator}
text={i18n('add')}
disabled={addingInProgress}
/>
</>
)}
<SessionSpinner loading={addingInProgress || firstLoading} />
</Flex>
</SessionModal>
);
} }

@ -33,9 +33,13 @@ import { OpenGroupManagerV2 } from '../../opengroup/opengroupV2/OpenGroupManager
import { loadDefaultRooms } from '../../opengroup/opengroupV2/ApiUtil'; import { loadDefaultRooms } from '../../opengroup/opengroupV2/ApiUtil';
import { forceRefreshRandomSnodePool } from '../../session/snode_api/snodePool'; import { forceRefreshRandomSnodePool } from '../../session/snode_api/snodePool';
import { SwarmPolling } from '../../session/snode_api/swarmPolling'; import { SwarmPolling } from '../../session/snode_api/swarmPolling';
import _ from 'lodash'; import _, { divide } from 'lodash';
import { ActionPanelOnionStatusLight, OnionPathModal } from '../OnionStatusDialog'; import { ActionPanelOnionStatusLight, OnionPathModal } from '../OnionStatusDialog';
import { EditProfileDialog } from '../EditProfileDialog'; import { EditProfileDialog } from '../EditProfileDialog';
import { StateType } from '../../state/reducer';
import { updateConfirmModal } from "../../state/ducks/modalDialog";
import { useInView } from 'react-intersection-observer';
import { SessionConfirm } from './SessionConfirm';
// tslint:disable-next-line: no-import-side-effect no-submodule-imports // tslint:disable-next-line: no-import-side-effect no-submodule-imports
@ -72,6 +76,11 @@ const Section = (props: {
/* tslint:disable:no-void-expression */ /* tslint:disable:no-void-expression */
if (type === SectionType.Profile) { if (type === SectionType.Profile) {
// window.showEditProfileDialog(); // window.showEditProfileDialog();
console.log("edit profile");
// window.inboxStore?.dispatch(updateConfirmModal({ title: "title test" }));
// dispatch(updateConfirmModal({ title: "title test" }));
// setModal(<EditProfileDialog2 onClose={() => setModal(null)}></EditProfileDialog2>); // setModal(<EditProfileDialog2 onClose={() => setModal(null)}></EditProfileDialog2>);
setModal(<EditProfileDialog onClose={handleModalClose} theme={theme} ></EditProfileDialog>); setModal(<EditProfileDialog onClose={handleModalClose} theme={theme} ></EditProfileDialog>);
@ -134,21 +143,21 @@ const Section = (props: {
return ( return (
<> <>
{type === SectionType.PathIndicator ? {type === SectionType.PathIndicator ?
<ActionPanelOnionStatusLight <ActionPanelOnionStatusLight
handleClick={handleClick} handleClick={handleClick}
isSelected={isSelected} isSelected={isSelected}
/> />
: :
<SessionIconButton <SessionIconButton
iconSize={SessionIconSize.Medium} iconSize={SessionIconSize.Medium}
iconType={iconType} iconType={iconType}
iconColor={iconColor} iconColor={iconColor}
notificationCount={unreadToShow} notificationCount={unreadToShow}
onClick={handleClick} onClick={handleClick}
isSelected={isSelected} isSelected={isSelected}
theme={theme} theme={theme}
/> />
} }
</> </>
); );
}; };
@ -260,24 +269,45 @@ export const ActionsPanel = () => {
void forceRefreshRandomSnodePool(); void forceRefreshRandomSnodePool();
}, DAYS * 1); }, DAYS * 1);
const formatLog = (s: any ) => {
console.log("@@@@:: ", s);
}
// const confirmModalState = useSelector((state: StateType) => state);
const confirmModalState = useSelector((state: StateType) => state.confirmModal);
console.log('@@@ confirm modal state', confirmModalState);
// formatLog(confirmModalState.modalState.title);
formatLog(confirmModalState);
// formatLog(confirmModalState2);
return ( return (
<> <>
{modal ? modal : null} {modal ? modal : null}
<div className="module-left-pane__sections-container"> {/* { confirmModalState && confirmModalState.title ? <div>{confirmModalState.title}</div> : null} */}
<Section { confirmModalState ? <SessionConfirm {...confirmModalState} />: null}
setModal={setModal} <div className="module-left-pane__sections-container">
type={SectionType.Profile} <Section
avatarPath={ourPrimaryConversation.avatarPath} setModal={setModal}
/> type={SectionType.Profile}
<Section type={SectionType.Message} /> avatarPath={ourPrimaryConversation.avatarPath}
<Section type={SectionType.Contact} /> />
<Section type={SectionType.Settings} /> <Section type={SectionType.Message} />
<Section type={SectionType.Contact} />
<SessionToastContainer /> <Section type={SectionType.Settings} />
<Section setModal={setModal} type={SectionType.PathIndicator} /> <SessionToastContainer />
<Section type={SectionType.Moon} />
</div> <Section
setModal={setModal}
type={SectionType.PathIndicator} />
<Section type={SectionType.Moon} />
</div>
</> </>
); );
}; };

@ -447,7 +447,7 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
return; return;
} }
this.setState({ loading: true }, async () => { this.setState({ loading: true }, async () => {
const groupCreated = await MainViewController.createClosedGroup(groupName, groupMembers, setModal); const groupCreated = await MainViewController.createClosedGroup(groupName, groupMembers);
if (groupCreated) { if (groupCreated) {
this.handleToggleOverlay(undefined); this.handleToggleOverlay(undefined);

@ -1,16 +1,13 @@
import React from 'react'; import React from 'react';
import { SessionIconButton, SessionIconSize, SessionIconType } from './icon'; import { SessionIconButton, SessionIconSize, SessionIconType } from './icon';
import { SessionToggle } from './SessionToggle';
import { SessionIdEditable } from './SessionIdEditable'; import { SessionIdEditable } from './SessionIdEditable';
import { UserSearchDropdown } from './UserSearchDropdown'; import { UserSearchDropdown } from './UserSearchDropdown';
import { ContactType, SessionMemberListItem } from './SessionMemberListItem'; import { ContactType, SessionMemberListItem } from './SessionMemberListItem';
import { ConversationType } from '../../state/ducks/conversations'; import { ConversationType } from '../../state/ducks/conversations';
import { SessionButton, SessionButtonColor, SessionButtonType } from './SessionButton'; import { SessionButton, SessionButtonColor, SessionButtonType } from './SessionButton';
import { SessionSpinner } from './SessionSpinner'; import { SessionSpinner } from './SessionSpinner';
import { PillDivider } from './PillDivider';
import { DefaultTheme } from 'styled-components'; import { DefaultTheme } from 'styled-components';
import { UserUtils } from '../../session/utils';
import { ConversationTypeEnum } from '../../models/conversation'; import { ConversationTypeEnum } from '../../models/conversation';
import { SessionJoinableRooms } from './SessionJoinableDefaultRooms'; import { SessionJoinableRooms } from './SessionJoinableDefaultRooms';

@ -5,15 +5,18 @@ import { SessionHtmlRenderer } from './SessionHTMLRenderer';
import { SessionIcon, SessionIconSize, SessionIconType } from './icon'; import { SessionIcon, SessionIconSize, SessionIconType } from './icon';
import { DefaultTheme, useTheme, withTheme } from 'styled-components'; import { DefaultTheme, useTheme, withTheme } from 'styled-components';
import { SessionWrapperModal } from './SessionWrapperModal'; import { SessionWrapperModal } from './SessionWrapperModal';
import { useDispatch } from 'react-redux';
import { updateConfirmModal } from '../../state/ducks/modalDialog';
import { update } from 'lodash';
type Props = { export interface SessionConfirmDialogProps {
message: string; message?: string;
messageSub?: string; messageSub?: string;
title: string; title?: string;
onOk?: any; onOk?: any;
onClose?: any; onClose?: any;
onClickOk?: any; onClickOk?: () => any;
onClickClose?: any; onClickClose?: () => any;
okText?: string; okText?: string;
cancelText?: string; cancelText?: string;
hideCancel?: boolean; hideCancel?: boolean;
@ -22,20 +25,25 @@ type Props = {
sessionIcon?: SessionIconType; sessionIcon?: SessionIconType;
iconSize?: SessionIconSize; iconSize?: SessionIconSize;
theme?: DefaultTheme; theme?: DefaultTheme;
closeAfterClickOk?: boolean;
shouldShowConfirm?: () => boolean | undefined;
}; };
const SessionConfirmInner = (props: Props) => { const SessionConfirmInner = (props: SessionConfirmDialogProps) => {
const { const {
title = '', title = '',
message, message = '',
messageSub = '', messageSub = '',
okTheme = SessionButtonColor.Primary, okTheme = SessionButtonColor.Primary,
closeTheme = SessionButtonColor.Primary, closeTheme = SessionButtonColor.Primary,
onClickOk, onClickOk,
onClickClose, onClickClose,
closeAfterClickOk = true,
hideCancel = false, hideCancel = false,
sessionIcon, sessionIcon,
iconSize, iconSize,
shouldShowConfirm,
// updateConfirmModal
} = props; } = props;
const okText = props.okText || window.i18n('ok'); const okText = props.okText || window.i18n('ok');
@ -46,8 +54,43 @@ const SessionConfirmInner = (props: Props) => {
const messageSubText = messageSub ? 'session-confirm-main-message' : 'subtle'; const messageSubText = messageSub ? 'session-confirm-main-message' : 'subtle';
return ( /**
* Calls close function after the ok button is clicked. If no close method specified, closes the modal
*/
const onClickOkWithClose = () => {
if (onClickOk) {
onClickOk();
}
if (onClickClose) {
onClickClose();
}
}
const onClickOkHandler = () => {
if (onClickOk) {
onClickOk();
}
window.inboxStore?.dispatch(updateConfirmModal(null));
}
if (shouldShowConfirm && !shouldShowConfirm()) {
return null;
}
const onClickCancelHandler = () => {
if (onClickClose) {
onClickClose();
}
window.inboxStore?.dispatch(updateConfirmModal(null));
}
return (
<SessionWrapperModal <SessionWrapperModal
title={title} title={title}
onClose={onClickClose} onClose={onClickClose}
@ -75,10 +118,12 @@ const SessionConfirmInner = (props: Props) => {
</div> </div>
<div className="session-modal__button-group"> <div className="session-modal__button-group">
<SessionButton text={okText} buttonColor={okTheme} onClick={onClickOk} /> {/* <SessionButton text={okText} buttonColor={okTheme} onClick={closeAfterClickOk ? onClickOk : onClickOkWithClose} /> */}
<SessionButton text={okText} buttonColor={okTheme} onClick={onClickOkHandler} />
{!hideCancel && ( {!hideCancel && (
<SessionButton text={cancelText} buttonColor={closeTheme} onClick={onClickClose} /> // <SessionButton text={cancelText} buttonColor={closeTheme} onClick={onClickClose} />
<SessionButton text={cancelText} buttonColor={closeTheme} onClick={onClickCancelHandler} />
)} )}
</div> </div>

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { updateConfirmModal } from '../../state/ducks/modalDialog';
interface Props { interface Props {
active: boolean; active: boolean;
@ -14,6 +15,8 @@ interface Props {
// okTheme: 'danger', // okTheme: 'danger',
// } // }
confirmationDialogParams?: any | undefined; confirmationDialogParams?: any | undefined;
updateConfirmModal?: any;
} }
interface State { interface State {
@ -62,15 +65,25 @@ export class SessionToggle extends React.PureComponent<Props, State> {
if ( if (
this.props.confirmationDialogParams && this.props.confirmationDialogParams &&
this.props.updateConfirmModal &&
this.props.confirmationDialogParams.shouldShowConfirm() this.props.confirmationDialogParams.shouldShowConfirm()
) { ) {
// If item needs a confirmation dialog to turn ON, render it // If item needs a confirmation dialog to turn ON, render it
window.confirmationDialog({ const closeConfirmModal = () => {
resolve: () => { this.props.updateConfirmModal(null);
}
this.props.updateConfirmModal({
onClickOk: () => {
stateManager(event); stateManager(event);
closeConfirmModal();
},
onClickClose: () => {
this.props.updateConfirmModal(null);
}, },
...this.props.confirmationDialogParams, ...this.props.confirmationDialogParams,
}); updateConfirmModal,
})
return; return;
} }

@ -1,6 +1,8 @@
// tslint:disable: no-backbone-get-set-outside-model // tslint:disable: no-backbone-get-set-outside-model
import React from 'react'; import React from 'react';
import { useDispatch } from "react-redux";
import { updateConfirmModal } from '../../../state/ducks/modalDialog';
import classNames from 'classnames'; import classNames from 'classnames';
@ -12,7 +14,7 @@ import { AttachmentUtil, GoogleChrome } from '../../../util';
import { ConversationHeaderWithDetails } from '../../conversation/ConversationHeader'; import { ConversationHeaderWithDetails } from '../../conversation/ConversationHeader';
import { SessionRightPanelWithDetails } from './SessionRightPanel'; import { SessionRightPanelWithDetails } from './SessionRightPanel';
import { SessionTheme } from '../../../state/ducks/SessionTheme'; import { SessionTheme } from '../../../state/ducks/SessionTheme';
import { DefaultTheme } from 'styled-components'; import { DefaultTheme, useTheme } from 'styled-components';
import { SessionMessagesList } from './SessionMessagesList'; import { SessionMessagesList } from './SessionMessagesList';
import { LightboxGallery, MediaItemType } from '../../LightboxGallery'; import { LightboxGallery, MediaItemType } from '../../LightboxGallery';
import { Message } from '../../conversation/media-gallery/types/Message'; import { Message } from '../../conversation/media-gallery/types/Message';
@ -31,6 +33,9 @@ import autoBind from 'auto-bind';
import { getDecryptedMediaUrl } from '../../../session/crypto/DecryptedAttachmentsManager'; import { getDecryptedMediaUrl } from '../../../session/crypto/DecryptedAttachmentsManager';
import { deleteOpenGroupMessages } from '../../../interactions/conversation'; import { deleteOpenGroupMessages } from '../../../interactions/conversation';
import { ConversationTypeEnum } from '../../../models/conversation'; import { ConversationTypeEnum } from '../../../models/conversation';
import { SessionButtonColor } from '../SessionButton';
interface State { interface State {
// Message sending progress // Message sending progress
@ -359,12 +364,10 @@ export class SessionConversation extends React.Component<Props, State> {
onSetDisappearingMessages: conversation.updateExpirationTimer, onSetDisappearingMessages: conversation.updateExpirationTimer,
onDeleteMessages: conversation.deleteMessages, onDeleteMessages: conversation.deleteMessages,
onDeleteSelectedMessages: this.deleteSelectedMessages, onDeleteSelectedMessages: this.deleteSelectedMessages,
onChangeNickname: conversation.changeNickname,
onClearNickname: conversation.clearNickname, onClearNickname: conversation.clearNickname,
onCloseOverlay: () => { onCloseOverlay: () => {
this.setState({ selectedMessages: [] }); this.setState({ selectedMessages: [] });
}, },
onDeleteContact: conversation.deleteContact,
onGoBack: () => { onGoBack: () => {
this.setState({ this.setState({
@ -451,8 +454,8 @@ export class SessionConversation extends React.Component<Props, State> {
const isAdmin = conversation.isMediumGroup() const isAdmin = conversation.isMediumGroup()
? true ? true
: conversation.isPublic() : conversation.isPublic()
? conversation.isAdmin(ourPrimary) ? conversation.isAdmin(ourPrimary)
: false; : false;
return { return {
id: conversation.id, id: conversation.id,
@ -507,7 +510,6 @@ export class SessionConversation extends React.Component<Props, State> {
onInviteContacts: () => { onInviteContacts: () => {
window.Whisper.events.trigger('inviteContacts', conversation); window.Whisper.events.trigger('inviteContacts', conversation);
}, },
onDeleteContact: conversation.deleteContact,
onLeaveGroup: () => { onLeaveGroup: () => {
window.Whisper.events.trigger('leaveClosedGroup', conversation); window.Whisper.events.trigger('leaveClosedGroup', conversation);
}, },
@ -661,14 +663,22 @@ export class SessionConversation extends React.Component<Props, State> {
} }
const okText = window.i18n(isServerDeletable ? 'deleteForEveryone' : 'delete'); const okText = window.i18n(isServerDeletable ? 'deleteForEveryone' : 'delete');
if (askUserForConfirmation) { if (askUserForConfirmation) {
window.confirmationDialog({ const onClickClose = () => {
this.props.actions.updateConfirmModal(null);
}
this.props.actions.updateConfirmModal({
title, title,
message: warningMessage, message: warningMessage,
okText, okText,
okTheme: 'danger', okTheme: SessionButtonColor.Danger,
resolve: doDelete, onClickOk: doDelete,
}); onClickClose,
closeAfterClick: true
})
} else { } else {
void doDelete(); void doDelete();
} }
@ -684,7 +694,7 @@ export class SessionConversation extends React.Component<Props, State> {
public selectMessage(messageId: string) { public selectMessage(messageId: string) {
const selectedMessages = this.state.selectedMessages.includes(messageId) const selectedMessages = this.state.selectedMessages.includes(messageId)
? // Add to array if not selected. Else remove. ? // Add to array if not selected. Else remove.
this.state.selectedMessages.filter(id => id !== messageId) this.state.selectedMessages.filter(id => id !== messageId)
: [...this.state.selectedMessages, messageId]; : [...this.state.selectedMessages, messageId];
this.setState({ selectedMessages }); this.setState({ selectedMessages });

@ -29,6 +29,7 @@ export type PropsConversationHeaderMenu = {
timerOptions: Array<TimerOption>; timerOptions: Array<TimerOption>;
isPrivate: boolean; isPrivate: boolean;
isBlocked: boolean; isBlocked: boolean;
theme: any;
hasNickname?: boolean; hasNickname?: boolean;
onDeleteMessages?: () => void; onDeleteMessages?: () => void;
@ -62,6 +63,7 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
isPrivate, isPrivate,
left, left,
hasNickname, hasNickname,
theme,
onClearNickname, onClearNickname,
onChangeNickname, onChangeNickname,
@ -104,11 +106,11 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
{getMarkAllReadMenuItem(onMarkAllRead, window.i18n)} {getMarkAllReadMenuItem(onMarkAllRead, window.i18n)}
{getChangeNicknameMenuItem(isMe, onChangeNickname, isGroup, window.i18n, id, setModal)} {getChangeNicknameMenuItem(isMe, onChangeNickname, isGroup, window.i18n, id, setModal)}
{getClearNicknameMenuItem(isMe, hasNickname, onClearNickname, isGroup, window.i18n)} {getClearNicknameMenuItem(isMe, hasNickname, onClearNickname, isGroup, window.i18n)}
{getDeleteMessagesMenuItem(isPublic, onDeleteMessages, window.i18n)} {getDeleteMessagesMenuItem(isPublic, onDeleteMessages, window.i18n, id)}
{getAddModeratorsMenuItem(isAdmin, isKickedFromGroup, onAddModerators, window.i18n)} {getAddModeratorsMenuItem(isAdmin, isKickedFromGroup, onAddModerators, window.i18n)}
{getRemoveModeratorsMenuItem(isAdmin, isKickedFromGroup, onRemoveModerators, window.i18n)} {getRemoveModeratorsMenuItem(isAdmin, isKickedFromGroup, onRemoveModerators, window.i18n)}
{getUpdateGroupNameMenuItem(isAdmin, isKickedFromGroup, left, onUpdateGroupName, window.i18n)} {getUpdateGroupNameMenuItem(isAdmin, isKickedFromGroup, left, onUpdateGroupName, window.i18n)}
{getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, onLeaveGroup, window.i18n)} {getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, onLeaveGroup, window.i18n, id, setModal, theme)}
{/* TODO: add delete group */} {/* TODO: add delete group */}
{getInviteContactMenuItem(isGroup, isPublic, onInviteContacts, window.i18n)} {getInviteContactMenuItem(isGroup, isPublic, onInviteContacts, window.i18n)}
{getDeleteContactMenuItem( {getDeleteContactMenuItem(
@ -118,7 +120,8 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
left, left,
isKickedFromGroup, isKickedFromGroup,
onDeleteContact, onDeleteContact,
window.i18n window.i18n,
id
)} )}
</Menu> </Menu>
</> </>

@ -24,6 +24,7 @@ export type PropsContextConversationItem = {
hasNickname?: boolean; hasNickname?: boolean;
isKickedFromGroup?: boolean; isKickedFromGroup?: boolean;
left?: boolean; left?: boolean;
theme?: any
onDeleteMessages?: () => void; onDeleteMessages?: () => void;
onDeleteContact?: () => void; onDeleteContact?: () => void;
@ -58,6 +59,7 @@ export const ConversationListItemContextMenu = (props: PropsContextConversationI
onInviteContacts, onInviteContacts,
onLeaveGroup, onLeaveGroup,
onChangeNickname, onChangeNickname,
theme
} = props; } = props;
const isGroup = type === 'group'; const isGroup = type === 'group';
@ -82,7 +84,7 @@ export const ConversationListItemContextMenu = (props: PropsContextConversationI
{getChangeNicknameMenuItem(isMe, onChangeNickname, isGroup, window.i18n, id, setModal)} {getChangeNicknameMenuItem(isMe, onChangeNickname, isGroup, window.i18n, id, setModal)}
{getClearNicknameMenuItem(isMe, hasNickname, onClearNickname, isGroup, window.i18n)} {getClearNicknameMenuItem(isMe, hasNickname, onClearNickname, isGroup, window.i18n)}
{getDeleteMessagesMenuItem(isPublic, onDeleteMessages, window.i18n)} {getDeleteMessagesMenuItem(isPublic, onDeleteMessages, window.i18n, id)}
{getInviteContactMenuItem(isGroup, isPublic, onInviteContacts, window.i18n)} {getInviteContactMenuItem(isGroup, isPublic, onInviteContacts, window.i18n)}
{getDeleteContactMenuItem( {getDeleteContactMenuItem(
isMe, isMe,
@ -91,9 +93,10 @@ export const ConversationListItemContextMenu = (props: PropsContextConversationI
left, left,
isKickedFromGroup, isKickedFromGroup,
onDeleteContact, onDeleteContact,
window.i18n window.i18n,
id
)} )}
{getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, onLeaveGroup, window.i18n)} {getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, onLeaveGroup, window.i18n, id, setModal, theme)}
</Menu> </Menu>
</> </>
); );

@ -3,6 +3,13 @@ import { LocalizerType } from '../../../types/Util';
import { TimerOption } from '../../conversation/ConversationHeader'; import { TimerOption } from '../../conversation/ConversationHeader';
import { Item, Submenu } from 'react-contexify'; import { Item, Submenu } from 'react-contexify';
import { SessionNicknameDialog } from '../SessionNicknameDialog'; import { SessionNicknameDialog } from '../SessionNicknameDialog';
import { useDispatch, useSelector } from 'react-redux';
import { updateConfirmModal } from '../../../state/ducks/modalDialog';
import { ConversationController } from '../../../session/conversations';
import { useTheme } from 'styled-components';
import { UserUtils } from '../../../session/utils';
import { AdminLeaveClosedGroupDialog } from '../../conversation/AdminLeaveClosedGroupDialog';
import { getTheme } from '../../../state/selectors/theme';
function showTimerOptions( function showTimerOptions(
isPublic: boolean, isPublic: boolean,
@ -95,7 +102,8 @@ export function getDeleteContactMenuItem(
isLeft: boolean | undefined, isLeft: boolean | undefined,
isKickedFromGroup: boolean | undefined, isKickedFromGroup: boolean | undefined,
action: any, action: any,
i18n: LocalizerType i18n: LocalizerType,
id: string
): JSX.Element | null { ): JSX.Element | null {
if ( if (
showDeleteContact( showDeleteContact(
@ -106,10 +114,31 @@ export function getDeleteContactMenuItem(
Boolean(isKickedFromGroup) Boolean(isKickedFromGroup)
) )
) { ) {
let menuItemText: string;
if (isPublic) { if (isPublic) {
return <Item onClick={action}>{i18n('leaveGroup')}</Item>; menuItemText = i18n('leaveGroup');
} else {
menuItemText = i18n('delete');
}
const dispatch = useDispatch();
const onClickClose = () => {
dispatch(updateConfirmModal(null));
} }
return <Item onClick={action}>{i18n('delete')}</Item>;
const showConfirmationModal = () => {
dispatch(updateConfirmModal({
title: menuItemText,
message: isGroup ? i18n('leaveGroupConfirmation'): i18n('deleteContactConfirmation'),
onClickClose,
onClickOk: () => {
void ConversationController.getInstance().deleteContact(id);
onClickClose();
}
}))
}
return <Item onClick={showConfirmationModal}>{menuItemText}</Item>;
} }
return null; return null;
} }
@ -120,12 +149,57 @@ export function getLeaveGroupMenuItem(
isGroup: boolean | undefined, isGroup: boolean | undefined,
isPublic: boolean | undefined, isPublic: boolean | undefined,
action: any, action: any,
i18n: LocalizerType i18n: LocalizerType,
id: string,
setModal: any,
theme: any
): JSX.Element | null { ): JSX.Element | null {
if ( if (
showLeaveGroup(Boolean(isKickedFromGroup), Boolean(left), Boolean(isGroup), Boolean(isPublic)) showLeaveGroup(Boolean(isKickedFromGroup), Boolean(left), Boolean(isGroup), Boolean(isPublic))
) { ) {
return <Item onClick={action}>{i18n('leaveGroup')}</Item>; const dispatch = useDispatch();
const conversation = ConversationController.getInstance().get(id);
const onClickClose = () => {
dispatch(updateConfirmModal(null));
}
const openConfirmationModal = () => {
if (!conversation.isGroup()) {
throw new Error('showLeaveGroupDialog() called with a non group convo.');
}
const title = window.i18n('leaveGroup');
const message = window.i18n('leaveGroupConfirmation');
const ourPK = UserUtils.getOurPubKeyStrFromCache();
const isAdmin = (conversation.get('groupAdmins') || []).includes(ourPK);
const isClosedGroup = conversation.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)) {
dispatch(updateConfirmModal({
title,
message,
onClickOk: () => {
conversation.leaveClosedGroup();
onClickClose();
},
onClickClose
}));
} else {
setModal(
<AdminLeaveClosedGroupDialog
groupName={conversation.getName()}
onSubmit={conversation.leaveClosedGroup}
onClose={() => {setModal(null)}}
theme={theme}
></AdminLeaveClosedGroupDialog>
)
}
}
return <Item onClick={openConfirmationModal}>{i18n('leaveGroup')}</Item>;
} }
return null; return null;
} }
@ -283,19 +357,13 @@ export function getChangeNicknameMenuItem(
setModal(null); setModal(null);
} }
// const onClickOk = () => {
// console.log("@@ onclickok clicked");
// }
const onClickCustom = () => { const onClickCustom = () => {
setModal(<SessionNicknameDialog onClickClose={clearModal} conversationId={conversationId}></SessionNicknameDialog>); setModal(<SessionNicknameDialog onClickClose={clearModal} conversationId={conversationId}></SessionNicknameDialog>);
// setModal(null);
} }
return ( return (
<> <>
<Item onClick={onClickCustom}>{i18n('changeNickname')}</Item> <Item onClick={onClickCustom}>{i18n('changeNickname')}</Item>
{/* <Item onClick={action}>{i18n('changeNickname')}</Item> */}
</> </>
); );
} }
@ -305,10 +373,33 @@ export function getChangeNicknameMenuItem(
export function getDeleteMessagesMenuItem( export function getDeleteMessagesMenuItem(
isPublic: boolean | undefined, isPublic: boolean | undefined,
action: any, action: any,
i18n: LocalizerType i18n: LocalizerType,
id: string
): JSX.Element | null { ): JSX.Element | null {
if (showDeleteMessages(Boolean(isPublic))) { if (showDeleteMessages(Boolean(isPublic))) {
return <Item onClick={action}>{i18n('deleteMessages')}</Item>;
const dispatch = useDispatch();
const conversation = ConversationController.getInstance().get(id);
const onClickClose = () => {
dispatch(updateConfirmModal(null));
}
const onClickOk = () => {
conversation.destroyMessages();
onClickClose();
}
const openConfirmationModal = () => {
dispatch(updateConfirmModal({
title: window.i18n('deleteMessages'),
message: window.i18n('deleteConversationConfirmation'),
onClickOk,
onClickClose,
}))
}
return <Item onClick={openConfirmationModal}>{i18n('deleteMessages')}</Item>;
} }
return null; return null;
} }

@ -7,6 +7,7 @@ import { SessionToggle } from '../SessionToggle';
import { SessionButton } from '../SessionButton'; import { SessionButton } from '../SessionButton';
import { SessionSettingType } from './SessionSettings'; import { SessionSettingType } from './SessionSettings';
import { SessionRadioGroup } from '../SessionRadioGroup'; import { SessionRadioGroup } from '../SessionRadioGroup';
import { SessionConfirmDialogProps } from '../SessionConfirm';
interface Props { interface Props {
title?: string; title?: string;
@ -17,7 +18,10 @@ interface Props {
onClick?: any; onClick?: any;
onSliderChange?: any; onSliderChange?: any;
content: any; content: any;
confirmationDialogParams?: any; confirmationDialogParams?: SessionConfirmDialogProps;
// for updating modal in redux
updateConfirmModal?: any
} }
interface State { interface State {
@ -61,6 +65,7 @@ export class SessionSettingListItem extends React.Component<Props, State> {
active={Boolean(value)} active={Boolean(value)}
onClick={this.handleClick} onClick={this.handleClick}
confirmationDialogParams={this.props.confirmationDialogParams} confirmationDialogParams={this.props.confirmationDialogParams}
updateConfirmModal={this.props.updateConfirmModal}
/> />
</div> </div>
)} )}

@ -12,6 +12,8 @@ import { getConversationLookup, getConversations } from '../../../state/selector
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getPasswordHash } from '../../../../ts/data/data'; import { getPasswordHash } from '../../../../ts/data/data';
import { PasswordAction, SessionPasswordModal } from '../SessionPasswordModal'; import { PasswordAction, SessionPasswordModal } from '../SessionPasswordModal';
import { SessionConfirmDialogProps } from '../SessionConfirm';
import { mapDispatchToProps } from '../../../state/actions';
export enum SessionSettingCategory { export enum SessionSettingCategory {
Appearance = 'appearance', Appearance = 'appearance',
@ -34,6 +36,7 @@ export interface SettingsViewProps {
// pass the conversation as props, so our render is called everytime they change. // pass the conversation as props, so our render is called everytime they change.
// we have to do this to make the list refresh on unblock() // we have to do this to make the list refresh on unblock()
conversations?: ConversationLookupType; conversations?: ConversationLookupType;
updateConfirmModal?: any;
} }
interface State { interface State {
@ -44,6 +47,10 @@ interface State {
modal: JSX.Element | null; modal: JSX.Element | null;
} }
interface ConfirmationDialogParams extends SessionConfirmDialogProps {
shouldShowConfirm: () => boolean | undefined;
}
interface LocalSettingType { interface LocalSettingType {
category: SessionSettingCategory; category: SessionSettingCategory;
description: string | undefined; description: string | undefined;
@ -56,7 +63,7 @@ interface LocalSettingType {
type: SessionSettingType | undefined; type: SessionSettingType | undefined;
setFn: any; setFn: any;
onClick: any; onClick: any;
confirmationDialogParams: any | undefined; confirmationDialogParams: ConfirmationDialogParams | undefined;
} }
class SettingsViewInner extends React.Component<SettingsViewProps, State> { class SettingsViewInner extends React.Component<SettingsViewProps, State> {
@ -147,6 +154,7 @@ class SettingsViewInner extends React.Component<SettingsViewProps, State> {
onSliderChange={sliderFn} onSliderChange={sliderFn}
content={content} content={content}
confirmationDialogParams={setting.confirmationDialogParams} confirmationDialogParams={setting.confirmationDialogParams}
updateConfirmModal={this.props.updateConfirmModal}
/> />
)} )}
</div> </div>
@ -345,7 +353,7 @@ class SettingsViewInner extends React.Component<SettingsViewProps, State> {
shouldShowConfirm: () => !window.getSettingValue('link-preview-setting'), shouldShowConfirm: () => !window.getSettingValue('link-preview-setting'),
title: window.i18n('linkPreviewsTitle'), title: window.i18n('linkPreviewsTitle'),
message: window.i18n('linkPreviewsConfirmMessage'), message: window.i18n('linkPreviewsConfirmMessage'),
okTheme: 'danger', okTheme: SessionButtonColor.Danger,
}, },
}, },
{ {
@ -614,11 +622,13 @@ class SettingsViewInner extends React.Component<SettingsViewProps, State> {
} }
} }
const mapStateToProps = (state: StateType) => { const mapStateToProps = (state: StateType) => {
return { return {
conversations: getConversationLookup(state), conversations: getConversationLookup(state),
}; };
}; };
const smart = connect(mapStateToProps);
const smart = connect(mapStateToProps, mapDispatchToProps);
export const SmartSettingsView = smart(SettingsViewInner); export const SmartSettingsView = smart(SettingsViewInner);

@ -10,6 +10,9 @@ import { PubKey } from '../session/types';
import { ToastUtils } from '../session/utils'; import { ToastUtils } from '../session/utils';
import { openConversationExternal } from '../state/ducks/conversations'; import { openConversationExternal } from '../state/ducks/conversations';
import { useDispatch } from 'react-redux';
import { updateConfirmModal } from '../state/ducks/modalDialog';
export function banUser(userToBan: string, conversation?: ConversationModel) { export function banUser(userToBan: string, conversation?: ConversationModel) {
let pubKeyToBan: PubKey; let pubKeyToBan: PubKey;
try { try {
@ -19,37 +22,46 @@ export function banUser(userToBan: string, conversation?: ConversationModel) {
ToastUtils.pushUserBanFailure(); ToastUtils.pushUserBanFailure();
return; return;
} }
window.confirmationDialog({
title: window.i18n('banUser'), const dispatch = useDispatch();
message: window.i18n('banUserConfirm'), const onClickClose = () => {
resolve: async () => { dispatch(updateConfirmModal(null))
if (!conversation) { };
window.log.info('cannot ban user, the corresponding conversation was not found.');
return; // const confirmationModalProps = {
} // title: window.i18n('banUser'),
let success = false; // message: window.i18n('banUserConfirm'),
if (isOpenGroupV2(conversation.id)) { // onClickClose,
const roomInfos = await getV2OpenGroupRoom(conversation.id); // // onClickOk: async () => {
if (!roomInfos) { // // if (!conversation) {
window.log.warn('banUser room not found'); // // window.log.info('cannot ban user, the corresponding conversation was not found.');
} else { // // return;
success = await ApiV2.banUser(pubKeyToBan, _.pick(roomInfos, 'serverUrl', 'roomId')); // // }
} // // let success = false;
} else { // // if (isOpenGroupV2(conversation.id)) {
const channelAPI = await conversation.getPublicSendData(); // // const roomInfos = await getV2OpenGroupRoom(conversation.id);
if (!channelAPI) { // // if (!roomInfos) {
window.log.info('cannot ban user, the corresponding channelAPI was not found.'); // // window.log.warn('banUser room not found');
return; // // } else {
} // // success = await ApiV2.banUser(pubKeyToBan, _.pick(roomInfos, 'serverUrl', 'roomId'));
success = await channelAPI.banUser(userToBan); // // }
} // // } else {
if (success) { // // const channelAPI = await conversation.getPublicSendData();
ToastUtils.pushUserBanSuccess(); // // if (!channelAPI) {
} else { // // window.log.info('cannot ban user, the corresponding channelAPI was not found.');
ToastUtils.pushUserBanFailure(); // // return;
} // // }
}, // // success = await channelAPI.banUser(userToBan);
}); // // }
// // if (success) {
// // ToastUtils.pushUserBanSuccess();
// // } else {
// // ToastUtils.pushUserBanFailure();
// // }
// // },
// }
// dispatch(updateConfirmModal(confirmationModalProps));
} }
/** /**
@ -70,31 +82,65 @@ export function unbanUser(userToUnBan: string, conversation?: ConversationModel)
ToastUtils.pushUserBanFailure(); ToastUtils.pushUserBanFailure();
return; return;
} }
window.confirmationDialog({
title: window.i18n('unbanUser'),
message: window.i18n('unbanUserConfirm'), const dispatch = useDispatch();
resolve: async () => { const onClickClose = () => dispatch(updateConfirmModal(null));
if (!conversation) {
// double check here. the convo might have been removed since the dialog was opened const onClickOk = async () => {
window.log.info('cannot unban user, the corresponding conversation was not found.'); if (!conversation) {
return; // double check here. the convo might have been removed since the dialog was opened
} window.log.info('cannot unban user, the corresponding conversation was not found.');
let success = false; return;
if (isOpenGroupV2(conversation.id)) { }
const roomInfos = await getV2OpenGroupRoom(conversation.id); let success = false;
if (!roomInfos) { if (isOpenGroupV2(conversation.id)) {
window.log.warn('unbanUser room not found'); const roomInfos = await getV2OpenGroupRoom(conversation.id);
} else { if (!roomInfos) {
success = await ApiV2.unbanUser(pubKeyToUnban, _.pick(roomInfos, 'serverUrl', 'roomId')); window.log.warn('unbanUser room not found');
}
}
if (success) {
ToastUtils.pushUserUnbanSuccess();
} else { } else {
ToastUtils.pushUserUnbanFailure(); success = await ApiV2.unbanUser(pubKeyToUnban, _.pick(roomInfos, 'serverUrl', 'roomId'));
} }
}, }
}); if (success) {
ToastUtils.pushUserUnbanSuccess();
} else {
ToastUtils.pushUserUnbanFailure();
}
}
dispatch(updateConfirmModal({
title: window.i18n('unbanUser'),
message: window.i18n('unbanUserConfirm'),
onClickOk,
onClickClose,
}));
// window.confirmationDialog({
// title: window.i18n('unbanUser'),
// message: window.i18n('unbanUserConfirm'),
// resolve: async () => {
// if (!conversation) {
// // double check here. the convo might have been removed since the dialog was opened
// window.log.info('cannot unban user, the corresponding conversation was not found.');
// return;
// }
// let success = false;
// if (isOpenGroupV2(conversation.id)) {
// const roomInfos = await getV2OpenGroupRoom(conversation.id);
// if (!roomInfos) {
// window.log.warn('unbanUser room not found');
// } else {
// success = await ApiV2.unbanUser(pubKeyToUnban, _.pick(roomInfos, 'serverUrl', 'roomId'));
// }
// }
// if (success) {
// ToastUtils.pushUserUnbanSuccess();
// } else {
// ToastUtils.pushUserUnbanFailure();
// }
// },
// });
} }
export function copyBodyToClipboard(body?: string) { export function copyBodyToClipboard(body?: string) {
@ -242,11 +288,27 @@ async function acceptOpenGroupInvitationV1(serverAddress: string) {
} }
const acceptOpenGroupInvitationV2 = (completeUrl: string, roomName?: string) => { const acceptOpenGroupInvitationV2 = (completeUrl: string, roomName?: string) => {
window.confirmationDialog({
const dispatch = useDispatch();
const onClickClose = () => {
dispatch(updateConfirmModal(null))
};
dispatch(updateConfirmModal({
title: window.i18n('joinOpenGroupAfterInvitationConfirmationTitle', roomName), title: window.i18n('joinOpenGroupAfterInvitationConfirmationTitle', roomName),
message: window.i18n('joinOpenGroupAfterInvitationConfirmationDesc', roomName), message: window.i18n('joinOpenGroupAfterInvitationConfirmationDesc', roomName),
resolve: () => joinOpenGroupV2WithUIEvents(completeUrl, true), onClickOk: () => joinOpenGroupV2WithUIEvents(completeUrl, true),
}); onClickClose
}))
// 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
}; };

@ -42,6 +42,8 @@ import { ConversationInteraction } from '../interactions';
import { OpenGroupVisibleMessage } from '../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; import { OpenGroupVisibleMessage } from '../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage';
import { OpenGroupRequestCommonType } from '../opengroup/opengroupV2/ApiUtil'; import { OpenGroupRequestCommonType } from '../opengroup/opengroupV2/ApiUtil';
import { getOpenGroupV2FromConversationId } from '../opengroup/utils/OpenGroupUtils'; import { getOpenGroupV2FromConversationId } from '../opengroup/utils/OpenGroupUtils';
import { useDispatch } from 'react-redux';
import { updateConfirmModal } from '../state/ducks/modalDialog';
export enum ConversationTypeEnum { export enum ConversationTypeEnum {
GROUP = 'group', GROUP = 'group',
@ -421,7 +423,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
onBlockContact: this.block, onBlockContact: this.block,
onUnblockContact: this.unblock, onUnblockContact: this.unblock,
onCopyPublicKey: this.copyPublicKey, onCopyPublicKey: this.copyPublicKey,
onDeleteContact: this.deleteContact,
onClearNickname: this.clearNickname, onClearNickname: this.clearNickname,
onDeleteMessages: this.deleteMessages, onDeleteMessages: this.deleteMessages,
onLeaveGroup: () => { onLeaveGroup: () => {
@ -1309,23 +1310,23 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
void this.setNickname(''); void this.setNickname('');
}; };
public deleteContact() { // public deleteContact() {
let title = window.i18n('delete'); // let title = window.i18n('delete');
let message = window.i18n('deleteContactConfirmation'); // let message = window.i18n('deleteContactConfirmation');
if (this.isGroup()) { // if (this.isGroup()) {
title = window.i18n('leaveGroup'); // title = window.i18n('leaveGroup');
message = window.i18n('leaveGroupConfirmation'); // message = window.i18n('leaveGroupConfirmation');
} // }
window.confirmationDialog({ // window.confirmationDialog({
title, // title,
message, // message,
resolve: () => { // resolve: () => {
void ConversationController.getInstance().deleteContact(this.id); // void ConversationController.getInstance().deleteContact(this.id);
}, // },
}); // });
} // }
public async removeMessage(messageId: any) { public async removeMessage(messageId: any) {
await dataRemoveMessage(messageId); await dataRemoveMessage(messageId);
@ -1351,7 +1352,14 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
}; };
} }
window.confirmationDialog(params); // window.confirmationDialog(params);
const dispatch = useDispatch()
dispatch(updateConfirmModal({
title: window.i18n('deleteMessages'),
message: window.i18n('deleteConversationConfirmation'),
onClickOk: () => this.destroyMessages(),
}))
} }
public async destroyMessages() { public async destroyMessages() {

@ -1,4 +1,3 @@
import React from 'react';
import { SignalService } from '../protobuf'; import { SignalService } from '../protobuf';
import { removeFromCache } from './cache'; import { removeFromCache } from './cache';
import { EnvelopePlus } from './types'; import { EnvelopePlus } from './types';
@ -36,7 +35,10 @@ import { queueAllCachedFromSource } from './receiver';
import { actions as conversationActions } from '../state/ducks/conversations'; import { actions as conversationActions } from '../state/ducks/conversations';
import { SwarmPolling } from '../session/snode_api/swarmPolling'; import { SwarmPolling } from '../session/snode_api/swarmPolling';
import { MessageModel } from '../models/message'; import { MessageModel } from '../models/message';
import { SessionConfirm } from '../components/session/SessionConfirm';
import { useSelector, useDispatch } from "react-redux";
import { updateConfirmModal } from "../state/ducks/modalDialog";
export const distributingClosedGroupEncryptionKeyPairs = new Map<string, ECKeyPair>(); export const distributingClosedGroupEncryptionKeyPairs = new Map<string, ECKeyPair>();
@ -838,7 +840,7 @@ async function handleClosedGroupEncryptionKeyPairRequest(
return removeFromCache(envelope); return removeFromCache(envelope);
} }
export async function createClosedGroup(groupName: string, members: Array<string>, setModal: any) { export async function createClosedGroup(groupName: string, members: Array<string>) {
const setOfMembers = new Set(members); const setOfMembers = new Set(members);
const ourNumber = UserUtils.getOurPubKeyFromCache(); const ourNumber = UserUtils.getOurPubKeyFromCache();
@ -894,7 +896,6 @@ export async function createClosedGroup(groupName: string, members: Array<string
admins, admins,
encryptionKeyPair, encryptionKeyPair,
dbMessage, dbMessage,
setModal
); );
if (allInvitesSent) { if (allInvitesSent) {
@ -929,7 +930,6 @@ async function sendToGroupMembers(
admins: Array<string>, admins: Array<string>,
encryptionKeyPair: ECKeyPair, encryptionKeyPair: ECKeyPair,
dbMessage: MessageModel, dbMessage: MessageModel,
setModal: any,
isRetry: boolean = false, isRetry: boolean = false,
): Promise<any> { ): Promise<any> {
const promises = createInvitePromises( const promises = createInvitePromises(
@ -945,35 +945,34 @@ async function sendToGroupMembers(
const inviteResults = await Promise.all(promises); const inviteResults = await Promise.all(promises);
const allInvitesSent = _.every(inviteResults, Boolean); const allInvitesSent = _.every(inviteResults, Boolean);
// const dispatch = useDispatch();
// window.inboxStore?.dispatch(updateConfirmModal({
// title: 'hi'
// }))
console.log('@@@@', inviteResults);
if (allInvitesSent) { if (allInvitesSent) {
// if (true) {
if (isRetry) { if (isRetry) {
const invitesTitle = const invitesTitle =
inviteResults.length > 1 inviteResults.length > 1
? window.i18n('closedGroupInviteSuccessTitlePlural') ? window.i18n('closedGroupInviteSuccessTitlePlural')
: window.i18n('closedGroupInviteSuccessTitle'); : window.i18n('closedGroupInviteSuccessTitle');
// setModal(<SessionConfirm message={'hi'} title={invitesTitle} />) window.inboxStore?.dispatch(updateConfirmModal({
title: invitesTitle,
setModal( message: window.i18n('closedGroupInviteSuccessMessage'),
<SessionConfirm hideCancel: true
title={title} }));
message={message}
onClickOk={deleteAccount}
okTheme={SessionButtonColor.Danger}
onClickClose={clearModal}
/>)
)
// window.confirmationDialog({
// title: invitesTitle,
// message: window.i18n('closedGroupInviteSuccessMessage'),
// });
} }
return allInvitesSent; return allInvitesSent;
} else { } else {
// Confirmation dialog that recursively calls sendToGroupMembers on resolve // Confirmation dialog that recursively calls sendToGroupMembers on resolve
window.confirmationDialog({
window.inboxStore?.dispatch(updateConfirmModal({
title: title:
inviteResults.length > 1 inviteResults.length > 1
? window.i18n('closedGroupInviteFailTitlePlural') ? window.i18n('closedGroupInviteFailTitlePlural')
@ -983,7 +982,7 @@ async function sendToGroupMembers(
? window.i18n('closedGroupInviteFailMessagePlural') ? window.i18n('closedGroupInviteFailMessagePlural')
: window.i18n('closedGroupInviteFailMessage'), : window.i18n('closedGroupInviteFailMessage'),
okText: window.i18n('closedGroupInviteOkText'), okText: window.i18n('closedGroupInviteOkText'),
resolve: async () => { onClickOk: async () => {
const membersToResend: Array<string> = new Array<string>(); const membersToResend: Array<string> = new Array<string>();
inviteResults.forEach((result, index) => { inviteResults.forEach((result, index) => {
const member = listOfMembers[index]; const member = listOfMembers[index];
@ -1001,12 +1000,45 @@ async function sendToGroupMembers(
admins, admins,
encryptionKeyPair, encryptionKeyPair,
dbMessage, dbMessage,
setModal,
isRetrySend isRetrySend
); );
} }
}, },
}); }));
// window.confirmationDialog({
// title:
// inviteResults.length > 1
// ? window.i18n('closedGroupInviteFailTitlePlural')
// : window.i18n('closedGroupInviteFailTitle'),
// message:
// inviteResults.length > 1
// ? window.i18n('closedGroupInviteFailMessagePlural')
// : window.i18n('closedGroupInviteFailMessage'),
// okText: window.i18n('closedGroupInviteOkText'),
// resolve: async () => {
// const membersToResend: Array<string> = new Array<string>();
// inviteResults.forEach((result, index) => {
// const member = listOfMembers[index];
// // group invite must always contain the admin member.
// if (result !== true || admins.includes(member)) {
// membersToResend.push(member);
// }
// });
// if (membersToResend.length > 0) {
// const isRetrySend = true;
// await sendToGroupMembers(
// membersToResend,
// groupPublicKey,
// groupName,
// admins,
// encryptionKeyPair,
// dbMessage,
// isRetrySend
// );
// }
// },
// });
} }
return allInvitesSent; return allInvitesSent;
} }

@ -5,6 +5,7 @@ import { actions as conversations } from './ducks/conversations';
import { actions as user } from './ducks/user'; import { actions as user } from './ducks/user';
import { actions as sections } from './ducks/section'; import { actions as sections } from './ducks/section';
import { actions as theme } from './ducks/theme'; import { actions as theme } from './ducks/theme';
import { actions as modalDialog } from './ducks/modalDialog';
export function mapDispatchToProps(dispatch: Dispatch): Object { export function mapDispatchToProps(dispatch: Dispatch): Object {
return { return {
@ -15,6 +16,7 @@ export function mapDispatchToProps(dispatch: Dispatch): Object {
...user, ...user,
...theme, ...theme,
...sections, ...sections,
...modalDialog
}, },
dispatch dispatch
), ),

@ -0,0 +1,21 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { SessionConfirmDialogProps } from '../../components/session/SessionConfirm';
export type ConfirmModalState = SessionConfirmDialogProps | null;
const initialState: ConfirmModalState = null as ConfirmModalState;
const confirmModalSlice = createSlice({
name: 'confirmModal',
initialState,
reducers: {
updateConfirmModal(state, action: PayloadAction<ConfirmModalState | null>) {
state = action.payload;
return action.payload;
}
}
})
export const { actions, reducer } = confirmModalSlice;
export const { updateConfirmModal } = actions;
export const confirmModalReducer = reducer;

@ -1,17 +1,10 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import _, { forEach } from 'underscore';
import { SnodePath, Snode } from '../../session/onions/index'; import { SnodePath, Snode } from '../../session/onions/index';
export type OnionState = { export type OnionState = {
// nodes: Array<OnionPathNodeType>;
// path: SnodePath;
snodePath: SnodePath; snodePath: SnodePath;
}; };
// const initialState: OnionState = {
// // nodes: new Array<OnionPathNodeType>(),
// nodes: new Array<Snode>(),
// };
const initialState = { const initialState = {
snodePath: { snodePath: {
@ -32,8 +25,6 @@ const onionSlice = createSlice({
let isEqual = JSON.stringify(state, null, 2) == JSON.stringify(newPayload, null, 2); let isEqual = JSON.stringify(state, null, 2) == JSON.stringify(newPayload, null, 2);
return isEqual ? state : newPayload; return isEqual ? state : newPayload;
return newPayload;
}, },
}, },
}); });

@ -6,8 +6,8 @@ import { reducer as user, UserStateType } from './ducks/user';
import { reducer as theme, ThemeStateType } from './ducks/theme'; import { reducer as theme, ThemeStateType } from './ducks/theme';
import { reducer as section, SectionStateType } from './ducks/section'; import { reducer as section, SectionStateType } from './ducks/section';
import { defaultRoomReducer as defaultRooms, DefaultRoomsState } from './ducks/defaultRooms'; import { defaultRoomReducer as defaultRooms, DefaultRoomsState } from './ducks/defaultRooms';
import { defaultOnionReducer as onionPaths, OnionState } from './ducks/onion'; import { defaultOnionReducer as onionPaths, OnionState } from './ducks/onion';
import { confirmModalReducer as confirmModal, ConfirmModalState } from "./ducks/modalDialog";
export type StateType = { export type StateType = {
search: SearchStateType; search: SearchStateType;
@ -19,6 +19,9 @@ export type StateType = {
defaultRooms: DefaultRoomsState; defaultRooms: DefaultRoomsState;
onionPaths: OnionState; onionPaths: OnionState;
confirmModal: ConfirmModalState
// modalState: ConfirmModalState
}; };
export const reducers = { export const reducers = {
@ -31,8 +34,8 @@ export const reducers = {
theme, theme,
section, section,
defaultRooms, defaultRooms,
onionPaths, onionPaths,
confirmModal
}; };
// Making this work would require that our reducer signature supported AnyAction, not // Making this work would require that our reducer signature supported AnyAction, not

@ -17,6 +17,7 @@ const mapStateToProps = (state: StateType) => {
theme: getTheme(state), theme: getTheme(state),
messages: getMessagesOfSelectedConversation(state), messages: getMessagesOfSelectedConversation(state),
ourNumber: getOurNumber(state), ourNumber: getOurNumber(state),
confirmModal: (state: StateType) => {state.confirmModal}
}; };
}; };

6
ts/window.d.ts vendored

@ -96,5 +96,11 @@ declare global {
darkTheme: DefaultTheme; darkTheme: DefaultTheme;
LokiPushNotificationServer: any; LokiPushNotificationServer: any;
LokiPushNotificationServerApi: any; LokiPushNotificationServerApi: any;
confirmationDialog: any;
} }
// window.confirmationDialog = () => {
// console.log("confirmation dialog stub called");
// }
} }

Loading…
Cancel
Save