background.ts/conversation_view.ts modules, Whisper.View/ToastView in TS

This commit is contained in:
Scott Nonnenberg 2021-02-26 13:06:37 -08:00 committed by Josh Perez
parent 2aa2aca9f2
commit d0e3a2ce29
34 changed files with 957 additions and 887 deletions

View file

@ -19,7 +19,7 @@
img-src 'self' blob: data:; img-src 'self' blob: data:;
media-src 'self' blob:; media-src 'self' blob:;
object-src 'none'; object-src 'none';
script-src 'self'; script-src 'self' 'sha256-5J9nLKMi84ERvoy7r/3XVwiW1iZ5YaPic9BNaF/0rtI=';
style-src 'self' 'unsafe-inline';" style-src 'self' 'unsafe-inline';"
> >
<title>Signal</title> <title>Signal</title>
@ -348,14 +348,11 @@
<script type='text/javascript' src='js/message_controller.js'></script> <script type='text/javascript' src='js/message_controller.js'></script>
<script type='text/javascript' src='js/views/react_wrapper_view.js'></script> <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/toast_view.js'></script>
<script type='text/javascript' src='js/views/list_view.js'></script> <script type='text/javascript' src='js/views/list_view.js'></script>
<script type='text/javascript' src='js/views/contact_list_view.js'></script> <script type='text/javascript' src='js/views/contact_list_view.js'></script>
<script type='text/javascript' src='js/views/key_verification_view.js'></script> <script type='text/javascript' src='js/views/key_verification_view.js'></script>
<script type='text/javascript' src='js/views/group_member_list_view.js'></script> <script type='text/javascript' src='js/views/group_member_list_view.js'></script>
<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='js/views/inbox_view.js'></script> <script type='text/javascript' src='js/views/inbox_view.js'></script>
<script type='text/javascript' src='ts/shims/showConfirmationDialog.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>
@ -386,6 +383,12 @@
</div> </div>
</div> </div>
<script type='text/javascript' src='ts/background.js'></script> <!--
Note: this inline script cannot be changed without also changing the hash in
the CSP at the top of this file
-->
<script type='text/javascript'>
window.startApp();
</script>
</body> </body>
</html> </html>

View file

@ -55,8 +55,6 @@
</script> </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/toast_view.js'></script>
<script type='text/javascript' src='js/views/debug_log_view.js'></script> <script type='text/javascript' src='js/views/debug_log_view.js'></script>
<script type='text/javascript' src='js/debug_log_start.js'></script> <script type='text/javascript' src='js/debug_log_start.js'></script>
</html> </html>

View file

@ -29,6 +29,9 @@ window.nodeSetImmediate = setImmediate;
window.getNodeVersion = () => config.node_version; window.getNodeVersion = () => config.node_version;
window.getEnvironment = getEnvironment; window.getEnvironment = getEnvironment;
window.Backbone = require('backbone');
require('./ts/backbone/views/whisper_view');
require('./ts/backbone/views/toast_view');
require('./ts/logging/set_up_renderer_logging'); require('./ts/logging/set_up_renderer_logging');
window.closeDebugLog = () => ipcRenderer.send('close-debug-log'); window.closeDebugLog = () => ipcRenderer.send('close-debug-log');

View file

