Move confirmation_dialog_view to ts and React

* Moves confirmation_dialog_view to ts and React

* showConfirmationDialog API
This commit is contained in:
Josh Perez 2021-01-04 13:47:14 -05:00 committed by Scott Nonnenberg
parent 031a1fcc3d
commit 2529e208c1
16 changed files with 154 additions and 254 deletions

View file

@ -114,18 +114,6 @@
<button class='finish' tabIndex='1'><span class='icon'></span></button> <button class='finish' tabIndex='1'><span class='icon'></span></button>
</script> </script>
<script type='text/x-tmpl-mustache' id='confirmation-dialog'>
<div class="content">
<div class='message'>{{ message }}</div>
<div class='buttons'>
<button class='ok' tabindex='2'>{{ ok }}</button>
{{ #showCancel }}
<button class='cancel' tabindex='1'>{{ cancel }}</button>
{{ /showCancel }}
</div>
</div>
</script>
<script type='text/x-tmpl-mustache' id='safety-number-change-dialog'> <script type='text/x-tmpl-mustache' id='safety-number-change-dialog'>
<div class='safety-number-change-dialog-wrapper'></div> <div class='safety-number-change-dialog-wrapper'></div>
</script> </script>
@ -361,7 +349,7 @@
<script type='text/javascript' src='js/views/recorder_view.js'></script> <script type='text/javascript' src='js/views/recorder_view.js'></script>
<script type='text/javascript' src='ts/views/conversation_view.js'></script> <script type='text/javascript' src='ts/views/conversation_view.js'></script>
<script type='text/javascript' src='js/views/inbox_view.js'></script> <script type='text/javascript' src='js/views/inbox_view.js'></script>
<script type='text/javascript' src='js/views/confirmation_dialog_view.js'></script> <script type='text/javascript' src='ts/shims/showConfirmationDialog.js'></script>
<script type='text/javascript' src='js/views/identicon_svg_view.js'></script> <script type='text/javascript' src='js/views/identicon_svg_view.js'></script>
<script type='text/javascript' src='js/views/install_view.js'></script> <script type='text/javascript' src='js/views/install_view.js'></script>
<script type='text/javascript' src='js/views/banner_view.js'></script> <script type='text/javascript' src='js/views/banner_view.js'></script>

View file

@ -1,7 +1,7 @@
// Copyright 2018-2020 Signal Messenger, LLC // Copyright 2018-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
/* global $, Whisper, i18n */ /* global $, i18n */
$(document).on('keydown', e => { $(document).on('keydown', e => {
if (e.keyCode === 27) { if (e.keyCode === 27) {
@ -35,7 +35,8 @@ if (window.forCalling) {
message = i18n('audioPermissionNeeded'); message = i18n('audioPermissionNeeded');
} }
window.view = new Whisper.ConfirmationDialogView({ window.showConfirmationDialog({
confirmStyle: 'affirmative',
message, message,
okText: i18n('allowAccess'), okText: i18n('allowAccess'),
resolve: () => { resolve: () => {
@ -48,5 +49,3 @@ window.view = new Whisper.ConfirmationDialogView({
}, },
reject: window.closePermissionsPopup, reject: window.closePermissionsPopup,
}); });
window.view.$el.appendTo($body);

View file

@ -1,79 +0,0 @@
// Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* global Backbone, Whisper, i18n */
// eslint-disable-next-line func-names
(function () {
window.Whisper = window.Whisper || {};
Whisper.ConfirmationDialogView = Whisper.View.extend({
className: 'confirmation-dialog modal',
templateName: 'confirmation-dialog',
initialize(options) {
this.previousFocus = document.activeElement;
this.message = options.message;
this.hideCancel = options.hideCancel;
this.resolve = options.resolve;
this.okText = options.okText || i18n('ok');
this.reject = options.reject;
this.cancelText = options.cancelText || i18n('cancel');
if (Whisper.activeConfirmationView) {
Whisper.activeConfirmationView.remove();
Whisper.activeConfirmationView = null;
}
Whisper.activeConfirmationView = this;
this.render();
},
events: {
keydown: 'onKeydown',
'click .ok': 'ok',
'click .cancel': 'cancel',
},
remove() {
if (this.previousFocus && this.previousFocus.focus) {
this.previousFocus.focus();
}
Backbone.View.prototype.remove.call(this);
},
render_attributes() {
return {
message: this.message,
showCancel: !this.hideCancel,
cancel: this.cancelText,
ok: this.okText,
};
},
ok() {
this.remove();
if (this.resolve) {
this.resolve();
}
},
cancel() {
this.remove();
if (this.reject) {
this.reject(new Error('User clicked cancel button'));
}
},
onKeydown(event) {
if (event.key === 'Escape' || event.key === 'Esc') {
this.cancel();
event.preventDefault();
event.stopPropagation();
}
},
focusCancel() {
// We delay this call because we might be called inside click handlers
// which would set focus to themselves afterwards!
setTimeout(() => this.$('.cancel').focus(), 1);
},
});
})();

View file

@ -54,14 +54,12 @@
}, },
confirm(message, okText) { confirm(message, okText) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const dialog = new Whisper.ConfirmationDialogView({ window.showConfirmationDialog({
message, message,
okText, okText,
resolve, resolve,
reject, reject,
}); });
this.$el.append(dialog.el);
dialog.focusCancel();
}); });
}, },
}, },

