Modals finished. Need to xfer all to React

pull/710/head
Vincent 5 years ago
parent 9afb8b4d5e
commit 8aac656107

@ -1817,7 +1817,7 @@
"description": "Label text for menu bar visibility setting" "description": "Label text for menu bar visibility setting"
}, },
"startConversation": { "startConversation": {
"message": "Start new conversation…", "message": "Start New Conversation",
"description": "description":
"Label underneath number a user enters that is not an existing contact" "Label underneath number a user enters that is not an existing contact"
}, },
@ -2259,6 +2259,14 @@
"editProfileDialogTitle": { "editProfileDialogTitle": {
"message": "Editing Profile" "message": "Editing Profile"
}, },
"editProfileModalTitle": {
"message": "Edit Profile",
"description": "Title for the Edit Profile modal"
},
"profileName": { "profileName": {
"message": "Profile Name" "message": "Profile Name"
}, },

@ -50,6 +50,7 @@ const {
} = require('../../ts/components/conversation/CreateGroupDialog'); } = require('../../ts/components/conversation/CreateGroupDialog');
const { EditProfileDialog } = require('../../ts/components/EditProfileDialog'); const { EditProfileDialog } = require('../../ts/components/EditProfileDialog');
const { UserDetailsDialog } = require('../../ts/components/UserDetailsDialog'); const { UserDetailsDialog } = require('../../ts/components/UserDetailsDialog');
const { DevicePairingDialog } = require('../../ts/components/DevicePairingDialog');
const { SessionToast } = require('../../ts/components/session/SessionToast'); const { SessionToast } = require('../../ts/components/session/SessionToast');
const { SessionToggle } = require('../../ts/components/session/SessionToggle'); const { SessionToggle } = require('../../ts/components/session/SessionToggle');
const { SessionModal } = require('../../ts/components/session/SessionModal'); const { SessionModal } = require('../../ts/components/session/SessionModal');
@ -248,6 +249,7 @@ exports.setup = (options = {}) => {
CreateGroupDialog, CreateGroupDialog,
EditProfileDialog, EditProfileDialog,
UserDetailsDialog, UserDetailsDialog,
DevicePairingDialog,
SessionRegistrationView, SessionRegistrationView,
ConfirmDialog, ConfirmDialog,
UpdateGroupDialog, UpdateGroupDialog,

@ -27,183 +27,12 @@
}); });
this.qr.makeCode(textsecure.storage.user.getNumber()); this.qr.makeCode(textsecure.storage.user.getNumber());
}, },
reset() {
this.pubKey = null;
this.accepted = false;
this.isListening = false;
this.pubKeyToUnpair = null;
this.success = false;
},
events: {
'click #startPairing': 'startReceivingRequests',
'click #close': 'close',
'click .waitingForRequestView .cancel': 'stopReceivingRequests',
'click .requestReceivedView .skip': 'skipDevice',
'click #allowPairing': 'allowDevice',
'click .requestAcceptedView .ok': 'stopReceivingRequests',
'click .confirmUnpairView .cancel': 'stopReceivingRequests',
'click .confirmUnpairView .unpairDevice': 'confirmUnpairDevice',
},
render_attributes() {
return {
defaultTitle: i18n('pairedDevices'),
waitingForRequestTitle: i18n('waitingForDeviceToRegister'),
requestReceivedTitle: i18n('devicePairingReceived'),
requestAcceptedTitle: i18n('devicePairingAccepted'),
startPairingText: i18n('pairNewDevice'),
cancelText: i18n('cancel'),
unpairDevice: i18n('unpairDevice'),
closeText: i18n('close'),
skipText: i18n('skip'),
okText: i18n('ok'),
allowPairingText: i18n('allowPairing'),
confirmUnpairViewTitle: i18n('confirmUnpairingTitle'),
};
},
startReceivingRequests() {
this.trigger('startReceivingRequests');
this.isListening = true;
this.showView();
},
stopReceivingRequests() {
if (this.success) {
const deviceAlias = this.$('#deviceAlias')[0].value.trim();
const conv = ConversationController.get(this.pubKey);
if (conv) {
conv.setNickname(deviceAlias);
}
}
this.trigger('stopReceivingRequests');
this.reset();
this.showView();
},
requestReceived(secondaryDevicePubKey) {
// FIFO: push at the front of the array with unshift()
this.pubKeyRequests.unshift(secondaryDevicePubKey);
if (!this.pubKey) {
this.nextPubKey();
this.showView('requestReceived');
}
},
allowDevice() {
this.accepted = true;
this.trigger('devicePairingRequestAccepted', this.pubKey, errors =>
this.transmisssionCB(errors)
);
this.showView();
},
transmisssionCB(errors) {
if (!errors) {
this.$('.transmissionStatus').text(i18n('provideDeviceAlias'));
this.$('#deviceAliasView').show();
this.$('#deviceAlias').on('input', e => {
if (e.target.value.trim()) {
this.$('.requestAcceptedView .ok').removeAttr('disabled');
} else {
this.$('.requestAcceptedView .ok').attr('disabled', true);
}
});
this.$('.requestAcceptedView .ok').show();
this.$('.requestAcceptedView .ok').attr('disabled', true);
this.success = true;
} else {
this.$('.transmissionStatus').text(errors);
this.$('.requestAcceptedView .ok').show();
}
},
skipDevice() {
this.trigger('devicePairingRequestRejected', this.pubKey);
this.nextPubKey();
this.showView();
},
nextPubKey() {
// FIFO: pop at the back of the array using pop()
this.pubKey = this.pubKeyRequests.pop();
},
async confirmUnpairDevice() {
this.trigger('deviceUnpairingRequested', this.pubKeyToUnpair);
this.reset();
this.showView();
},
requestUnpairDevice(pubKey) {
this.pubKeyToUnpair = pubKey;
this.showView();
},
getPubkeyName(pubKey) {
const secretWords = window.mnemonic.pubkey_to_secret_words(pubKey);
const conv = ConversationController.get(pubKey);
const deviceAlias = conv ? conv.getNickname() : 'Unnamed Device';
return `${deviceAlias} (pairing secret: <i>${secretWords}</i>)`;
},
async showView() {
const defaultView = this.$('.defaultView');
const waitingForRequestView = this.$('.waitingForRequestView');
const requestReceivedView = this.$('.requestReceivedView');
const requestAcceptedView = this.$('.requestAcceptedView');
const confirmUnpairView = this.$('.confirmUnpairView');
if (this.pubKeyToUnpair) {
defaultView.hide();
requestReceivedView.hide();
waitingForRequestView.hide();
requestAcceptedView.hide();
confirmUnpairView.show();
const name = this.getPubkeyName(this.pubKeyToUnpair);
this.$('.confirmUnpairView #pubkey').html(name);
} else if (!this.isListening) {
requestReceivedView.hide();
waitingForRequestView.hide();
requestAcceptedView.hide();
confirmUnpairView.hide();
const ourPubKey = textsecure.storage.user.getNumber(); render() {
defaultView.show(); this.dialogView = new Whisper.ReactWrapperView({
const pubKeys = await libloki.storage.getSecondaryDevicesFor(ourPubKey); className: 'device-pairing-dialog',
this.$('#pairedPubKeys').empty(); Component: window.Signal.Components.DevicePairingDialog,
if (pubKeys && pubKeys.length > 0) {
this.$('#startPairing').attr('disabled', true);
pubKeys.forEach(x => {
const name = this.getPubkeyName(x);
const li = $('<li>').html(name);
if (window.lokiFeatureFlags.multiDeviceUnpairing) {
const link = $('<a>')
.text('Unpair')
.attr('href', '#');
link.on('click', () => this.requestUnpairDevice(x));
li.append(' - ');
li.append(link);
}
this.$('#pairedPubKeys').append(li);
}); });
} else {
this.$('#startPairing').removeAttr('disabled');
this.$('#pairedPubKeys').append('<li>No paired devices</li>');
}
} else if (this.accepted) {
defaultView.hide();
requestReceivedView.hide();
waitingForRequestView.hide();
requestAcceptedView.show();
} else if (this.pubKey) {
const secretWords = window.mnemonic.pubkey_to_secret_words(this.pubKey);
this.$('.secretWords').text(secretWords);
requestReceivedView.show();
waitingForRequestView.hide();
requestAcceptedView.hide();
defaultView.hide();
} else {
waitingForRequestView.show();
requestReceivedView.hide();
requestAcceptedView.hide();
defaultView.hide();
}
},
close() {
this.remove();
this.qr.clear();
if (this.pubKey && !this.accepted) {
this.trigger('devicePairingRequestRejected', this.pubKey);
}
this.trigger('close');
}, },
}); });
})(); })();