@ -1,7 +1,7 @@
// Copyright 2017-2020 Signal Messenger, LLC // Copyright 2017-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper */ /* global Whisper, $ */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function () { (function () {
@ -9,7 +9,7 @@
Whisper.BannerView = Whisper.View.extend({ Whisper.BannerView = Whisper.View.extend({
className: 'banner', className: 'banner',
templateName: 'banner', template: () => $('#banner').html(),
events: { events: {
'click .dismiss': 'onDismiss', 'click .dismiss': 'onDismiss',
'click .body': 'onClick', 'click .body': 'onClick',

View file

@ -1,10 +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 i18n: false */ /* global i18n, Whisper, $ */
/* global Whisper: false */
/* eslint-disable no-new */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function () { (function () {
@ -16,7 +13,7 @@
DELETING: 2, DELETING: 2,
}; };
window.Whisper.ClearDataView = Whisper.View.extend({ window.Whisper.ClearDataView = Whisper.View.extend({
templateName: 'clear-data', template: () => $('#clear-data').html(),
className: 'full-screen-flow overlay', className: 'full-screen-flow overlay',
events: { events: {
'click .cancel': 'onCancel', 'click .cancel': 'onCancel',

View file

@ -1,8 +1,7 @@
// Copyright 2015-2020 Signal Messenger, LLC // Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper: false */ /* global Whisper, textsecure, $ */
/* global textsecure: false */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function () { (function () {
@ -13,7 +12,7 @@
itemView: Whisper.View.extend({ itemView: Whisper.View.extend({
tagName: 'div', tagName: 'div',
className: 'contact', className: 'contact',
templateName: 'contact', template: () => $('#contact').html(),
initialize(options) { initialize(options) {
this.ourNumber = textsecure.storage.user.getNumber(); this.ourNumber = textsecure.storage.user.getNumber();
this.listenBack = options.listenBack; this.listenBack = options.listenBack;

View file

@ -1,8 +1,7 @@
// Copyright 2015-2021 Signal Messenger, LLC // Copyright 2015-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
/* global i18n: false */ /* global i18n, Whisper, $ */
/* global Whisper: false */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function () { (function () {
@ -31,7 +30,7 @@
}); });
Whisper.DebugLogLinkView = Whisper.View.extend({ Whisper.DebugLogLinkView = Whisper.View.extend({
templateName: 'debug-log-link', template: () => $('#debug-log-link').html(),
initialize(options) { initialize(options) {
this.url = options.url; this.url = options.url;
}, },
@ -66,7 +65,7 @@
* edit them in their own editor. This is mostly a stopgap solution. * edit them in their own editor. This is mostly a stopgap solution.
*/ */
Whisper.DebugLogView = Whisper.View.extend({ Whisper.DebugLogView = Whisper.View.extend({
templateName: 'debug-log', template: () => $('#debug-log').html(),
className: 'debug-log modal', className: 'debug-log modal',
initialize() { initialize() {
this.render(); this.render();

View file

@ -1,16 +1,15 @@
// Copyright 2015-2020 Signal Messenger, LLC // Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper, i18n */ /* global Whisper, i18n, $ */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function () { (function () {
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
// TODO: take a title string which could replace the 'members' header
Whisper.GroupMemberList = Whisper.View.extend({ Whisper.GroupMemberList = Whisper.View.extend({
className: 'group-member-list panel', className: 'group-member-list panel',
templateName: 'group-member-list', template: () => $('#group-member-list').html(),
initialize(options) { initialize(options) {
this.needVerify = options.needVerify; this.needVerify = options.needVerify;

View file

@ -1,7 +1,7 @@
// Copyright 2015-2021 Signal Messenger, LLC // Copyright 2015-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper, loadImage */ /* global Whisper, loadImage, $ */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function () { (function () {
@ -11,7 +11,7 @@
* Render an avatar identicon to an svg for use in a notification. * Render an avatar identicon to an svg for use in a notification.
*/ */
Whisper.IdenticonSVGView = Whisper.View.extend({ Whisper.IdenticonSVGView = Whisper.View.extend({
templateName: 'identicon-svg', template: () => $('#identicon-svg').html(),
initialize(options) { initialize(options) {
this.render_attributes = options; this.render_attributes = options;
this.render_attributes.color = COLORS[this.render_attributes.color]; this.render_attributes.color = COLORS[this.render_attributes.color];

View file

@ -5,7 +5,8 @@
ConversationController, ConversationController,
i18n, i18n,
Whisper, Whisper,
Signal Signal,
$
*/ */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
@ -60,7 +61,7 @@
}); });
Whisper.AppLoadingScreen = Whisper.View.extend({ Whisper.AppLoadingScreen = Whisper.View.extend({
templateName: 'app-loading-screen', template: () => $('#app-loading-screen').html(),
className: 'app-loading-screen', className: 'app-loading-screen',
updateProgress(count) { updateProgress(count) {
if (count > 0) { if (count > 0) {
@ -74,7 +75,7 @@
}); });
Whisper.InboxView = Whisper.View.extend({ Whisper.InboxView = Whisper.View.extend({
templateName: 'two-column', template: () => $('#two-column').html(),
className: 'inbox index', className: 'inbox index',
initialize(options = {}) { initialize(options = {}) {
this.ready = false; this.ready = false;

View file

@ -24,7 +24,7 @@
const TOO_OLD = 409; const TOO_OLD = 409;
Whisper.InstallView = Whisper.View.extend({ Whisper.InstallView = Whisper.View.extend({
templateName: 'link-flow-template', template: () => $('#link-flow-template').html(),
className: 'main full-screen-flow', className: 'main full-screen-flow',
events: { events: {
'click .try-again': 'connect', 'click .try-again': 'connect',

View file

@ -1,9 +1,7 @@
// Copyright 2015-2020 Signal Messenger, LLC // Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
/* global Backbone, Signal, Whisper */ /* global Backbone, Signal, Whisper, $ */
/* eslint-disable more/no-then */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function () { (function () {
@ -11,7 +9,7 @@
Whisper.KeyVerificationPanelView = Whisper.View.extend({ Whisper.KeyVerificationPanelView = Whisper.View.extend({
className: 'panel', className: 'panel',
templateName: 'key-verification', template: () => $('#key-verification').html(),
initialize(options) { initialize(options) {
this.render(); this.render();

View file

@ -1,7 +1,7 @@
// Copyright 2015-2020 Signal Messenger, LLC // Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
/* global libphonenumber, Whisper */ /* global libphonenumber, Whisper, $ */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function () { (function () {
@ -10,7 +10,7 @@
Whisper.PhoneInputView = Whisper.View.extend({ Whisper.PhoneInputView = Whisper.View.extend({
tagName: 'div', tagName: 'div',
className: 'phone-input', className: 'phone-input',
templateName: 'phone-number', template: () => $('#phone-number').html(),
initialize() { initialize() {
this.$('input.number').intlTelInput(); this.$('input.number').intlTelInput();
}, },

View file

@ -11,7 +11,7 @@
Whisper.RecorderView = Whisper.View.extend({ Whisper.RecorderView = Whisper.View.extend({
className: 'recorder clearfix', className: 'recorder clearfix',
templateName: 'recorder', template: () => $('#recorder').html(),
initialize() { initialize() {
this.startTime = Date.now(); this.startTime = Date.now();
this.interval = setInterval(this.updateTime.bind(this), 1000); this.interval = setInterval(this.updateTime.bind(this), 1000);

View file

@ -1,14 +1,14 @@
// Copyright 2020 Signal Messenger, LLC // Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper, Signal */ /* global Whisper, Signal, $ */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function () { (function () {
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
Whisper.SafetyNumberChangeDialogView = Whisper.View.extend({ Whisper.SafetyNumberChangeDialogView = Whisper.View.extend({
templateName: 'safety-number-change-dialog', template: () => $('#safety-number-change-dialog').html(),
initialize(options) { initialize(options) {
const dialog = new Whisper.ReactWrapperView({ const dialog = new Whisper.ReactWrapperView({
Component: window.Signal.Components.SafetyNumberChangeDialog, Component: window.Signal.Components.SafetyNumberChangeDialog,

View file

@ -91,7 +91,7 @@
}); });
Whisper.SettingsView = Whisper.View.extend({ Whisper.SettingsView = Whisper.View.extend({
className: 'settings modal expand', className: 'settings modal expand',
templateName: 'settings', template: () => $('#settings').html(),
initialize() { initialize() {
this.render(); this.render();
new RadioButtonGroupView({ new RadioButtonGroupView({
@ -271,7 +271,7 @@
}); });
const SyncView = Whisper.View.extend({ const SyncView = Whisper.View.extend({
templateName: 'syncSettings', template: () => $('#syncSettings').html(),
className: 'syncSettings', className: 'syncSettings',
events: { events: {
'click .sync': 'sync', 'click .sync': 'sync',

View file

@ -10,7 +10,7 @@
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
Whisper.StandaloneRegistrationView = Whisper.View.extend({ Whisper.StandaloneRegistrationView = Whisper.View.extend({
templateName: 'standalone', template: () => $('#standalone').html(),
className: 'full-screen-flow', className: 'full-screen-flow',
initialize() { initialize() {
window.readyForUpdates(); window.readyForUpdates();

View file

@ -1,40 +0,0 @@
// Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper, Mustache, _ */
// eslint-disable-next-line func-names
(function () {
window.Whisper = window.Whisper || {};
Whisper.ToastView = Whisper.View.extend({
className: 'toast',
templateName: 'toast',
initialize() {
this.$el.hide();
this.timeout = 2000;
},
close() {
this.$el.fadeOut(this.remove.bind(this));
},
render() {
this.$el.html(
Mustache.render(
_.result(this, 'template', ''),
_.result(this, 'render_attributes', '')
)
);
this.$el.attr('tabIndex', 0);
this.$el.show();
setTimeout(this.close.bind(this), this.timeout);
},
});
Whisper.ToastView.show = (View, el) => {
const toast = new View();
toast.$el.appendTo(el);
toast.render();
};
})();

View file

@ -1,79 +0,0 @@
// Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper, Backbone, Mustache, _, $ */
/*
* Whisper.View
*
* This is the base for most of our views. The Backbone view is extended
* with some conveniences:
*
* 1. Pre-parses all our mustache templates for performance.
* https://github.com/janl/mustache.js#pre-parsing-and-caching-templates
*
* 2. Defines a default definition for render() which allows sub-classes
* to simply specify a templateName and renderAttributes which are plugged
* into Mustache.render
*
* 3. Makes all the templates available for rendering as partials.
* https://github.com/janl/mustache.js#partials
*
* 4. Provides some common functionality, e.g. confirmation dialog
*
*/
// eslint-disable-next-line func-names
(function () {
window.Whisper = window.Whisper || {};
Whisper.View = Backbone.View.extend(
{
constructor(...params) {
Backbone.View.call(this, ...params);
Mustache.parse(_.result(this, 'template'));
},
render_attributes() {
return _.result(this.model, 'attributes', {});
},
render_partials() {
return Whisper.View.Templates;
},
template() {
if (this.templateName) {
return Whisper.View.Templates[this.templateName];
}
return '';
},
render() {
const attrs = _.result(this, 'render_attributes', {});
const template = _.result(this, 'template', '');
const partials = _.result(this, 'render_partials', '');
this.$el.html(Mustache.render(template, attrs, partials));
return this;
},
confirm(message, okText) {
return new Promise((resolve, reject) => {
window.showConfirmationDialog({
message,
okText,
resolve,
reject,
});
});
},
},
{
// Class attributes
Templates: (() => {
const templates = {};
$('script[type="text/x-tmpl-mustache"]').each((i, el) => {
const $el = $(el);
const id = $el.attr('id');
templates[id] = $el.html();
});
return templates;
})(),
}
);
})();

View file

@ -25,7 +25,6 @@
</body> </body>
<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='ts/shims/showConfirmationDialog.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

@ -520,6 +520,11 @@ try {
require('./ts/models/messages'); require('./ts/models/messages');
require('./ts/models/conversations'); require('./ts/models/conversations');
require('./ts/backbone/views/whisper_view');
require('./ts/backbone/views/toast_view');
require('./ts/views/conversation_view');
require('./ts/background');
function wrapWithPromise(fn) { function wrapWithPromise(fn) {
return (...args) => Promise.resolve(fn(...args)); return (...args) => Promise.resolve(fn(...args));
} }

View file

@ -168,7 +168,6 @@
</script> </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/settings_view.js'></script> <script type='text/javascript' src='js/views/settings_view.js'></script>
<script type='text/javascript' src='js/settings_start.js'></script> <script type='text/javascript' src='js/settings_start.js'></script>
</html> </html>

View file

@ -126,6 +126,7 @@ function makeSetter(name) {
}); });
} }
require('./ts/logging/set_up_renderer_logging');
window.Backbone = require('backbone'); window.Backbone = require('backbone');
require('./ts/backbone/views/whisper_view');
require('./ts/backbone/views/toast_view');
require('./ts/logging/set_up_renderer_logging');

View file

@ -0,0 +1,35 @@
// Copyright 2015-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
window.Whisper = window.Whisper || {};
window.Whisper.ToastView = window.Whisper.View.extend({
className: 'toast',
template: () => $('#toast').html(),
initialize() {
this.$el.hide();
this.timeout = 2000;
},
close() {
this.$el.fadeOut(this.remove.bind(this));
},
render() {
this.$el.html(
window.Mustache.render(
window._.result(this, 'template', ''),
window._.result(this, 'render_attributes', '')
)
);
this.$el.attr('tabIndex', 0);
this.$el.show();
setTimeout(this.close.bind(this), this.timeout);
},
});
window.Whisper.ToastView.show = (View, el) => {
const toast = new View();
toast.$el.appendTo(el);
toast.render();
};

View file

@ -0,0 +1,32 @@
// Copyright 2015-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/*
* Defines a default definition for render() which allows sub-classes
* to simply specify a template property and renderAttributes which are plugged
* into Mustache.render
*/
// eslint-disable-next-line func-names
(function () {
window.Whisper = window.Whisper || {};
window.Whisper.View = Backbone.View.extend({
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
constructor(...params: Array<any>) {
window.Backbone.View.call(this, ...params);
// Checks for syntax errors
window.Mustache.parse(_.result(this, 'template'));
},
render_attributes() {
return _.result(this.model, 'attributes', {});
},
render() {
const attrs = window._.result(this, 'render_attributes', {});
const template = window._.result(this, 'template', '');
this.$el.html(window.Mustache.render(template, attrs));
return this;
},
});
})();

View file

@ -1,15 +1,10 @@
// Copyright 2020-2021 Signal Messenger, LLC // Copyright 2020-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
// This allows us to pull in types despite the fact that this is not a module. We can't import { DataMessageClass } from './textsecure.d';
// use normal import syntax, nor can we use 'import type' syntax, or this will be turned import { WhatIsThis } from './window.d';
// into a module, and we'll get the dreaded 'exports is not defined' error.
// see https://github.com/microsoft/TypeScript/issues/41562
type DataMessageClass = import('./textsecure.d').DataMessageClass;
type WhatIsThis = import('./window.d').WhatIsThis;
// eslint-disable-next-line func-names export async function startApp(): Promise<void> {
(async function () {
const eventHandlerQueue = new window.PQueue({ const eventHandlerQueue = new window.PQueue({
concurrency: 1, concurrency: 1,
timeout: 1000 * 60 * 2, timeout: 1000 * 60 * 2,
@ -23,7 +18,7 @@ type WhatIsThis = import('./window.d').WhatIsThis;
wait: 500, wait: 500,
maxSize: 500, maxSize: 500,
processBatch: async (items: WhatIsThis) => { processBatch: async (items: WhatIsThis) => {
const byConversationId = _.groupBy(items, item => const byConversationId = window._.groupBy(items, item =>
window.ConversationController.ensureContactIds({ window.ConversationController.ensureContactIds({
e164: item.source, e164: item.source,
uuid: item.sourceUuid, uuid: item.sourceUuid,
@ -205,12 +200,12 @@ type WhatIsThis = import('./window.d').WhatIsThis;
if (messageReceiver) { if (messageReceiver) {
return messageReceiver.getStatus(); return messageReceiver.getStatus();
} }
if (_.isNumber(preMessageReceiverStatus)) { if (window._.isNumber(preMessageReceiverStatus)) {
return preMessageReceiverStatus; return preMessageReceiverStatus;
} }
return WebSocket.CLOSED; return WebSocket.CLOSED;
}; };
window.Whisper.events = _.clone(window.Backbone.Events); window.Whisper.events = window._.clone(window.Backbone.Events);
let accountManager: typeof window.textsecure.AccountManager; let accountManager: typeof window.textsecure.AccountManager;
window.getAccountManager = () => { window.getAccountManager = () => {
if (!accountManager) { if (!accountManager) {
@ -2632,7 +2627,7 @@ type WhatIsThis = import('./window.d').WhatIsThis;
sentTo = data.unidentifiedStatus.map( sentTo = data.unidentifiedStatus.map(
(item: WhatIsThis) => item.destinationUuid || item.destination (item: WhatIsThis) => item.destinationUuid || item.destination
); );
const unidentified = _.filter(data.unidentifiedStatus, item => const unidentified = window._.filter(data.unidentifiedStatus, item =>
Boolean(item.unidentified) Boolean(item.unidentified)
); );
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
@ -3284,4 +3279,6 @@ type WhatIsThis = import('./window.d').WhatIsThis;
// Note: We don't wait for completion here // Note: We don't wait for completion here
window.Whisper.DeliveryReceipts.onReceipt(receipt); window.Whisper.DeliveryReceipts.onReceipt(receipt);
} }
})(); }
window.startApp = startApp;

View file

@ -61,7 +61,7 @@ export async function joinViaLink(hash: string): Promise<void> {
window.reduxActions.conversations.openConversationInternal({ window.reduxActions.conversations.openConversationInternal({
conversationId: existingConversation.id, conversationId: existingConversation.id,
}); });
window.window.Whisper.ToastView.show( window.Whisper.ToastView.show(
window.Whisper.AlreadyGroupMemberToast, window.Whisper.AlreadyGroupMemberToast,
document.getElementsByClassName('conversation-stack')[0] document.getElementsByClassName('conversation-stack')[0]
); );
@ -333,7 +333,7 @@ export async function joinViaLink(hash: string): Promise<void> {
window.log.info(`joinViaLink/${logId}: Showing modal`); window.log.info(`joinViaLink/${logId}: Showing modal`);
let groupV2InfoDialog = new Whisper.ReactWrapperView({ let groupV2InfoDialog = new window.Whisper.ReactWrapperView({
className: 'group-v2-join-dialog-wrapper', className: 'group-v2-join-dialog-wrapper',
JSX: window.Signal.State.Roots.createGroupV2JoinModal(window.reduxStore, { JSX: window.Signal.State.Roots.createGroupV2JoinModal(window.reduxStore, {
join, join,

View file

@ -4052,7 +4052,7 @@ export class ConversationModel extends window.Backbone.Model<
this.set({ left: true }); this.set({ left: true });
window.Signal.Data.updateConversation(this.attributes); window.Signal.Data.updateConversation(this.attributes);
const model = new Whisper.Message(({ const model = new window.Whisper.Message(({
group_update: { left: 'You' }, group_update: { left: 'You' },
conversationId: this.id, conversationId: this.id,
type: 'outgoing', type: 'outgoing',
@ -4062,7 +4062,7 @@ export class ConversationModel extends window.Backbone.Model<
} as unknown) as MessageAttributesType); } as unknown) as MessageAttributesType);
const id = await window.Signal.Data.saveMessage(model.attributes, { const id = await window.Signal.Data.saveMessage(model.attributes, {
Message: Whisper.Message, Message: window.Whisper.Message,
}); });
model.set({ id }); model.set({ id });
@ -5128,13 +5128,13 @@ window.Whisper.ConversationCollection = window.Backbone.Collection.extend({
// We create a new model if it's not already a model // We create a new model if it's not already a model
if (!item.get) { if (!item.get) {
hydratedData.push(new Whisper.Conversation(item)); hydratedData.push(new window.Whisper.Conversation(item));
} else { } else {
hydratedData.push(item); hydratedData.push(item);
} }
} }
} else if (!data.get) { } else if (!data.get) {
hydratedData = new Whisper.Conversation(data); hydratedData = new window.Whisper.Conversation(data);
} else { } else {
hydratedData = data; hydratedData = data;
} }

View file

@ -13,7 +13,7 @@ type ConfirmationDialogViewProps = {
confirmStyle?: 'affirmative' | 'negative'; confirmStyle?: 'affirmative' | 'negative';
message: string; message: string;
okText: string; okText: string;
reject?: () => void; reject?: (error: Error) => void;
resolve: () => void; resolve: () => void;
}; };
@ -65,7 +65,7 @@ function showConfirmationDialog(options: ConfirmationDialogViewProps) {
onClose={() => { onClose={() => {
removeConfirmationDialog(); removeConfirmationDialog();
if (options.reject) { if (options.reject) {
options.reject(); options.reject(new Error('showConfirmationDialog: onClose called'));
} }
}} }}
title={options.message} title={options.message}

View file

@ -22,7 +22,7 @@ describe('Message', () => {
const ourUuid = window.getGuid(); const ourUuid = window.getGuid();
function createMessage(attrs: { [key: string]: unknown }) { function createMessage(attrs: { [key: string]: unknown }) {
const messages = new Whisper.MessageCollection(); const messages = new window.Whisper.MessageCollection();
return messages.add(attrs); return messages.add(attrs);
} }
@ -136,13 +136,13 @@ describe('Message', () => {
describe('getContact', () => { describe('getContact', () => {
it('gets outgoing contact', () => { it('gets outgoing contact', () => {
const messages = new Whisper.MessageCollection(); const messages = new window.Whisper.MessageCollection();
const message = messages.add(attributes); const message = messages.add(attributes);
message.getContact(); message.getContact();
}); });
it('gets incoming contact', () => { it('gets incoming contact', () => {
const messages = new Whisper.MessageCollection(); const messages = new window.Whisper.MessageCollection();
const message = messages.add({ const message = messages.add({
type: 'incoming', type: 'incoming',
source, source,
@ -153,7 +153,7 @@ describe('Message', () => {
describe('isIncoming', () => { describe('isIncoming', () => {
it('checks if is incoming message', () => { it('checks if is incoming message', () => {
const messages = new Whisper.MessageCollection(); const messages = new window.Whisper.MessageCollection();
let message = messages.add(attributes); let message = messages.add(attributes);
assert.notOk(message.isIncoming()); assert.notOk(message.isIncoming());
message = messages.add({ type: 'incoming' }); message = messages.add({ type: 'incoming' });
@ -163,7 +163,7 @@ describe('Message', () => {
describe('isOutgoing', () => { describe('isOutgoing', () => {
it('checks if is outgoing message', () => { it('checks if is outgoing message', () => {
const messages = new Whisper.MessageCollection(); const messages = new window.Whisper.MessageCollection();
let message = messages.add(attributes); let message = messages.add(attributes);
assert.ok(message.isOutgoing()); assert.ok(message.isOutgoing());
message = messages.add({ type: 'incoming' }); message = messages.add({ type: 'incoming' });
@ -173,7 +173,7 @@ describe('Message', () => {
describe('isGroupUpdate', () => { describe('isGroupUpdate', () => {
it('checks if is group update', () => { it('checks if is group update', () => {
const messages = new Whisper.MessageCollection(); const messages = new window.Whisper.MessageCollection();
let message = messages.add(attributes); let message = messages.add(attributes);
assert.notOk(message.isGroupUpdate()); assert.notOk(message.isGroupUpdate());
@ -584,7 +584,7 @@ describe('Message', () => {
describe('isEndSession', () => { describe('isEndSession', () => {
it('checks if it is end of the session', () => { it('checks if it is end of the session', () => {
const messages = new Whisper.MessageCollection(); const messages = new window.Whisper.MessageCollection();
let message = messages.add(attributes); let message = messages.add(attributes);
assert.notOk(message.isEndSession()); assert.notOk(message.isEndSession());
@ -596,7 +596,7 @@ describe('Message', () => {
describe('MessageCollection', () => { describe('MessageCollection', () => {
it('should be ordered oldest to newest', () => { it('should be ordered oldest to newest', () => {
const messages = new Whisper.MessageCollection(); const messages = new window.Whisper.MessageCollection();
// Timestamps // Timestamps
const today = Date.now(); const today = Date.now();
const tomorrow = today + 12345; const tomorrow = today + 12345;

File diff suppressed because it is too large Load diff

View file

@ -16,14 +16,14 @@ export async function longRunningTaskWrapper<T>({
const ONE_SECOND = 1000; const ONE_SECOND = 1000;
const TWO_SECONDS = 2000; const TWO_SECONDS = 2000;
let progressView: typeof Whisper.ReactWrapperView | undefined; let progressView: typeof window.Whisper.ReactWrapperView | undefined;
let spinnerStart; let spinnerStart;
let progressTimeout: NodeJS.Timeout | undefined = setTimeout(() => { let progressTimeout: NodeJS.Timeout | undefined = setTimeout(() => {
window.log.info(`longRunningTaskWrapper/${idLog}: Creating spinner`); window.log.info(`longRunningTaskWrapper/${idLog}: Creating spinner`);
// Note: this component uses a portal to render itself into the top-level DOM. No // Note: this component uses a portal to render itself into the top-level DOM. No
// need to attach it to the DOM here. // need to attach it to the DOM here.
progressView = new Whisper.ReactWrapperView({ progressView = new window.Whisper.ReactWrapperView({
className: 'progress-modal-wrapper', className: 'progress-modal-wrapper',
Component: window.Signal.Components.ProgressModal, Component: window.Signal.Components.ProgressModal,
}); });
@ -76,7 +76,7 @@ export async function longRunningTaskWrapper<T>({
// Note: this component uses a portal to render itself into the top-level DOM. No // Note: this component uses a portal to render itself into the top-level DOM. No
// need to attach it to the DOM here. // need to attach it to the DOM here.
const errorView = new Whisper.ReactWrapperView({ const errorView = new window.Whisper.ReactWrapperView({
className: 'error-modal-wrapper', className: 'error-modal-wrapper',
Component: window.Signal.Components.ErrorModal, Component: window.Signal.Components.ErrorModal,
props: { props: {

View file

@ -3,14 +3,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
// This allows us to pull in types despite the fact that this is not a module. We can't import { AttachmentType } from '../types/Attachment';
// use normal import syntax, nor can we use 'import type' syntax, or this will be turned import { GroupV2PendingMemberType } from '../model-types.d';
// into a module, and we'll get the dreaded 'exports is not defined' error. import { MediaItemType } from '../components/LightboxGallery';
// see https://github.com/microsoft/TypeScript/issues/41562 import { MessageType } from '../state/ducks/conversations';
type AttachmentType = import('../types/Attachment').AttachmentType;
type GroupV2PendingMemberType = import('../model-types.d').GroupV2PendingMemberType;
type MediaItemType = import('../components/LightboxGallery').MediaItemType;
type MessageType = import('../state/ducks/conversations').MessageType;
type GetLinkPreviewImageResult = { type GetLinkPreviewImageResult = {
data: ArrayBuffer; data: ArrayBuffer;
@ -250,7 +246,7 @@ Whisper.MessageBodyTooLongToast = Whisper.ToastView.extend({
}); });
Whisper.FileSizeToast = Whisper.ToastView.extend({ Whisper.FileSizeToast = Whisper.ToastView.extend({
templateName: 'file-size-modal', template: () => $('#file-size-modal').html(),
render_attributes() { render_attributes() {
return { return {
'file-size-warning': window.i18n('fileSizeWarning'), 'file-size-warning': window.i18n('fileSizeWarning'),
@ -267,31 +263,31 @@ Whisper.UnableToLoadToast = Whisper.ToastView.extend({
}); });
Whisper.DangerousFileTypeToast = Whisper.ToastView.extend({ Whisper.DangerousFileTypeToast = Whisper.ToastView.extend({
template: window.i18n('dangerousFileType'), template: () => window.i18n('dangerousFileType'),
}); });
Whisper.OneNonImageAtATimeToast = Whisper.ToastView.extend({ Whisper.OneNonImageAtATimeToast = Whisper.ToastView.extend({
template: window.i18n('oneNonImageAtATimeToast'), template: () => window.i18n('oneNonImageAtATimeToast'),
}); });
Whisper.CannotMixImageAndNonImageAttachmentsToast = Whisper.ToastView.extend({ Whisper.CannotMixImageAndNonImageAttachmentsToast = Whisper.ToastView.extend({
template: window.i18n('cannotMixImageAndNonImageAttachments'), template: () => window.i18n('cannotMixImageAndNonImageAttachments'),
}); });
Whisper.MaxAttachmentsToast = Whisper.ToastView.extend({ Whisper.MaxAttachmentsToast = Whisper.ToastView.extend({
template: window.i18n('maximumAttachments'), template: () => window.i18n('maximumAttachments'),
}); });
Whisper.AlreadyGroupMemberToast = Whisper.ToastView.extend({ Whisper.AlreadyGroupMemberToast = Whisper.ToastView.extend({
template: window.i18n('GroupV2--join--already-in-group'), template: () => window.i18n('GroupV2--join--already-in-group'),
}); });
Whisper.AlreadyRequestedToJoinToast = Whisper.ToastView.extend({ Whisper.AlreadyRequestedToJoinToast = Whisper.ToastView.extend({
template: window.i18n('GroupV2--join--already-awaiting-approval'), template: () => window.i18n('GroupV2--join--already-awaiting-approval'),
}); });
Whisper.ConversationLoadingScreen = Whisper.View.extend({ Whisper.ConversationLoadingScreen = Whisper.View.extend({
templateName: 'conversation-loading-screen', template: () => $('#conversation-loading-screen').html(),
className: 'conversation-loading-screen', className: 'conversation-loading-screen',
}); });
@ -302,7 +298,7 @@ Whisper.ConversationView = Whisper.View.extend({
id() { id() {
return `conversation-${this.model.cid}`; return `conversation-${this.model.cid}`;
}, },
template: $('#conversation').html(), template: () => $('#conversation').html(),
render_attributes() { render_attributes() {
return { return {
'send-message': window.i18n('sendMessage'), 'send-message': window.i18n('sendMessage'),
@ -374,31 +370,37 @@ Whisper.ConversationView = Whisper.View.extend({
this.downloadNewVersion this.downloadNewVersion
); );
this.lazyUpdateVerified = _.debounce( this.lazyUpdateVerified = window._.debounce(
this.model.updateVerified.bind(this.model), this.model.updateVerified.bind(this.model),
1000 // one second 1000 // one second
); );
this.model.throttledGetProfiles = this.model.throttledGetProfiles =
this.model.throttledGetProfiles || this.model.throttledGetProfiles ||
_.throttle(this.model.getProfiles.bind(this.model), FIVE_MINUTES); window._.throttle(this.model.getProfiles.bind(this.model), FIVE_MINUTES);
this.model.throttledUpdateSharedGroups = this.model.throttledUpdateSharedGroups =
this.model.throttledUpdateSharedGroups || this.model.throttledUpdateSharedGroups ||
_.throttle(this.model.updateSharedGroups.bind(this.model), FIVE_MINUTES); window._.throttle(
this.model.updateSharedGroups.bind(this.model),
FIVE_MINUTES
);
this.model.throttledFetchLatestGroupV2Data = this.model.throttledFetchLatestGroupV2Data =
this.model.throttledFetchLatestGroupV2Data || this.model.throttledFetchLatestGroupV2Data ||
_.throttle( window._.throttle(
this.model.fetchLatestGroupV2Data.bind(this.model), this.model.fetchLatestGroupV2Data.bind(this.model),
FIVE_MINUTES FIVE_MINUTES
); );
this.model.throttledMaybeMigrateV1Group = this.model.throttledMaybeMigrateV1Group =
this.model.throttledMaybeMigrateV1Group || this.model.throttledMaybeMigrateV1Group ||
_.throttle(this.model.maybeMigrateV1Group.bind(this.model), FIVE_MINUTES); window._.throttle(
this.model.maybeMigrateV1Group.bind(this.model),
FIVE_MINUTES
);
this.debouncedMaybeGrabLinkPreview = _.debounce( this.debouncedMaybeGrabLinkPreview = window._.debounce(
this.maybeGrabLinkPreview.bind(this), this.maybeGrabLinkPreview.bind(this),
200 200
); );
this.debouncedSaveDraft = _.debounce(this.saveDraft.bind(this), 200); this.debouncedSaveDraft = window._.debounce(this.saveDraft.bind(this), 200);
this.render(); this.render();
@ -1506,7 +1508,7 @@ Whisper.ConversationView = Whisper.View.extend({
const draftAttachments = this.model.get('draftAttachments') || []; const draftAttachments = this.model.get('draftAttachments') || [];
this.model.set({ this.model.set({
draftAttachments: _.reject( draftAttachments: window._.reject(
draftAttachments, draftAttachments,
item => item.path === attachment.path item => item.path === attachment.path
), ),
@ -1553,7 +1555,7 @@ Whisper.ConversationView = Whisper.View.extend({
} }
const draftAttachments = this.model.get('draftAttachments') || []; const draftAttachments = this.model.get('draftAttachments') || [];
const files = _.compact( const files = window._.compact(
await Promise.all( await Promise.all(
draftAttachments.map((attachment: any) => this.getFile(attachment)) draftAttachments.map((attachment: any) => this.getFile(attachment))
) )
@ -1575,7 +1577,7 @@ Whisper.ConversationView = Whisper.View.extend({
} }
return { return {
..._.pick(attachment, [ ...window._.pick(attachment, [
'contentType', 'contentType',
'fileName', 'fileName',
'size', 'size',
@ -1620,14 +1622,14 @@ Whisper.ConversationView = Whisper.View.extend({
if (toWrite.data) { if (toWrite.data) {
const path = await writeNewDraftData(toWrite.data); const path = await writeNewDraftData(toWrite.data);
toWrite = { toWrite = {
..._.omit(toWrite, ['data']), ...window._.omit(toWrite, ['data']),
path, path,
}; };
} }
if (toWrite.screenshotData) { if (toWrite.screenshotData) {
const screenshotPath = await writeNewDraftData(toWrite.screenshotData); const screenshotPath = await writeNewDraftData(toWrite.screenshotData);
toWrite = { toWrite = {
..._.omit(toWrite, ['screenshotData']), ...window._.omit(toWrite, ['screenshotData']),
screenshotPath, screenshotPath,
}; };
} }
@ -3168,8 +3170,10 @@ Whisper.ConversationView = Whisper.View.extend({
}, },
async destroyMessages() { async destroyMessages() {
try { window.showConfirmationDialog({
await this.confirm(window.i18n('deleteConversationConfirmation')); message: window.i18n('deleteConversationConfirmation'),
okText: window.i18n('delete'),
resolve: () => {
this.longRunningTaskWrapper({ this.longRunningTaskWrapper({
name: 'destroymessages', name: 'destroymessages',
task: async () => { task: async () => {
@ -3178,9 +3182,11 @@ Whisper.ConversationView = Whisper.View.extend({
this.model.updateLastMessage(); this.model.updateLastMessage();
}, },
}); });
} catch (error) { },
// nothing to see here, user canceled out of dialog reject: () => {
} window.log.info('destroyMessages: User canceled delete');
},
});
}, },
async isCallSafe() { async isCallSafe() {
@ -3981,7 +3987,7 @@ Whisper.ConversationView = Whisper.View.extend({
// We eliminate the ObjectURL here, unneeded for send or save // We eliminate the ObjectURL here, unneeded for send or save
return { return {
...item, ...item,
image: _.omit(item.image, 'url'), image: window._.omit(item.image, 'url'),
}; };
} }

66
ts/window.d.ts vendored
View file

@ -107,7 +107,7 @@ type ConfirmationDialogViewProps = {
confirmStyle?: 'affirmative' | 'negative'; confirmStyle?: 'affirmative' | 'negative';
message: string; message: string;
okText: string; okText: string;
reject?: () => void; reject?: (error: Error) => void;
resolve: () => void; resolve: () => void;
}; };
@ -115,6 +115,8 @@ declare global {
// We want to extend `window`'s properties, so we need an interface. // We want to extend `window`'s properties, so we need an interface.
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
interface Window { interface Window {
startApp: () => void;
_: typeof Underscore; _: typeof Underscore;
$: typeof jQuery; $: typeof jQuery;
@ -127,6 +129,10 @@ declare global {
PQueue: typeof PQueue; PQueue: typeof PQueue;
PQueueType: PQueue; PQueueType: PQueue;
Mustache: {
render: (template: string, data: any, partials?: any) => string;
parse: (template: string) => void;
};
WhatIsThis: WhatIsThis; WhatIsThis: WhatIsThis;
@ -655,7 +661,7 @@ export type WhisperType = {
ClearDataView: WhatIsThis; ClearDataView: WhatIsThis;
ReactWrapperView: WhatIsThis; ReactWrapperView: WhatIsThis;
activeConfirmationView: WhatIsThis; activeConfirmationView: WhatIsThis;
ToastView: typeof Whisper.View & { ToastView: typeof window.Whisper.View & {
show: (view: typeof Backbone.View, el: Element) => void; show: (view: typeof Backbone.View, el: Element) => void;
}; };
ConversationArchivedToast: WhatIsThis; ConversationArchivedToast: WhatIsThis;
@ -731,33 +737,35 @@ export type WhisperType = {
deliveryReceiptBatcher: BatcherType<WhatIsThis>; deliveryReceiptBatcher: BatcherType<WhatIsThis>;
RotateSignedPreKeyListener: WhatIsThis; RotateSignedPreKeyListener: WhatIsThis;
AlreadyGroupMemberToast: typeof Whisper.ToastView; AlreadyGroupMemberToast: typeof window.Whisper.ToastView;
AlreadyRequestedToJoinToast: typeof Whisper.ToastView; AlreadyRequestedToJoinToast: typeof window.Whisper.ToastView;
BlockedGroupToast: typeof Whisper.ToastView; BlockedGroupToast: typeof window.Whisper.ToastView;
BlockedToast: typeof Whisper.ToastView; BlockedToast: typeof window.Whisper.ToastView;
CannotMixImageAndNonImageAttachmentsToast: typeof Whisper.ToastView; CannotMixImageAndNonImageAttachmentsToast: typeof window.Whisper.ToastView;
DangerousFileTypeToast: typeof Whisper.ToastView; DangerousFileTypeToast: typeof window.Whisper.ToastView;
ExpiredToast: typeof Whisper.ToastView; ExpiredToast: typeof window.Whisper.ToastView;
FileSavedToast: typeof Whisper.ToastView; FileSavedToast: typeof window.Whisper.ToastView;
FileSizeToast: any; FileSizeToast: any;
FoundButNotLoadedToast: typeof Whisper.ToastView; FoundButNotLoadedToast: typeof window.Whisper.ToastView;
InvalidConversationToast: typeof Whisper.ToastView; InvalidConversationToast: typeof window.Whisper.ToastView;
LeftGroupToast: typeof Whisper.ToastView; LeftGroupToast: typeof window.Whisper.ToastView;
MaxAttachmentsToast: typeof Whisper.ToastView; MaxAttachmentsToast: typeof window.Whisper.ToastView;
MessageBodyTooLongToast: typeof Whisper.ToastView; MessageBodyTooLongToast: typeof window.Whisper.ToastView;
OneNonImageAtATimeToast: typeof Whisper.ToastView; OneNonImageAtATimeToast: typeof window.Whisper.ToastView;
OriginalNoLongerAvailableToast: typeof Whisper.ToastView; OriginalNoLongerAvailableToast: typeof window.Whisper.ToastView;
OriginalNotFoundToast: typeof Whisper.ToastView; OriginalNotFoundToast: typeof window.Whisper.ToastView;
PinnedConversationsFullToast: typeof Whisper.ToastView; PinnedConversationsFullToast: typeof window.Whisper.ToastView;
ReactionFailedToast: typeof Whisper.ToastView; ReactionFailedToast: typeof window.Whisper.ToastView;
TapToViewExpiredIncomingToast: typeof Whisper.ToastView; TapToViewExpiredIncomingToast: typeof window.Whisper.ToastView;
TapToViewExpiredOutgoingToast: typeof Whisper.ToastView; TapToViewExpiredOutgoingToast: typeof window.Whisper.ToastView;
TimerConflictToast: typeof Whisper.ToastView; TimerConflictToast: typeof window.Whisper.ToastView;
UnableToLoadToast: typeof Whisper.ToastView; UnableToLoadToast: typeof window.Whisper.ToastView;
VoiceNoteLimit: typeof Whisper.ToastView; VoiceNoteLimit: typeof window.Whisper.ToastView;
VoiceNoteMustBeOnlyAttachmentToast: typeof Whisper.ToastView; VoiceNoteMustBeOnlyAttachmentToast: typeof window.Whisper.ToastView;
ConversationLoadingScreen: typeof Whisper.View; ConversationLoadingScreen: typeof window.Whisper.View;
ConversationView: typeof Whisper.View; ConversationView: typeof window.Whisper.View;
View: typeof Backbone.View; View: typeof Backbone.View & {
Templates: Record<string, string>;
};
}; };