View file

@ -23,20 +23,9 @@
</head> </head>
<body class='permissions-popup'> <body class='permissions-popup'>
</body> </body>
<script type='text/x-tmpl-mustache' id='confirmation-dialog'>
<div class="content">
<div class='message'>{{ message }}</div>
<div class='buttons'>
{{ #showCancel }}
<button class='cancel' tabindex='2'>{{ cancel }}</button>
{{ /showCancel }}
<button class='ok' tabindex='1'>{{ ok }}</button>
</div>
</div>
</script>
<script type='text/javascript' src='js/components.js'></script> <script type='text/javascript' src='js/components.js'></script>
<script type='text/javascript' src='ts/backboneJquery.js'></script> <script type='text/javascript' src='ts/backboneJquery.js'></script>
<script type='text/javascript' src='js/views/whisper_view.js'></script> <script type='text/javascript' src='js/views/whisper_view.js'></script>
<script type='text/javascript' src='js/views/confirmation_dialog_view.js'></script> <script type='text/javascript' src='ts/shims/showConfirmationDialog.js'></script>
<script type='text/javascript' src='js/permissions_popup_start.js'></script> <script type='text/javascript' src='js/permissions_popup_start.js'></script>
</html> </html>

View file

@ -3,9 +3,13 @@
/* global window */ /* global window */
window.React = require('react');
window.ReactDOM = require('react-dom');
const { ipcRenderer, remote } = require('electron'); const { ipcRenderer, remote } = require('electron');
const url = require('url'); const url = require('url');
const i18n = require('./js/modules/i18n'); const i18n = require('./js/modules/i18n');
const { ConfirmationModal } = require('./ts/components/ConfirmationModal');
const { makeGetter, makeSetter } = require('./preload_utils'); const { makeGetter, makeSetter } = require('./preload_utils');
const { nativeTheme } = remote.require('electron'); const { nativeTheme } = remote.require('electron');
@ -20,6 +24,11 @@ window.theme = config.theme;
window.i18n = i18n.setup(locale, localeMessages); window.i18n = i18n.setup(locale, localeMessages);
window.forCalling = config.forCalling === 'true'; window.forCalling = config.forCalling === 'true';
window.forCamera = config.forCamera === 'true'; window.forCamera = config.forCamera === 'true';
window.Signal = {
Components: {
ConfirmationModal,
},
};
function setSystemTheme() { function setSystemTheme() {
window.systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; window.systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';

View file

@ -292,79 +292,10 @@
cursor: pointer; cursor: pointer;
} }
.confirmation-dialog {
.content {
max-width: 350px;
margin: 100px auto;
padding: 1em;
border-radius: 5px;
overflow: auto;
@include light-theme {
background: $color-white;
box-shadow: 0px 0px 15px 0px $color-black-alpha-20;
}
@include dark-theme {
background: $color-black;
color: $color-gray-02;
box-shadow: 0px 0px 15px 0px $color-white-alpha-20;
}
.buttons {
margin-top: 10px;
button {
float: right;
margin-left: 10px;
padding: 5px 8px;
border-radius: 5px;
outline: none;
@include keyboard-mode {
&:focus {
outline: -webkit-focus-ring-color auto 5px;
}
}
@include light-theme {
background-color: $color-gray-02;
border: 1px solid $color-gray-15;
}
@include dark-theme {
background-color: $color-gray-90;
border: 1px solid $color-gray-45;
color: $color-gray-02;
}
&:hover {
@include light-theme {
background-color: $color-gray-15;
border-color: $color-gray-25;
}
@include dark-theme {
background-color: $color-gray-75;
border-color: $color-gray-45;
}
}
}
}
}
}
.permissions-popup, .permissions-popup,
.debug-log-window { .debug-log-window {
.modal { .modal {
background-color: transparent; background-color: transparent;
padding: 0; padding: 0;
} }
.confirmation-dialog .content {
box-shadow: 0px 0px 0px 0px;
max-width: 1000px;
margin: 0;
margin-left: auto;
margin-right: auto;
margin-top: 15px;
}
} }

View file

@ -8308,7 +8308,8 @@ button.module-image__border-overlay:focus {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
z-index: 5; // THIS Z-INDEX IS OVER NINE THOUSAND. OVER NINE THOUSAND?! THAT CAN'T BE!
z-index: 9001;
} }
&__container { &__container {

View file

@ -125,16 +125,8 @@
<button class='close'><span class='icon'></span></button> <button class='close'><span class='icon'></span></button>
</script> </script>
<script type='text/x-tmpl-mustache' id='confirmation-dialog'> <script type='text/x-tmpl-mustache' id='safety-number-change-dialog'>
<div class="content"> <div class='safety-number-change-dialog-wrapper'></div>
<div class='message'>{{ message }}</div>
<div class='buttons'>
{{ #showCancel }}
<button class='cancel' tabindex='2'>{{ cancel }}</button>
{{ /showCancel }}
<button class='ok' tabindex='1'>{{ ok }}</button>
</div>
</div>
</script> </script>
<script type='text/x-tmpl-mustache' id='identicon-svg'> <script type='text/x-tmpl-mustache' id='identicon-svg'>

View file

@ -241,15 +241,14 @@ type WhatIsThis = import('./window.d').WhatIsThis;
try { try {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const dialog = new window.Whisper.ConfirmationDialogView({ window.showConfirmationDialog({
cancelText: window.i18n('quit'),
confirmStyle: 'negative',
message: window.i18n('deleteOldIndexedDBData'), message: window.i18n('deleteOldIndexedDBData'),
okText: window.i18n('deleteOldData'), okText: window.i18n('deleteOldData'),
cancelText: window.i18n('quit'), reject: () => reject(),
resolve, resolve: () => resolve(),
reject,
}); });
document.body.append(dialog.el);
dialog.focusCancel();
}); });
} catch (error) { } catch (error) {
window.log.info( window.log.info(

View file

@ -12,9 +12,8 @@ import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
storiesOf('Components/ConfirmationDialog', module).add( storiesOf('Components/ConfirmationDialog', module)
'ConfirmationDialog', .add('ConfirmationDialog', () => {
() => {
return ( return (
<ConfirmationDialog <ConfirmationDialog
i18n={i18n} i18n={i18n}
@ -36,5 +35,23 @@ storiesOf('Components/ConfirmationDialog', module).add(
{text('Child text', 'asdf blip')} {text('Child text', 'asdf blip')}
</ConfirmationDialog> </ConfirmationDialog>
); );
} })
); .add('Custom cancel text', () => {
return (
<ConfirmationDialog
cancelText="Nah"
i18n={i18n}
onClose={action('onClose')}
title={text('Title', 'Foo bar banana baz?')}
actions={[
{
text: 'Maybe',
style: 'affirmative',
action: action('affirmative'),
},
]}
>
{text('Child text', 'asdf blip')}
</ConfirmationDialog>
);
});

View file

@ -12,11 +12,12 @@ export type ActionSpec = {
}; };
export type OwnProps = { export type OwnProps = {
readonly i18n: LocalizerType;
readonly children: React.ReactNode;
readonly title?: string | React.ReactNode;
readonly actions: Array<ActionSpec>; readonly actions: Array<ActionSpec>;
readonly cancelText?: string;
readonly children?: React.ReactNode;
readonly i18n: LocalizerType;
readonly onClose: () => unknown; readonly onClose: () => unknown;
readonly title?: string | React.ReactNode;
}; };
export type Props = OwnProps; export type Props = OwnProps;
@ -28,7 +29,7 @@ function focusRef(el: HTMLElement | null) {
} }
export const ConfirmationDialog = React.memo( export const ConfirmationDialog = React.memo(
({ i18n, onClose, children, title, actions }: Props) => { ({ i18n, onClose, cancelText, children, title, actions }: Props) => {
React.useEffect(() => { React.useEffect(() => {
const handler = ({ key }: KeyboardEvent) => { const handler = ({ key }: KeyboardEvent) => {
if (key === 'Escape') { if (key === 'Escape') {
@ -81,7 +82,7 @@ export const ConfirmationDialog = React.memo(
ref={focusRef} ref={focusRef}
className="module-confirmation-dialog__container__buttons__button" className="module-confirmation-dialog__container__buttons__button"
> >
{i18n('confirmation-dialog--Cancel')} {cancelText || i18n('confirmation-dialog--Cancel')}
</button> </button>
{actions.map((action, i) => ( {actions.map((action, i) => (
<button <button

View file

@ -0,0 +1,77 @@
// Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
// This file is here temporarily while we're switching off of Backbone into
// React. In the future, and in React-land, please just import and use
// ConfirmationModal directly. This is the thin API layer to bridge the gap
// while we convert things over. Please delete this file once all usages are
// ported over. Note: this file cannot have any imports/exports since it is
// being included in a <script /> tag.
type ConfirmationDialogViewProps = {
cancelText?: string;
confirmStyle?: 'affirmative' | 'negative';
message: string;
okText: string;
reject?: () => void;
resolve: () => void;
};
let confirmationDialogViewNode: HTMLElement | null = null;
let confirmationDialogPreviousFocus: HTMLElement | null = null;
function removeConfirmationDialog() {
if (!confirmationDialogViewNode) {
return;
}
window.ReactDOM.unmountComponentAtNode(confirmationDialogViewNode);
document.body.removeChild(confirmationDialogViewNode);
if (
confirmationDialogPreviousFocus &&
typeof confirmationDialogPreviousFocus.focus === 'function'
) {
confirmationDialogPreviousFocus.focus();
}
confirmationDialogViewNode = null;
}
function showConfirmationDialog(options: ConfirmationDialogViewProps) {
if (confirmationDialogViewNode) {
removeConfirmationDialog();
}
confirmationDialogViewNode = document.createElement('div');
document.body.appendChild(confirmationDialogViewNode);
confirmationDialogPreviousFocus = document.activeElement as HTMLElement;
window.ReactDOM.render(
// eslint-disable-next-line react/react-in-jsx-scope, react/jsx-no-undef
<window.Signal.Components.ConfirmationModal
actions={[
{
action: () => {
removeConfirmationDialog();
options.resolve();
},
style: options.confirmStyle,
text: options.okText || window.i18n('ok'),
},
]}
cancelText={options.cancelText || window.i18n('cancel')}
i18n={window.i18n}
onClose={() => {
removeConfirmationDialog();
if (options.reject) {
options.reject();
}
}}
title={options.message}
/>,
confirmationDialogViewNode
);
}
window.showConfirmationDialog = showConfirmationDialog;

View file

@ -286,15 +286,6 @@
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
}, },
{
"rule": "jQuery-appendTo(",
"path": "js/permissions_popup_start.js",
"line": "window.view.$el.appendTo($body);",
"lineNumber": 52,
"reasonCategory": "usageTrusted",
"updated": "2020-06-02T21:51:34.813Z",
"reasonDetail": "Interacting with already-existing DOM nodes"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/settings_start.js", "path": "js/settings_start.js",
@ -357,15 +348,6 @@
"updated": "2018-09-19T18:13:29.628Z", "updated": "2018-09-19T18:13:29.628Z",
"reasonDetail": "Interacting with already-existing DOM nodes" "reasonDetail": "Interacting with already-existing DOM nodes"
}, },
{
"rule": "jQuery-$(",
"path": "js/views/confirmation_dialog_view.js",
"line": " setTimeout(() => this.$('.cancel').focus(), 1);",
"lineNumber": 76,
"reasonCategory": "usageTrusted",
"updated": "2019-12-07T02:04:56.987Z",
"reasonDetail": "Protected from arbitrary input"
},
{ {
"rule": "jQuery-append(", "rule": "jQuery-append(",
"path": "js/views/contact_list_view.js", "path": "js/views/contact_list_view.js",
@ -1359,20 +1341,11 @@
"updated": "2018-09-15T00:38:04.183Z", "updated": "2018-09-15T00:38:04.183Z",
"reasonDetail": "Value set came directly from Mustache tempating engine" "reasonDetail": "Value set came directly from Mustache tempating engine"
}, },
{
"rule": "jQuery-append(",
"path": "js/views/whisper_view.js",
"line": " this.$el.append(dialog.el);",
"lineNumber": 63,
"reasonCategory": "usageTrusted",
"updated": "2018-09-19T18:13:29.628Z",
"reasonDetail": "Interacting with already-existing DOM nodes"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/whisper_view.js", "path": "js/views/whisper_view.js",
"line": " $('script[type=\"text/x-tmpl-mustache\"]').each((i, el) => {", "line": " $('script[type=\"text/x-tmpl-mustache\"]').each((i, el) => {",
"lineNumber": 72, "lineNumber": 70,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -1381,7 +1354,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/whisper_view.js", "path": "js/views/whisper_view.js",
"line": " const $el = $(el);", "line": " const $el = $(el);",
"lineNumber": 73, "lineNumber": 71,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -1390,7 +1363,7 @@
"rule": "jQuery-html(", "rule": "jQuery-html(",
"path": "js/views/whisper_view.js", "path": "js/views/whisper_view.js",
"line": " templates[id] = $el.html();", "line": " templates[id] = $el.html();",
"lineNumber": 75, "lineNumber": 73,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-15T00:38:04.183Z", "updated": "2018-09-15T00:38:04.183Z",
"reasonDetail": "Getting the value, not setting it" "reasonDetail": "Getting the value, not setting it"

View file

@ -2034,7 +2034,8 @@ Whisper.ConversationView = Whisper.View.extend({
this.$('.microphone').hide(); this.$('.microphone').hide();
}, },
handleAudioConfirm(blob: any, lostFocus: any) { handleAudioConfirm(blob: any, lostFocus: any) {
const dialog = new Whisper.ConfirmationDialogView({ window.showConfirmationDialog({
confirmStyle: 'negative',
cancelText: window.i18n('discard'), cancelText: window.i18n('discard'),
message: lostFocus message: lostFocus
? window.i18n('voiceRecordingInterruptedBlur') ? window.i18n('voiceRecordingInterruptedBlur')
@ -2044,9 +2045,6 @@ Whisper.ConversationView = Whisper.View.extend({
await this.handleAudioCapture(blob); await this.handleAudioCapture(blob);
}, },
}); });
this.$el.prepend(dialog.el);
dialog.focusCancel();
}, },
async handleAudioCapture(blob: any) { async handleAudioCapture(blob: any) {
if (this.hasFiles()) { if (this.hasFiles()) {
@ -2346,7 +2344,8 @@ Whisper.ConversationView = Whisper.View.extend({
throw new Error(`forceSend: Did not find message for id ${messageId}`); throw new Error(`forceSend: Did not find message for id ${messageId}`);
} }
const dialog = new Whisper.ConfirmationDialogView({ window.showConfirmationDialog({
confirmStyle: 'negative',
message: window.i18n('identityKeyErrorOnSend', { message: window.i18n('identityKeyErrorOnSend', {
name1: contact.getTitle(), name1: contact.getTitle(),
name2: contact.getTitle(), name2: contact.getTitle(),
@ -2367,9 +2366,6 @@ Whisper.ConversationView = Whisper.View.extend({
message.resend(contact.getSendTarget()); message.resend(contact.getSendTarget());
}, },
}); });
this.$el.prepend(dialog.el);
dialog.focusCancel();
}, },
showSafetyNumber(id: any) { showSafetyNumber(id: any) {
@ -2513,7 +2509,8 @@ Whisper.ConversationView = Whisper.View.extend({
); );
} }
const dialog = new Whisper.ConfirmationDialogView({ window.showConfirmationDialog({
confirmStyle: 'negative',
message: window.i18n('deleteWarning'), message: window.i18n('deleteWarning'),
okText: window.i18n('delete'), okText: window.i18n('delete'),
resolve: () => { resolve: () => {
@ -2530,9 +2527,6 @@ Whisper.ConversationView = Whisper.View.extend({
this.resetPanel(); this.resetPanel();
}, },
}); });
this.$el.prepend(dialog.el);
dialog.focusCancel();
}, },
deleteMessageForEveryone(messageId: string) { deleteMessageForEveryone(messageId: string) {
@ -2543,7 +2537,8 @@ Whisper.ConversationView = Whisper.View.extend({
); );
} }
const dialog = new Whisper.ConfirmationDialogView({ window.showConfirmationDialog({
confirmStyle: 'negative',
message: window.i18n('deleteForEveryoneWarning'), message: window.i18n('deleteForEveryoneWarning'),
okText: window.i18n('delete'), okText: window.i18n('delete'),
resolve: async () => { resolve: async () => {
@ -2551,9 +2546,6 @@ Whisper.ConversationView = Whisper.View.extend({
this.resetPanel(); this.resetPanel();
}, },
}); });
this.$el.prepend(dialog.el);
dialog.focusCancel();
}, },
showStickerPackPreview(packId: any, packKey: any) { showStickerPackPreview(packId: any, packKey: any) {

15
ts/window.d.ts vendored
View file

@ -64,6 +64,7 @@ import { MessageModel } from './models/messages';
import { ConversationModel } from './models/conversations'; import { ConversationModel } from './models/conversations';
import { combineNames } from './util'; import { combineNames } from './util';
import { BatcherType } from './util/batcher'; import { BatcherType } from './util/batcher';
import { ConfirmationModal } from './components/ConfirmationModal';
import { ErrorModal } from './components/ErrorModal'; import { ErrorModal } from './components/ErrorModal';
import { ProgressModal } from './components/ProgressModal'; import { ProgressModal } from './components/ProgressModal';
import { ContactModal } from './components/conversation/ContactModal'; import { ContactModal } from './components/conversation/ContactModal';
@ -74,6 +75,17 @@ type TaskResultType = any;
export type WhatIsThis = any; export type WhatIsThis = any;
// Synced with the type in ts/shims/showConfirmationDialog
// we are duplicating it here because that file cannot import/export.
type ConfirmationDialogViewProps = {
cancelText?: string;
confirmStyle?: 'affirmative' | 'negative';
message: string;
okText: string;
reject?: () => void;
resolve: () => void;
};
declare global { declare global {
interface Window { interface Window {
_: typeof Underscore; _: typeof Underscore;
@ -160,6 +172,7 @@ declare global {
setAutoHideMenuBar: (value: WhatIsThis) => void; setAutoHideMenuBar: (value: WhatIsThis) => void;
setBadgeCount: (count: number) => void; setBadgeCount: (count: number) => void;
setMenuBarVisibility: (value: WhatIsThis) => void; setMenuBarVisibility: (value: WhatIsThis) => void;
showConfirmationDialog: (options: ConfirmationDialogViewProps) => void;
showKeyboardShortcuts: () => void; showKeyboardShortcuts: () => void;
storage: { storage: {
addBlockedGroup: (group: string) => void; addBlockedGroup: (group: string) => void;
@ -397,6 +410,7 @@ declare global {
Components: { Components: {
AttachmentList: any; AttachmentList: any;
CaptionEditor: any; CaptionEditor: any;
ConfirmationModal: typeof ConfirmationModal;
ContactDetail: any; ContactDetail: any;
ErrorModal: typeof ErrorModal; ErrorModal: typeof ErrorModal;
ContactModal: typeof ContactModal; ContactModal: typeof ContactModal;
@ -600,7 +614,6 @@ export type WhisperType = {
MessageType: MessageModel; MessageType: MessageModel;
GroupMemberConversation: WhatIsThis; GroupMemberConversation: WhatIsThis;
KeyChangeListener: WhatIsThis; KeyChangeListener: WhatIsThis;
ConfirmationDialogView: WhatIsThis;
ClearDataView: WhatIsThis; ClearDataView: WhatIsThis;
ReactWrapperView: WhatIsThis; ReactWrapperView: WhatIsThis;
activeConfirmationView: WhatIsThis; activeConfirmationView: WhatIsThis;