@ -0,0 +1,209 @@
/* global
Whisper,
i18n,
libloki,
textsecure,
ConversationController,
$,
QRCode,
*/
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
Whisper.DevicePairingDialogView = Whisper.View.extend({
className: 'loki-dialog device-pairing-dialog modal',
templateName: 'device-pairing-dialog',
initialize() {
this.pubKeyRequests = [];
this.reset();
this.render();
this.showView();
this.qr = new QRCode(this.$('#qr')[0], {
correctLevel: QRCode.CorrectLevel.L,
});
this.qr.makeCode(textsecure.storage.user.getNumber());
},
reset() {
this.pubKey = null;
this.accepted = false;
this.isListening = false;
this.pubKeyToUnpair = null;
this.success = false;
},
events: {
'click #startPairing': 'startReceivingRequests',
'click #close': 'close',
'click .waitingForRequestView .cancel': 'stopReceivingRequests',
'click .requestReceivedView .skip': 'skipDevice',
'click #allowPairing': 'allowDevice',
'click .requestAcceptedView .ok': 'stopReceivingRequests',
'click .confirmUnpairView .cancel': 'stopReceivingRequests',
'click .confirmUnpairView .unpairDevice': 'confirmUnpairDevice',
},
render_attributes() {
return {
defaultTitle: i18n('pairedDevices'),
waitingForRequestTitle: i18n('waitingForDeviceToRegister'),
requestReceivedTitle: i18n('devicePairingReceived'),
requestAcceptedTitle: i18n('devicePairingAccepted'),
startPairingText: i18n('pairNewDevice'),
cancelText: i18n('cancel'),
unpairDevice: i18n('unpairDevice'),
closeText: i18n('close'),
skipText: i18n('skip'),
okText: i18n('ok'),
allowPairingText: i18n('allowPairing'),
confirmUnpairViewTitle: i18n('confirmUnpairingTitle'),
};
},
startReceivingRequests() {
this.trigger('startReceivingRequests');
this.isListening = true;
this.showView();
},
stopReceivingRequests() {
if (this.success) {
const deviceAlias = this.$('#deviceAlias')[0].value.trim();
const conv = ConversationController.get(this.pubKey);
if (conv) {
conv.setNickname(deviceAlias);
}
}
this.trigger('stopReceivingRequests');
this.reset();
this.showView();
},
requestReceived(secondaryDevicePubKey) {
// FIFO: push at the front of the array with unshift()
this.pubKeyRequests.unshift(secondaryDevicePubKey);
if (!this.pubKey) {
this.nextPubKey();
this.showView('requestReceived');
}
},
allowDevice() {
this.accepted = true;
this.trigger('devicePairingRequestAccepted', this.pubKey, errors =>
this.transmisssionCB(errors)
);
this.showView();
},
transmisssionCB(errors) {
if (!errors) {
this.$('.transmissionStatus').text(i18n('provideDeviceAlias'));
this.$('#deviceAliasView').show();
this.$('#deviceAlias').on('input', e => {
if (e.target.value.trim()) {
this.$('.requestAcceptedView .ok').removeAttr('disabled');
} else {
this.$('.requestAcceptedView .ok').attr('disabled', true);
}
});
this.$('.requestAcceptedView .ok').show();
this.$('.requestAcceptedView .ok').attr('disabled', true);
this.success = true;
} else {
this.$('.transmissionStatus').text(errors);
this.$('.requestAcceptedView .ok').show();
}
},
skipDevice() {
this.trigger('devicePairingRequestRejected', this.pubKey);
this.nextPubKey();
this.showView();
},
nextPubKey() {
// FIFO: pop at the back of the array using pop()
this.pubKey = this.pubKeyRequests.pop();
},
async confirmUnpairDevice() {
this.trigger('deviceUnpairingRequested', this.pubKeyToUnpair);
this.reset();
this.showView();
},
requestUnpairDevice(pubKey) {
this.pubKeyToUnpair = pubKey;
this.showView();
},
getPubkeyName(pubKey) {
const secretWords = window.mnemonic.pubkey_to_secret_words(pubKey);
const conv = ConversationController.get(pubKey);
const deviceAlias = conv ? conv.getNickname() : 'Unnamed Device';
return `${deviceAlias} (pairing secret: <i>${secretWords}</i>)`;
},
async showView() {
const defaultView = this.$('.defaultView');
const waitingForRequestView = this.$('.waitingForRequestView');
const requestReceivedView = this.$('.requestReceivedView');
const requestAcceptedView = this.$('.requestAcceptedView');
const confirmUnpairView = this.$('.confirmUnpairView');
if (this.pubKeyToUnpair) {
defaultView.hide();
requestReceivedView.hide();
waitingForRequestView.hide();
requestAcceptedView.hide();
confirmUnpairView.show();
const name = this.getPubkeyName(this.pubKeyToUnpair);
this.$('.confirmUnpairView #pubkey').html(name);
} else if (!this.isListening) {
requestReceivedView.hide();
waitingForRequestView.hide();
requestAcceptedView.hide();
confirmUnpairView.hide();
const ourPubKey = textsecure.storage.user.getNumber();
defaultView.show();
const pubKeys = await libloki.storage.getSecondaryDevicesFor(ourPubKey);
this.$('#pairedPubKeys').empty();
if (pubKeys && pubKeys.length > 0) {
this.$('#startPairing').attr('disabled', true);
pubKeys.forEach(x => {
const name = this.getPubkeyName(x);
const li = $('<li>').html(name);
if (window.lokiFeatureFlags.multiDeviceUnpairing) {
const link = $('<a>')
.text('Unpair')
.attr('href', '#');
link.on('click', () => this.requestUnpairDevice(x));
li.append(' - ');
li.append(link);
}
this.$('#pairedPubKeys').append(li);
});
} else {
this.$('#startPairing').removeAttr('disabled');
this.$('#pairedPubKeys').append('<li>No paired devices</li>');
}
} else if (this.accepted) {
defaultView.hide();
requestReceivedView.hide();
waitingForRequestView.hide();
requestAcceptedView.show();
} else if (this.pubKey) {
const secretWords = window.mnemonic.pubkey_to_secret_words(this.pubKey);
this.$('.secretWords').text(secretWords);
requestReceivedView.show();
waitingForRequestView.hide();
requestAcceptedView.hide();
defaultView.hide();
} else {
waitingForRequestView.show();
requestReceivedView.hide();
requestAcceptedView.hide();
defaultView.hide();
}
},
close() {
this.remove();
this.qr.clear();
if (this.pubKey && !this.accepted) {
this.trigger('devicePairingRequestRejected', this.pubKey);
}
this.trigger('close');
},
});
})();

