WIP: Refactoring modals.

This commit is contained in:
Warrick Corfe-Tan 2021-06-08 14:04:09 +10:00
parent ddda525f63
commit 1bfdbc5c93
14 changed files with 47 additions and 321 deletions

View File

@ -129,7 +129,6 @@
<script type='text/javascript' src='js/views/react_wrapper_view.js'></script>
<script type='text/javascript' src='js/views/whisper_view.js'></script>
<script type='text/javascript' src='js/views/session_confirm_view.js'></script>
<script type='text/javascript' src='js/views/session_inbox_view.js'></script>
<script type='text/javascript' src='js/views/identicon_svg_view.js'></script>
@ -140,16 +139,12 @@
<!-- DIALOGS-->
<script type='text/javascript' src='js/views/update_group_dialog_view.js'></script>
<script type='text/javascript' src='js/views/edit_profile_dialog_view.js'></script>
<script type='text/javascript' src='js/views/session_change_nickname_dialog_view.js'></script>
<script type='text/javascript' src='js/views/onion_status_dialog_view.js'></script>
<script type='text/javascript' src='js/views/invite_contacts_dialog_view.js'></script>
<script type='text/javascript' src='js/views/admin_leave_closed_group_dialog_view.js'></script>
<script type='text/javascript' src='js/views/moderators_add_dialog_view.js'></script>
<script type='text/javascript' src='js/views/moderators_remove_dialog_view.js'></script>
<script type='text/javascript' src='js/views/user_details_dialog_view.js'></script>
<script type='text/javascript' src='js/views/password_dialog_view.js'></script>
<script type='text/javascript' src='js/views/session_id_reset_view.js'></script>
<!-- CRYPTO -->

View File