@ -78,6 +78,16 @@ $session-margin-sm: 10px;
$session-margin-md: 15px; $session-margin-md: 15px;
$session-margin-lg: 20px; $session-margin-lg: 20px;
div.spacer-sm {
height: $session-margin-sm;
}
div.spacer-md {
height: $session-margin-md;
}
div.spacer-lg {
height: $session-margin-lg;
}
@mixin session-color-subtle($color) { @mixin session-color-subtle($color) {
color: rgba($color, 0.6); color: rgba($color, 0.6);
} }
@ -173,6 +183,9 @@ $session_message-container-border-radius: 5px;
&.secondary { &.secondary {
background-color: $session-color-secondary; background-color: $session-color-secondary;
} }
&.success {
background-color: $session-color-success;
}
&.danger { &.danger {
background-color: $session-color-danger; background-color: $session-color-danger;
} }
@ -493,19 +506,21 @@ label {
.session-modal { .session-modal {
z-index: 150; z-index: 150;
position: fixed; position: fixed;
top: 50%;
left: 50%; left: 50%;
top: 50%;
transform: translate(-50%, -50%);
box-sizing: border-box; box-sizing: border-box;
height: 529px; max-height: 70vh;
width: 345px; max-width: 70vw;
background-color: $session-shade-4; background-color: $session-shade-4;
border: 1px solid $session-shade-8; border: 1px solid $session-shade-8;
.header { &__header {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
padding: $session-margin-lg; padding: $session-margin-lg;
font-family: 'Wasa'; font-family: 'Wasa';
@ -514,17 +529,45 @@ label {
font-size: 15px; font-size: 15px;
font-weight: 700; font-weight: 700;
.close, &__icons, &__close {
.icons { width: 60px;
width: 70px;
} }
.close > div { &__icons {
float: right;
}
&__close > div {
float: left; float: left;
} }
.icons > div { &__icons > div {
float: right; float: right;
padding-left: 10px;
}
}
&__body {
padding: $session-margin-lg;
font-family: 'Wasa';
line-height: 16px;
font-size: 13px;
.message{
text-align: center;
} }
} }
&__button-group {
display: flex;
justify-content: flex-end;
&__center{
align-items: center;
}
.session-button {
margin-left: $session-margin-sm;
}
}
} }
.session-toggle { .session-toggle {
@ -636,3 +679,11 @@ label {
} }
} }
} }
.edit-profile-dialog .image-upload-section {
position: absolute;
margin-top: 50px;
margin-left: 75px;
}