@ -363,157 +363,11 @@
window.addEventListener('focus', () => Whisper.Notifications.clear());
window.addEventListener('unload', () => Whisper.Notifications.fastClear());
window.confirmationDialog = params => {
const confirmDialog = new Whisper.SessionConfirmView({
el: $('body'),
title: params.title,
message: params.message,
messageSub: params.messageSub || undefined,
resolve: params.resolve || undefined,
reject: params.reject || undefined,
okText: params.okText || undefined,
okTheme: params.okTheme || undefined,
closeTheme: params.closeTheme || undefined,
cancelText: params.cancelText || undefined,
hideCancel: params.hideCancel || false,
sessionIcon: params.sessionIcon || undefined,
iconSize: params.iconSize || undefined,
});
confirmDialog.render();
};
window.showResetSessionIdDialog = () => {
appView.showResetSessionIdDialog();
};
window.showOnionStatusDialog = () => {
appView.showOnionStatusDialog();
};
window.commitProfileEdits = async (newName, avatar) => {
const ourNumber = window.storage.get('primaryDevicePubKey');
const conversation = await window
.getConversationController()
.getOrCreateAndWait(ourNumber, 'private');
const readFile = attachment =>
new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = e => {
const data = e.target.result;
resolve({
...attachment,
data,
size: data.byteLength,
});
};
fileReader.onerror = reject;
fileReader.onabort = reject;
fileReader.readAsArrayBuffer(attachment.file);
});
// const avatarPath = conversation.getAvatarPath();
// const profile = conversation.getLokiProfile();
// const displayName = profile && profile.displayName;
let newAvatarPath = '';
let url = null;
let profileKey = null;
if (avatar) {
const data = await readFile({ file: avatar });
// Ensure that this file is either small enough or is resized to meet our
// requirements for attachments
try {
const withBlob = await window.Signal.Util.AttachmentUtil.autoScale(
{
contentType: avatar.type,
file: new Blob([data.data], {
type: avatar.contentType,
}),
},
{
maxSide: 640,
maxSize: 1000 * 1024,
}
);
const dataResized = await window.Signal.Types.Attachment.arrayBufferFromFile(
withBlob.file
);
// For simplicity we use the same attachment pointer that would send to
// others, which means we need to wait for the database response.
// To avoid the wait, we create a temporary url for the local image
// and use it until we the the response from the server
const tempUrl = window.URL.createObjectURL(avatar);
conversation.setLokiProfile({ displayName: newName });
conversation.set('avatar', tempUrl);
// Encrypt with a new key every time
profileKey = libsignal.crypto.getRandomBytes(32);
const encryptedData = await textsecure.crypto.encryptProfile(dataResized, profileKey);
const avatarPointer = await libsession.Utils.AttachmentUtils.uploadAvatarV1({
...dataResized,
data: encryptedData,
size: encryptedData.byteLength,
});
({ url } = avatarPointer);
storage.put('profileKey', profileKey);
conversation.set('avatarPointer', url);
const upgraded = await Signal.Migrations.processNewAttachment({
isRaw: true,
data: data.data,
url,
});
newAvatarPath = upgraded.path;
// Replace our temporary image with the attachment pointer from the server:
conversation.set('avatar', null);
conversation.setLokiProfile({
displayName: newName,
avatar: newAvatarPath,
});
await conversation.commit();
window.libsession.Utils.UserUtils.setLastProfileUpdateTimestamp(Date.now());
await window.libsession.Utils.SyncUtils.forceSyncConfigurationNowIfNeeded(true);
} catch (error) {
window.log.error(
'showEditProfileDialog Error ensuring that image is properly sized:',
error && error.stack ? error.stack : error
);
}
} else {
// do not update the avatar if it did not change
conversation.setLokiProfile({
displayName: newName,
});
// might be good to not trigger a sync if the name did not change
await conversation.commit();
window.libsession.Utils.UserUtils.setLastProfileUpdateTimestamp(Date.now());
await window.libsession.Utils.SyncUtils.forceSyncConfigurationNowIfNeeded(true);
}
// inform all your registered public servers
// could put load on all the servers
// if they just keep changing their names without sending messages
// so we could disable this here
// or least it enable for the quickest response
window.lokiPublicChatAPI.setProfileName(newName);
if (avatar) {
window
.getConversationController()
.getConversations()
.filter(convo => convo.isPublic())
.forEach(convo => convo.trigger('ourAvatarChanged', { url, profileKey }));
}
};
// Set user's launch count.
const prevLaunchCount = window.getSettingValue('launch-count');
const launchCount = !prevLaunchCount ? 1 : prevLaunchCount + 1;
@ -641,12 +495,6 @@
});
Whisper.events.on('showPasswordDialog', async options => {
if (appView) {
appView.showPasswordDialog(options);
}
});
Whisper.events.on('password-updated', () => {
if (appView && appView.inboxView) {
appView.inboxView.trigger('password-updated');

View File

@ -16,7 +16,6 @@
this.applyRtl();
this.applyHideMenu();
this.showPasswordDialog = this.showPasswordDialog.bind(this);
},
events: {
openInbox: 'openInbox',
@ -124,12 +123,6 @@
const dialog = new Whisper.UserDetailsDialogView(options);
this.el.prepend(dialog.el);
},
showPasswordDialog(options) {
// eslint-disable-next-line no-param-reassign
options.theme = this.getThemeObject();
const dialog = new Whisper.PasswordDialogView(options);
this.el.prepend(dialog.el);
},
getThemeObject() {
const themeSettings = storage.get('theme-setting') || 'light';
const theme = themeSettings === 'light' ? window.lightTheme : window.darkTheme;

View File

@ -1,44 +0,0 @@
/* global Whisper */
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
Whisper.PasswordDialogView = Whisper.View.extend({
className: 'loki-dialog password-dialog modal',
initialize(options) {
this.close = this.close.bind(this);
this.onOk = this.onOk.bind(this);
this.props = options;
this.render();
},
render() {
this.dialogView = new Whisper.ReactWrapperView({
className: 'password-dialog-wrapper',
Component: window.Signal.Components.SessionPasswordModal,
props: {
onClose: this.close,
onOk: this.onOk,
...this.props,
},
});
this.$el.append(this.dialogView.el);
return this;
},
onOk(action) {
if (this.props.onSuccess) {
this.props.onSuccess(action);
}
},
close() {
this.remove();
},
});
})();

View File

@ -1,80 +0,0 @@
/* global Whisper */
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
Whisper.SessionConfirmView = Whisper.View.extend({
initialize(options) {
this.props = {
title: options.title,
message: options.message,
messageSub: options.messageSub,
onClickOk: this.ok.bind(this),
onClickClose: this.cancel.bind(this),
resolve: options.resolve,
reject: options.reject,
okText: options.okText,
cancelText: options.cancelText,
okTheme: options.okTheme,
closeTheme: options.closeTheme,
hideCancel: options.hideCancel,
sessionIcon: options.sessionIcon,
iconSize: options.iconSize,
};
},
registerEvents() {
this.unregisterEvents();
document.addEventListener('keyup', this.props.onClickClose, false);
},
unregisterEvents() {
document.removeEventListener('keyup', this.props.onClickClose, false);
if (this.confirmView && this.confirmView.el) {
window.ReactDOM.unmountComponentAtNode(this.confirmView.el);
}
this.$('.session-confirm-wrapper').remove();
},
render() {
this.$('.session-confirm-wrapper').remove();
this.registerEvents();
this.confirmView = new Whisper.ReactWrapperView({
className: 'loki-dialog modal session-confirm-wrapper',
Component: window.Signal.Components.SessionConfirm,
props: this.props,
});
this.$el.prepend(this.confirmView.el);
},
ok() {
this.unregisterEvents();
this.$('.session-confirm-wrapper').remove();
if (this.props.resolve) {
this.props.resolve();
}
},
cancel() {
this.unregisterEvents();
this.$('.session-confirm-wrapper').remove();
if (this.props.reject) {
this.props.reject();
}
},
onKeyup(event) {
if (event.key === 'Escape' || event.key === 'Esc') {
this.unregisterEvents();
this.props.onClickClose();
}
},
});
})();