@ -1,5 +1,8 @@
import React from 'react'; import React from 'react';
import { SessionModal } from './session/SessionModal';
import { SessionButton } from './session/SessionButton';
interface Props { interface Props {
titleText: string; titleText: string;
messageText: string; messageText: string;
@ -16,18 +19,23 @@ export class ConfirmDialog extends React.Component<Props> {
public render() { public render() {
return ( return (
<div className="content"> <SessionModal title={this.props.titleText} onClose={() => null} onOk={() => null}>
<p className="titleText">{this.props.titleText}</p> <div className="spacer-md"></div>
<p className="messageText">{this.props.messageText}</p> <p className="messageText">{this.props.messageText}</p>
<div className="buttons"> <div className="spacer-md"></div>
<button className="cancel" tabIndex={0} onClick={this.props.onClose}>
{this.props.cancelText} <div className="session-modal__button-group">
</button> <SessionButton
<button className="ok" tabIndex={0} onClick={this.props.onConfirm}> text = {this.props.okText}
{this.props.okText} onClick = {this.props.onConfirm}
</button> />
</div>
<SessionButton
text = {this.props.cancelText}
onClick = {this.props.onClose}
/>
</div> </div>
</SessionModal>
); );
} }
} }

@ -2,6 +2,11 @@ import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { Avatar } from './Avatar'; import { Avatar } from './Avatar';
import { SessionModal } from './session/SessionModal';
import { SessionButton, SessionButtonColor } from './session/SessionButton';
import { SessionIconButton, SessionIconType, SessionIconSize } from './session/icon';
declare global { declare global {
interface Window { interface Window {
displayNameRegex: any; displayNameRegex: any;
@ -63,11 +68,15 @@ export class EditProfileDialog extends React.Component<Props, State> {
); );
return ( return (
<div className="content"> <SessionModal
title={i18n('editProfileModalTitle')}
onOk={this.onClickOK}
onClose={this.closeDialog}
>
<div className="avatar-center"> <div className="avatar-center">
<div className="avatar-center-inner"> <div className="avatar-center-inner">
{this.renderAvatar()} {this.renderAvatar()}
<div className="upload-btn-background"> <div className="image-upload-section">
<input <input
type="file" type="file"
ref={this.inputEl} ref={this.inputEl}
@ -76,9 +85,10 @@ export class EditProfileDialog extends React.Component<Props, State> {
name="name" name="name"
onChange={this.onFileSelected} onChange={this.onFileSelected}
/> />
<div
role="button" <SessionIconButton
className={'module-message__buttons__upload'} iconType = {SessionIconType.Upload}
iconSize = {SessionIconSize.Large}
onClick={() => { onClick={() => {
const el = this.inputEl.current; const el = this.inputEl.current;
if (el) { if (el) {
@ -89,6 +99,9 @@ export class EditProfileDialog extends React.Component<Props, State> {
</div> </div>
</div> </div>
</div> </div>
<div className="spacer md"></div>
<input <input
type="text" type="text"
className="profile-name" className="profile-name"
@ -101,15 +114,22 @@ export class EditProfileDialog extends React.Component<Props, State> {
/> />
<div className="message">{i18n('editProfileDisplayNameWarning')}</div> <div className="message">{i18n('editProfileDisplayNameWarning')}</div>
<span className={errorMessageClasses}>{this.state.errorMessage}</span> <span className={errorMessageClasses}>{this.state.errorMessage}</span>
<div className="buttons">
<button className="cancel" tabIndex={0} onClick={this.closeDialog}> <div className="spacer-lg"></div>
{cancelText}
</button> <div className="session-modal__button-group">
<button className="ok" tabIndex={0} onClick={this.onClickOK}> <SessionButton
{okText} text={okText}
</button> buttonColor={SessionButtonColor.Secondary}
</div> onClick={this.onClickOK}
/>
<SessionButton
text={cancelText}
buttonColor={SessionButtonColor.Primary}
onClick={this.closeDialog}
/>
</div> </div>
</SessionModal>
); );
} }

@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import { Avatar } from './Avatar'; import { Avatar } from './Avatar';
import { SessionDropdown } from './session/SessionDropdown'; import { SessionModal } from './session/SessionModal';
import { SessionButton, SessionButtonType, SessionButtonColor } from './session/SessionButton';
declare global { declare global {
interface Window { interface Window {
@ -32,63 +33,26 @@ export class UserDetailsDialog extends React.Component<Props> {
public render() { public render() {
const i18n = this.props.i18n; const i18n = this.props.i18n;
const cancelText = i18n('cancel');
const startConversation = i18n('startConversation');
const items = [
{
content: 'sdgsdfg',
display: true, //!isPublic && !isMe,
},
{
content: i18n('changeNickname'),
display: true, //!isPublic && !isMe,
},
{
content: i18n('clearNickname'),
display: true, //!isPublic && !isMe && hasNickname,
},
{
content: i18n('copyPublicKey'),
display: false, //!isPublic,
},
{
content: i18n('deleteMessages'),
},
{
content: i18n('deleteContact'),
display: true, //!isMe && isClosable && !isPublic,
},
{
content: i18n('deletePublicChannel'),
display: true, //!isMe && isClosable && !isPublic,
},
];
return ( return (
<div className="content"> <SessionModal
<SessionDropdown items={items} /> title={this.props.profileName}
onOk={() => null}
onClose={this.closeDialog}
>
<div className="avatar-center"> <div className="avatar-center">
<div className="avatar-center-inner">{this.renderAvatar()}</div> <div className="avatar-center-inner">{this.renderAvatar()}</div>
</div> </div>
<div className="profile-name">{this.props.profileName}</div>
<div className="message">{this.props.pubkey}</div> <div className="message">{this.props.pubkey}</div>
<div className="buttons"> <div className="session-modal__button-group__center">
<button className="cancel" tabIndex={0} onClick={this.closeDialog}> <SessionButton
{cancelText} text={i18n('startConversation')}
</button> buttonType={SessionButtonType.Default}
buttonColor={SessionButtonColor.Primary}
<button
className="ok"
tabIndex={0}
onClick={this.onClickStartConversation} onClick={this.onClickStartConversation}
> />
{startConversation}
</button>
</div>
</div> </div>
</SessionModal>
); );
} }

@ -16,6 +16,7 @@ export enum SessionButtonColor {
White = 'white', White = 'white',
Primary = 'primary', Primary = 'primary',
Secondary = 'secondary', Secondary = 'secondary',
Success = 'success',
Danger = 'danger', Danger = 'danger',
Warning = 'warning', Warning = 'warning',
} }

@ -5,39 +5,87 @@ import { SessionIconButton, SessionIconSize, SessionIconType } from './icon/';
interface Props { interface Props {
title: string; title: string;
body: any; onClose: any;
onOk: any;
//Maximum of two icons in header
headerIconButtons?: Array<{
type: SessionIconType;
onClick?: any;
}>;
} }
export class SessionModal extends React.PureComponent<Props> { interface State {
isVisible: boolean;
}
export class SessionModal extends React.PureComponent<Props, State> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.state = {
isVisible: true,
};
this.close = this.close.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
window.addEventListener('keyup', this.onKeyUp);
} }
public render() { public render() {
const { title } = this.props; const { title, headerIconButtons } = this.props;
const { isVisible } = this.state;
return ( return isVisible ? (
<div className={classNames('session-modal')}> <div className={classNames('session-modal')}>
<div className="header"> <div className="session-modal__header">
<div className="close"> <div className="session-modal__header__close">
<SessionIconButton <SessionIconButton
iconType={SessionIconType.Exit} iconType={SessionIconType.Exit}
iconSize={SessionIconSize.Small} iconSize={SessionIconSize.Small}
onClick={this.close}
/> />
</div> </div>
<div className="title">{title}</div> <div className="session-modal__header__title">{title}</div>
<div className="icons"> <div className="session-modal__header__icons">
<SessionIconButton { headerIconButtons ? headerIconButtons.map((iconItem: any) => {
iconType={SessionIconType.Search} return (
iconSize={SessionIconSize.Medium}
/>
<SessionIconButton <SessionIconButton
iconType={SessionIconType.AddUser} iconType={iconItem.type}
iconSize={SessionIconSize.Medium} iconSize={SessionIconSize.Medium}
/> />
)
}) : null
}
</div>
</div> </div>
<div className="session-modal__body">
{this.props.children}
</div> </div>
</div> </div>
); ) : null;
}
public close() {
this.setState({
isVisible: false,
});
window.removeEventListener('keyup', this.onKeyUp);
this.props.onClose();
}
public onKeyUp(event: any){
switch (event.key) {
case 'Enter':
this.props.onOk();
break;
case 'Esc':
case 'Escape':
this.close();
break;
default:
}
} }
} }

@ -26,6 +26,7 @@ export enum SessionIconType {
Star = 'star', Star = 'star',
QR = 'qr', QR = 'qr',
Users = 'users', Users = 'users',
Upload = 'upload',
Warning = 'warning', Warning = 'warning',
} }
@ -171,6 +172,11 @@ export const icons = {
'M9.38,2.17c-1.73,0-3.12,1.4-3.12,3.12s1.4,3.12,3.12,3.12s3.12-1.4,3.12-3.12S11.1,2.17,9.38,2.17z M16.93,0.25c2.3,0.59,3.92,2.67,3.92,5.05s-1.61,4.46-3.92,5.05c-0.56,0.14-1.12-0.19-1.27-0.75c-0.14-0.56,0.19-1.12,0.75-1.27 c1.38-0.35,2.35-1.6,2.35-3.03s-0.97-2.67-2.35-3.03c-0.56-0.14-0.9-0.71-0.75-1.27C15.8,0.44,16.37,0.11,16.93,0.25z M9.38,0.08 c2.88,0,5.21,2.33,5.21,5.21s-2.33,5.21-5.21,5.21S4.17,8.17,4.17,5.29C4.17,2.42,6.5,0.08,9.38,0.08z M21.09,12.75 c2.22,0.57,3.8,2.53,3.9,4.81L25,17.79v2.08c0,0.58-0.47,1.04-1.04,1.04c-0.54,0-0.98-0.41-1.04-0.93l-0.01-0.11v-2.08 c0-1.42-0.96-2.67-2.34-3.02c-0.56-0.14-0.89-0.71-0.75-1.27C19.97,12.94,20.54,12.61,21.09,12.75z M13.54,12.58 c2.8,0,5.09,2.21,5.2,4.99v0.22v2.08c0,0.58-0.47,1.04-1.04,1.04c-0.54,0-0.98-0.41-1.04-0.93l-0.01-0.11v-2.08 c0-1.67-1.3-3.03-2.95-3.12h-0.18H5.21c-1.67,0-3.03,1.3-3.12,2.95v0.18v2.08c0,0.58-0.47,1.04-1.04,1.04 c-0.54,0-0.98-0.41-1.04-0.93L0,19.88V17.8c0-2.8,2.21-5.09,4.99-5.2h0.22h8.33V12.58z', 'M9.38,2.17c-1.73,0-3.12,1.4-3.12,3.12s1.4,3.12,3.12,3.12s3.12-1.4,3.12-3.12S11.1,2.17,9.38,2.17z M16.93,0.25c2.3,0.59,3.92,2.67,3.92,5.05s-1.61,4.46-3.92,5.05c-0.56,0.14-1.12-0.19-1.27-0.75c-0.14-0.56,0.19-1.12,0.75-1.27 c1.38-0.35,2.35-1.6,2.35-3.03s-0.97-2.67-2.35-3.03c-0.56-0.14-0.9-0.71-0.75-1.27C15.8,0.44,16.37,0.11,16.93,0.25z M9.38,0.08 c2.88,0,5.21,2.33,5.21,5.21s-2.33,5.21-5.21,5.21S4.17,8.17,4.17,5.29C4.17,2.42,6.5,0.08,9.38,0.08z M21.09,12.75 c2.22,0.57,3.8,2.53,3.9,4.81L25,17.79v2.08c0,0.58-0.47,1.04-1.04,1.04c-0.54,0-0.98-0.41-1.04-0.93l-0.01-0.11v-2.08 c0-1.42-0.96-2.67-2.34-3.02c-0.56-0.14-0.89-0.71-0.75-1.27C19.97,12.94,20.54,12.61,21.09,12.75z M13.54,12.58 c2.8,0,5.09,2.21,5.2,4.99v0.22v2.08c0,0.58-0.47,1.04-1.04,1.04c-0.54,0-0.98-0.41-1.04-0.93l-0.01-0.11v-2.08 c0-1.67-1.3-3.03-2.95-3.12h-0.18H5.21c-1.67,0-3.03,1.3-3.12,2.95v0.18v2.08c0,0.58-0.47,1.04-1.04,1.04 c-0.54,0-0.98-0.41-1.04-0.93L0,19.88V17.8c0-2.8,2.21-5.09,4.99-5.2h0.22h8.33V12.58z',
viewBox: '0 0 25 21', viewBox: '0 0 25 21',
}, },
[SessionIconType.Upload]: {
path:
'M380.032,133.472l-112-128C264.992,2.016,260.608,0,256,0c-4.608,0-8.992,2.016-12.032,5.472l-112,128 c-4.128,4.736-5.152,11.424-2.528,17.152C132.032,156.32,137.728,160,144,160h64v208c0,8.832,7.168,16,16,16h64 c8.832,0,16-7.168,16-16V160h64c6.272,0,11.968-3.648,14.56-9.376C385.152,144.896,384.192,138.176,380.032,133.472z M432,352v96H80v-96H16v128c0,17.696,14.336,32,32,32h416c17.696,0,32-14.304,32-32V352H432z',
viewBox: '0 0 512 512',
},
[SessionIconType.Warning]: { [SessionIconType.Warning]: {
path: path:
'M243.225,333.382c-13.6,0-25,11.4-25,25s11.4,25,25,25c13.1,0,25-11.4,24.4-24.4 C268.225,344.682,256.925,333.382,243.225,333.382z M474.625,421.982c15.7-27.1,15.8-59.4,0.2-86.4l-156.6-271.2c-15.5-27.3-43.5-43.5-74.9-43.5s-59.4,16.3-74.9,43.4 l-156.8,271.5c-15.6,27.3-15.5,59.8,0.3,86.9c15.6,26.8,43.5,42.9,74.7,42.9h312.8 C430.725,465.582,458.825,449.282,474.625,421.982z M440.625,402.382c-8.7,15-24.1,23.9-41.3,23.9h-312.8 c-17,0-32.3-8.7-40.8-23.4c-8.6-14.9-8.7-32.7-0.1-47.7l156.8-271.4c8.5-14.9,23.7-23.7,40.9-23.7c17.1,0,32.4,8.9,40.9,23.8 l156.7,271.4C449.325,369.882,449.225,387.482,440.625,402.382z M237.025,157.882c-11.9,3.4-19.3,14.2-19.3,27.3c0.6,7.9,1.1,15.9,1.7,23.8c1.7,30.1,3.4,59.6,5.1,89.7 c0.6,10.2,8.5,17.6,18.7,17.6c10.2,0,18.2-7.9,18.7-18.2c0-6.2,0-11.9,0.6-18.2c1.1-19.3,2.3-38.6,3.4-57.9 c0.6-12.5,1.7-25,2.3-37.5c0-4.5-0.6-8.5-2.3-12.5C260.825,160.782,248.925,155.082,237.025,157.882z', 'M243.225,333.382c-13.6,0-25,11.4-25,25s11.4,25,25,25c13.1,0,25-11.4,24.4-24.4 C268.225,344.682,256.925,333.382,243.225,333.382z M474.625,421.982c15.7-27.1,15.8-59.4,0.2-86.4l-156.6-271.2c-15.5-27.3-43.5-43.5-74.9-43.5s-59.4,16.3-74.9,43.4 l-156.8,271.5c-15.6,27.3-15.5,59.8,0.3,86.9c15.6,26.8,43.5,42.9,74.7,42.9h312.8 C430.725,465.582,458.825,449.282,474.625,421.982z M440.625,402.382c-8.7,15-24.1,23.9-41.3,23.9h-312.8 c-17,0-32.3-8.7-40.8-23.4c-8.6-14.9-8.7-32.7-0.1-47.7l156.8-271.4c8.5-14.9,23.7-23.7,40.9-23.7c17.1,0,32.4,8.9,40.9,23.8 l156.7,271.4C449.325,369.882,449.225,387.482,440.625,402.382z M237.025,157.882c-11.9,3.4-19.3,14.2-19.3,27.3c0.6,7.9,1.1,15.9,1.7,23.8c1.7,30.1,3.4,59.6,5.1,89.7 c0.6,10.2,8.5,17.6,18.7,17.6c10.2,0,18.2-7.9,18.7-18.2c0-6.2,0-11.9,0.6-18.2c1.1-19.3,2.3-38.6,3.4-57.9 c0.6-12.5,1.7-25,2.3-37.5c0-4.5-0.6-8.5-2.3-12.5C260.825,160.782,248.925,155.082,237.025,157.882z',

Loading…
Cancel
Save