View File

@ -178,7 +178,6 @@
<script type="text/javascript" src="../js/views/react_wrapper_view.js"></script>
<script type="text/javascript" src="../js/views/whisper_view.js"></script>
<script type="text/javascript" src="../js/views/session_confirm_view.js"></script>
<script type='text/javascript' src='../js/views/session_inbox_view.js'></script>
<script type="text/javascript" src="../js/views/identicon_svg_view.js"></script>

View File

@ -327,7 +327,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
* @returns
*/
private onClickOK() {
const newName = this.state.profileName.trim();
const newName = this.state.profileName ? this.state.profileName.trim() : '';
if (newName.length === 0 || newName.length > MAX_USERNAME_LENGTH) {
return;

View File

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

View File

@ -29,6 +29,8 @@ import { openGroupV2CompleteURLRegex } from '../../opengroup/utils/OpenGroupUtil
import { joinOpenGroupV2WithUIEvents } from '../../opengroup/opengroupV2/JoinOpenGroupV2';
import autoBind from 'auto-bind';
import { createClosedGroup } from "../../receiver/closedGroups";
export interface Props {
searchTerm: string;
@ -59,6 +61,7 @@ interface State {
loading: boolean;
overlay: false | SessionComposeToType;
valuePasted: string;
modal: null | JSX.Element;
}
export class LeftPaneMessageSection extends React.Component<Props, State> {
@ -71,6 +74,7 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
loading: false,
overlay: false,
valuePasted: '',
modal: null
};
autoBind(this);
@ -166,11 +170,20 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
return (
<div className="session-left-pane-section-content">
{this.renderHeader()}
{ this.state.modal ? this.state.modal : null }
{overlay ? this.renderClosableOverlay(overlay) : this.renderConversations()}
</div>
);
}
public setModal (modal: null | JSX.Element) {
this.setState({
modal
});
}
public renderConversations() {
return (
<div className="module-conversations-list-content">
@ -434,7 +447,7 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
return;
}
this.setState({ loading: true }, async () => {
const groupCreated = await MainViewController.createClosedGroup(groupName, groupMembers);
const groupCreated = await MainViewController.createClosedGroup(groupName, groupMembers, setModal);
if (groupCreated) {
this.handleToggleOverlay(undefined);

View File

@ -12,12 +12,12 @@ type Props = {
title: string;
onOk?: any;
onClose?: any;
onClickOk: any;
onClickOk?: any;
onClickClose?: any;
okText?: string;
cancelText?: string;
hideCancel?: boolean;
okTheme: SessionButtonColor;
okTheme?: SessionButtonColor;
closeTheme?: SessionButtonColor;
sessionIcon?: SessionIconType;
iconSize?: SessionIconSize;

View File

@ -12,7 +12,6 @@ import { getConversationLookup, getConversations } from '../../../state/selector
import { connect } from 'react-redux';
import { getPasswordHash } from '../../../../ts/data/data';
import { PasswordAction, SessionPasswordModal } from '../SessionPasswordModal';
import { ModalStatusLight } from '../../OnionStatusDialog';
export enum SessionSettingCategory {
Appearance = 'appearance',

View File

@ -422,7 +422,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
onUnblockContact: this.unblock,
onCopyPublicKey: this.copyPublicKey,
onDeleteContact: this.deleteContact,
onChangeNickname: this.changeNickname,
onClearNickname: this.clearNickname,
onDeleteMessages: this.deleteMessages,
onLeaveGroup: () => {
@ -1306,17 +1305,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
void ConversationInteraction.copyPublicKey(this.id);
}
public changeNickname() {
if (this.isGroup()) {
throw new Error(
'Called changeNickname() on a group. This is only supported in 1-on-1 conversation items and 1-on-1 conversation headers'
);
}
window.showNicknameDialog({
convoId: this.id,
});
}
public clearNickname = () => {
void this.setNickname('');
};

View File

@ -1,3 +1,4 @@
import React from 'react';
import { SignalService } from '../protobuf';
import { removeFromCache } from './cache';
import { EnvelopePlus } from './types';
@ -35,6 +36,7 @@ import { queueAllCachedFromSource } from './receiver';
import { actions as conversationActions } from '../state/ducks/conversations';
import { SwarmPolling } from '../session/snode_api/swarmPolling';
import { MessageModel } from '../models/message';
import { SessionConfirm } from '../components/session/SessionConfirm';
export const distributingClosedGroupEncryptionKeyPairs = new Map<string, ECKeyPair>();
@ -45,8 +47,7 @@ export async function handleClosedGroupControlMessage(
const { type } = groupUpdate;
const { Type } = SignalService.DataMessage.ClosedGroupControlMessage;
window.log.info(
` handle closed group update from ${envelope.senderIdentity || envelope.source} about group ${
envelope.source
` handle closed group update from ${envelope.senderIdentity || envelope.source} about group ${envelope.source
}`
);
@ -837,7 +838,7 @@ async function handleClosedGroupEncryptionKeyPairRequest(
return removeFromCache(envelope);
}
export async function createClosedGroup(groupName: string, members: Array<string>) {
export async function createClosedGroup(groupName: string, members: Array<string>, setModal: any) {
const setOfMembers = new Set(members);
const ourNumber = UserUtils.getOurPubKeyFromCache();
@ -892,7 +893,8 @@ export async function createClosedGroup(groupName: string, members: Array<string
groupName,
admins,
encryptionKeyPair,
dbMessage
dbMessage,
setModal
);
if (allInvitesSent) {
@ -927,7 +929,8 @@ async function sendToGroupMembers(
admins: Array<string>,
encryptionKeyPair: ECKeyPair,
dbMessage: MessageModel,
isRetry: boolean = false
setModal: any,
isRetry: boolean = false,
): Promise<any> {
const promises = createInvitePromises(
listOfMembers,
@ -948,14 +951,28 @@ async function sendToGroupMembers(
inviteResults.length > 1
? window.i18n('closedGroupInviteSuccessTitlePlural')
: window.i18n('closedGroupInviteSuccessTitle');
window.confirmationDialog({
title: invitesTitle,
message: window.i18n('closedGroupInviteSuccessMessage'),
});
// setModal(<SessionConfirm message={'hi'} title={invitesTitle} />)
setModal(
<SessionConfirm
title={title}
message={message}
onClickOk={deleteAccount}
okTheme={SessionButtonColor.Danger}
onClickClose={clearModal}
/>)
)
// window.confirmationDialog({
// title: invitesTitle,
// message: window.i18n('closedGroupInviteSuccessMessage'),
// });
}
return allInvitesSent;
} else {
// Confirmation dialog that recursively calls sendToGroupMembers on resolve
window.confirmationDialog({
title:
inviteResults.length > 1
@ -984,6 +1001,7 @@ async function sendToGroupMembers(
admins,
encryptionKeyPair,
dbMessage,
setModal,
isRetrySend
);
}

4
ts/window.d.ts vendored
View File

@ -41,7 +41,6 @@ declare global {
Whisper: any;
clearLocalData: any;
clipboard: any;
confirmationDialog: (params: ConfirmationDialogParams) => any;
dcodeIO: any;
displayNameRegex: any;
friends: any;
@ -68,9 +67,6 @@ declare global {
seedNodeList: any;
setPassword: any;
setSettingValue: any;
showEditProfileDialog: any;
showNicknameDialog: (options: { convoId: string }) => void;
showOnionStatusDialog: any;
showResetSessionIdDialog: any;
storage: any;
textsecure: LibTextsecure;