New top-level React root: <App />
This commit is contained in:
parent
9a1f722545
commit
173771d34b
22 changed files with 457 additions and 266 deletions
|
@ -258,7 +258,7 @@ and [iOS](https://github.com/signalapp/Signal-iOS/blob/master/BUILDING.md) proje
|
||||||
|
|
||||||
Then you can set up your development build of Signal Desktop as normal. If you've already
|
Then you can set up your development build of Signal Desktop as normal. If you've already
|
||||||
set up as a standalone install, you can switch by opening the DevTools (View -> Toggle
|
set up as a standalone install, you can switch by opening the DevTools (View -> Toggle
|
||||||
Developer Tools) and entering this into the Console and pressing enter: `window.owsDesktopApp.appView.openInstaller();`
|
Developer Tools) and entering this into the Console and pressing enter: `window.reduxActions.app.openInstaller();`
|
||||||
|
|
||||||
## Changing to production
|
## Changing to production
|
||||||
|
|
||||||
|
|
|
@ -333,7 +333,7 @@
|
||||||
<script type='text/javascript' src='js/reliable_trigger.js'></script>
|
<script type='text/javascript' src='js/reliable_trigger.js'></script>
|
||||||
<script type='text/javascript' src='js/database.js'></script>
|
<script type='text/javascript' src='js/database.js'></script>
|
||||||
<script type='text/javascript' src='js/storage.js'></script>
|
<script type='text/javascript' src='js/storage.js'></script>
|
||||||
|
|
||||||
<script type='text/javascript' src='libtextsecure/protocol_wrapper.js'></script>
|
<script type='text/javascript' src='libtextsecure/protocol_wrapper.js'></script>
|
||||||
<script type='text/javascript' src='libtextsecure/storage/user.js'></script>
|
<script type='text/javascript' src='libtextsecure/storage/user.js'></script>
|
||||||
<script type='text/javascript' src='libtextsecure/storage/unprocessed.js'></script>
|
<script type='text/javascript' src='libtextsecure/storage/unprocessed.js'></script>
|
||||||
|
@ -368,7 +368,6 @@
|
||||||
<script type="text/javascript" src="js/views/phone-input-view.js"></script>
|
<script type="text/javascript" src="js/views/phone-input-view.js"></script>
|
||||||
<script type='text/javascript' src='js/views/safety_number_change_dialog_view.js'></script>
|
<script type='text/javascript' src='js/views/safety_number_change_dialog_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/standalone_registration_view.js'></script>
|
<script type='text/javascript' src='js/views/standalone_registration_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/app_view.js'></script>
|
|
||||||
<script type='text/javascript' src='js/views/clear_data_view.js'></script>
|
<script type='text/javascript' src='js/views/clear_data_view.js'></script>
|
||||||
|
|
||||||
<script type='text/javascript' src='js/wall_clock_listener.js'></script>
|
<script type='text/javascript' src='js/wall_clock_listener.js'></script>
|
||||||
|
|
|
@ -80,6 +80,7 @@ const {
|
||||||
const {
|
const {
|
||||||
createConversationHeader,
|
createConversationHeader,
|
||||||
} = require('../../ts/state/roots/createConversationHeader');
|
} = require('../../ts/state/roots/createConversationHeader');
|
||||||
|
const { createApp } = require('../../ts/state/roots/createApp');
|
||||||
const { createCallManager } = require('../../ts/state/roots/createCallManager');
|
const { createCallManager } = require('../../ts/state/roots/createCallManager');
|
||||||
const {
|
const {
|
||||||
createForwardMessageModal,
|
createForwardMessageModal,
|
||||||
|
@ -120,6 +121,7 @@ const {
|
||||||
} = require('../../ts/state/roots/createShortcutGuideModal');
|
} = require('../../ts/state/roots/createShortcutGuideModal');
|
||||||
|
|
||||||
const { createStore } = require('../../ts/state/createStore');
|
const { createStore } = require('../../ts/state/createStore');
|
||||||
|
const appDuck = require('../../ts/state/ducks/app');
|
||||||
const callingDuck = require('../../ts/state/ducks/calling');
|
const callingDuck = require('../../ts/state/ducks/calling');
|
||||||
const conversationsDuck = require('../../ts/state/ducks/conversations');
|
const conversationsDuck = require('../../ts/state/ducks/conversations');
|
||||||
const emojisDuck = require('../../ts/state/ducks/emojis');
|
const emojisDuck = require('../../ts/state/ducks/emojis');
|
||||||
|
@ -356,6 +358,7 @@ exports.setup = (options = {}) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Roots = {
|
const Roots = {
|
||||||
|
createApp,
|
||||||
createCallManager,
|
createCallManager,
|
||||||
createChatColorPicker,
|
createChatColorPicker,
|
||||||
createCompositionArea,
|
createCompositionArea,
|
||||||
|
@ -379,6 +382,7 @@ exports.setup = (options = {}) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Ducks = {
|
const Ducks = {
|
||||||
|
app: appDuck,
|
||||||
calling: callingDuck,
|
calling: callingDuck,
|
||||||
conversations: conversationsDuck,
|
conversations: conversationsDuck,
|
||||||
emojis: emojisDuck,
|
emojis: emojisDuck,
|
||||||
|
|
|
@ -1,154 +0,0 @@
|
||||||
// Copyright 2017-2021 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
/* global Backbone, Whisper, storage, _, ConversationController, $ */
|
|
||||||
|
|
||||||
/* eslint-disable more/no-then */
|
|
||||||
|
|
||||||
// eslint-disable-next-line func-names
|
|
||||||
(function () {
|
|
||||||
window.Whisper = window.Whisper || {};
|
|
||||||
|
|
||||||
function resolveTheme() {
|
|
||||||
const theme = storage.get('theme-setting') || 'system';
|
|
||||||
if (theme === 'system') {
|
|
||||||
return window.systemTheme;
|
|
||||||
}
|
|
||||||
return theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
Whisper.AppView = Backbone.View.extend({
|
|
||||||
initialize() {
|
|
||||||
this.inboxView = null;
|
|
||||||
this.installView = null;
|
|
||||||
|
|
||||||
this.applyTheme();
|
|
||||||
this.applyHideMenu();
|
|
||||||
|
|
||||||
window.subscribeToSystemThemeChange(() => {
|
|
||||||
this.applyTheme();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
events: {
|
|
||||||
openInbox: 'openInbox',
|
|
||||||
},
|
|
||||||
applyTheme() {
|
|
||||||
const theme = resolveTheme();
|
|
||||||
this.$el
|
|
||||||
.removeClass('light-theme')
|
|
||||||
.removeClass('dark-theme')
|
|
||||||
.addClass(`${theme}-theme`);
|
|
||||||
},
|
|
||||||
applyHideMenu() {
|
|
||||||
const hideMenuBar = storage.get('hide-menu-bar', false);
|
|
||||||
window.setAutoHideMenuBar(hideMenuBar);
|
|
||||||
window.setMenuBarVisibility(!hideMenuBar);
|
|
||||||
},
|
|
||||||
openView(view) {
|
|
||||||
this.el.innerHTML = '';
|
|
||||||
this.el.append(view.el);
|
|
||||||
this.delegateEvents();
|
|
||||||
},
|
|
||||||
openDebugLog() {
|
|
||||||
this.closeDebugLog();
|
|
||||||
this.debugLogView = new Whisper.DebugLogView();
|
|
||||||
this.debugLogView.$el.appendTo(this.el);
|
|
||||||
},
|
|
||||||
closeDebugLog() {
|
|
||||||
if (this.debugLogView) {
|
|
||||||
this.debugLogView.remove();
|
|
||||||
this.debugLogView = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openInstaller(options = {}) {
|
|
||||||
window.addSetupMenuItems();
|
|
||||||
|
|
||||||
this.resetViews();
|
|
||||||
const installView = new Whisper.InstallView(options);
|
|
||||||
this.installView = installView;
|
|
||||||
|
|
||||||
this.openView(this.installView);
|
|
||||||
},
|
|
||||||
closeInstaller() {
|
|
||||||
if (this.installView) {
|
|
||||||
this.installView.remove();
|
|
||||||
this.installView = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openStandalone() {
|
|
||||||
if (window.getEnvironment() !== 'production') {
|
|
||||||
window.addSetupMenuItems();
|
|
||||||
this.resetViews();
|
|
||||||
this.standaloneView = new Whisper.StandaloneRegistrationView();
|
|
||||||
this.openView(this.standaloneView);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closeStandalone() {
|
|
||||||
if (this.standaloneView) {
|
|
||||||
this.standaloneView.remove();
|
|
||||||
this.standaloneView = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resetViews() {
|
|
||||||
this.closeInstaller();
|
|
||||||
this.closeStandalone();
|
|
||||||
},
|
|
||||||
openInbox(options = {}) {
|
|
||||||
// The inbox can be created before the 'empty' event fires or afterwards. If
|
|
||||||
// before, it's straightforward: the onEmpty() handler below updates the
|
|
||||||
// view directly, and we're in good shape. If we create the inbox late, we
|
|
||||||
// need to be sure that the current value of initialLoadComplete is provided
|
|
||||||
// so its loading screen doesn't stick around forever.
|
|
||||||
|
|
||||||
// Two primary techniques at play for this situation:
|
|
||||||
// - background.js has two openInbox() calls, and passes initalLoadComplete
|
|
||||||
// directly via the options parameter.
|
|
||||||
// - in other situations openInbox() will be called with no options. So this
|
|
||||||
// view keeps track of whether onEmpty() has ever been called with
|
|
||||||
// this.initialLoadComplete. An example of this: on a phone-pairing setup.
|
|
||||||
_.defaults(options, { initialLoadComplete: this.initialLoadComplete });
|
|
||||||
|
|
||||||
window.log.info('open inbox');
|
|
||||||
this.closeInstaller();
|
|
||||||
|
|
||||||
if (!this.inboxView) {
|
|
||||||
// We create the inbox immediately so we don't miss an update to
|
|
||||||
// this.initialLoadComplete between the start of this method and the
|
|
||||||
// creation of inboxView.
|
|
||||||
this.inboxView = new Whisper.InboxView({
|
|
||||||
window,
|
|
||||||
initialLoadComplete: options.initialLoadComplete,
|
|
||||||
});
|
|
||||||
return ConversationController.loadPromise().then(() => {
|
|
||||||
this.openView(this.inboxView);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!$.contains(this.el, this.inboxView.el)) {
|
|
||||||
this.openView(this.inboxView);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
},
|
|
||||||
onEmpty() {
|
|
||||||
const view = this.inboxView;
|
|
||||||
|
|
||||||
this.initialLoadComplete = true;
|
|
||||||
if (view) {
|
|
||||||
view.onEmpty();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onProgress(count) {
|
|
||||||
const view = this.inboxView;
|
|
||||||
if (view) {
|
|
||||||
view.onProgress(count);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openConversation(id, messageId) {
|
|
||||||
if (id) {
|
|
||||||
this.openInbox().then(() => {
|
|
||||||
this.inboxView.openConversation(id, messageId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
|
@ -106,6 +106,30 @@
|
||||||
this.conversation_stack.unload();
|
this.conversation_stack.unload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.Whisper.events.on('showConversation', async (id, messageId) => {
|
||||||
|
const conversation = await ConversationController.getOrCreateAndWait(
|
||||||
|
id,
|
||||||
|
'private'
|
||||||
|
);
|
||||||
|
|
||||||
|
conversation.setMarkedUnread(false);
|
||||||
|
|
||||||
|
const { openConversationExternal } = window.reduxActions.conversations;
|
||||||
|
if (openConversationExternal) {
|
||||||
|
openConversationExternal(conversation.id, messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.conversation_stack.open(conversation, messageId);
|
||||||
|
this.focusConversation();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.Whisper.events.on('loadingProgress', count => {
|
||||||
|
const view = this.appLoadingScreen;
|
||||||
|
if (view) {
|
||||||
|
view.updateProgress(count);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!options.initialLoadComplete) {
|
if (!options.initialLoadComplete) {
|
||||||
this.appLoadingScreen = new Whisper.AppLoadingScreen();
|
this.appLoadingScreen = new Whisper.AppLoadingScreen();
|
||||||
this.appLoadingScreen.render();
|
this.appLoadingScreen.render();
|
||||||
|
@ -209,12 +233,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onProgress(count) {
|
|
||||||
const view = this.appLoadingScreen;
|
|
||||||
if (view) {
|
|
||||||
view.updateProgress(count);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
focusConversation(e) {
|
focusConversation(e) {
|
||||||
if (e && this.$(e.target).closest('.placeholder').length) {
|
if (e && this.$(e.target).closest('.placeholder').length) {
|
||||||
return;
|
return;
|
||||||
|
@ -231,22 +249,6 @@
|
||||||
reloadBackgroundPage() {
|
reloadBackgroundPage() {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
},
|
},
|
||||||
async openConversation(id, messageId) {
|
|
||||||
const conversation = await ConversationController.getOrCreateAndWait(
|
|
||||||
id,
|
|
||||||
'private'
|
|
||||||
);
|
|
||||||
|
|
||||||
conversation.setMarkedUnread(false);
|
|
||||||
|
|
||||||
const { openConversationExternal } = window.reduxActions.conversations;
|
|
||||||
if (openConversationExternal) {
|
|
||||||
openConversationExternal(conversation.id, messageId);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.conversation_stack.open(conversation, messageId);
|
|
||||||
this.focusConversation();
|
|
||||||
},
|
|
||||||
closeRecording(e) {
|
closeRecording(e) {
|
||||||
if (e && this.$(e.target).closest('.capture-audio').length > 0) {
|
if (e && this.$(e.target).closest('.capture-audio').length > 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -26,15 +26,15 @@ body {
|
||||||
--title-bar-drag-area-height: 28px;
|
--title-bar-drag-area-height: 28px;
|
||||||
--draggable-app-region: drag;
|
--draggable-app-region: drag;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
body.light-theme {
|
&.light-theme {
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
color: $color-gray-90;
|
color: $color-gray-90;
|
||||||
}
|
}
|
||||||
body.dark-theme {
|
&.dark-theme {
|
||||||
background-color: $color-gray-95;
|
background-color: $color-gray-95;
|
||||||
color: $color-gray-05;
|
color: $color-gray-05;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
|
|
12
stylesheets/components/App.scss
Normal file
12
stylesheets/components/App.scss
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.App {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&.light-theme {
|
||||||
|
background-color: $color-white;
|
||||||
|
color: $color-gray-90;
|
||||||
|
}
|
||||||
|
&.dark-theme {
|
||||||
|
background-color: $color-gray-95;
|
||||||
|
color: $color-gray-05;
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
// New style: components
|
// New style: components
|
||||||
@import './components/AddGroupMembersModal.scss';
|
@import './components/AddGroupMembersModal.scss';
|
||||||
|
@import './components/App.scss';
|
||||||
@import './components/Avatar.scss';
|
@import './components/Avatar.scss';
|
||||||
@import './components/AvatarInput.scss';
|
@import './components/AvatarInput.scss';
|
||||||
@import './components/Button.scss';
|
@import './components/Button.scss';
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { isNumber } from 'lodash';
|
import { isNumber } from 'lodash';
|
||||||
|
import { render } from 'react-dom';
|
||||||
import {
|
import {
|
||||||
DecryptionErrorMessage,
|
DecryptionErrorMessage,
|
||||||
PlaintextContent,
|
PlaintextContent,
|
||||||
|
@ -300,10 +301,8 @@ export async function startApp(): Promise<void> {
|
||||||
window.log.info('environment:', window.getEnvironment());
|
window.log.info('environment:', window.getEnvironment());
|
||||||
|
|
||||||
let idleDetector: WhatIsThis;
|
let idleDetector: WhatIsThis;
|
||||||
let initialLoadComplete = false;
|
|
||||||
let newVersion = false;
|
let newVersion = false;
|
||||||
|
|
||||||
window.owsDesktopApp = {};
|
|
||||||
window.document.title = window.getTitle();
|
window.document.title = window.getTitle();
|
||||||
|
|
||||||
window.Whisper.KeyChangeListener.init(window.textsecure.storage.protocol);
|
window.Whisper.KeyChangeListener.init(window.textsecure.storage.protocol);
|
||||||
|
@ -905,6 +904,9 @@ export async function startApp(): Promise<void> {
|
||||||
const ourUuid = window.textsecure.storage.user.getUuid();
|
const ourUuid = window.textsecure.storage.user.getUuid();
|
||||||
const ourConversationId = window.ConversationController.getOurConversationId();
|
const ourConversationId = window.ConversationController.getOurConversationId();
|
||||||
|
|
||||||
|
const themeSetting = window.Events.getThemeSetting();
|
||||||
|
const theme = themeSetting === 'system' ? window.systemTheme : themeSetting;
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
conversations: {
|
conversations: {
|
||||||
conversationLookup: window.Signal.Util.makeLookup(conversations, 'id'),
|
conversationLookup: window.Signal.Util.makeLookup(conversations, 'id'),
|
||||||
|
@ -943,7 +945,7 @@ export async function startApp(): Promise<void> {
|
||||||
platform: window.platform,
|
platform: window.platform,
|
||||||
i18n: window.i18n,
|
i18n: window.i18n,
|
||||||
interactionMode: window.getInteractionMode(),
|
interactionMode: window.getInteractionMode(),
|
||||||
theme: window.Events.getThemeSetting(),
|
theme,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -955,6 +957,10 @@ export async function startApp(): Promise<void> {
|
||||||
|
|
||||||
// Binding these actions to our redux store and exposing them allows us to update
|
// Binding these actions to our redux store and exposing them allows us to update
|
||||||
// redux when things change in the backbone world.
|
// redux when things change in the backbone world.
|
||||||
|
actions.app = window.Signal.State.bindActionCreators(
|
||||||
|
window.Signal.State.Ducks.app.actions,
|
||||||
|
store.dispatch
|
||||||
|
);
|
||||||
actions.calling = window.Signal.State.bindActionCreators(
|
actions.calling = window.Signal.State.bindActionCreators(
|
||||||
window.Signal.State.Ducks.calling.actions,
|
window.Signal.State.Ducks.calling.actions,
|
||||||
store.dispatch
|
store.dispatch
|
||||||
|
@ -1547,17 +1553,11 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.Whisper.events.on('setupAsNewDevice', () => {
|
window.Whisper.events.on('setupAsNewDevice', () => {
|
||||||
const { appView } = window.owsDesktopApp;
|
window.reduxActions.app.openInstaller();
|
||||||
if (appView) {
|
|
||||||
appView.openInstaller();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
window.Whisper.events.on('setupAsStandalone', () => {
|
window.Whisper.events.on('setupAsStandalone', () => {
|
||||||
const { appView } = window.owsDesktopApp;
|
window.reduxActions.app.openStandalone();
|
||||||
if (appView) {
|
|
||||||
appView.openStandalone();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
window.Whisper.events.on('powerMonitorSuspend', () => {
|
window.Whisper.events.on('powerMonitorSuspend', () => {
|
||||||
|
@ -1712,10 +1712,13 @@ export async function startApp(): Promise<void> {
|
||||||
});
|
});
|
||||||
|
|
||||||
cancelInitializationMessage();
|
cancelInitializationMessage();
|
||||||
const appView = new window.Whisper.AppView({
|
render(
|
||||||
el: $('body'),
|
window.Signal.State.Roots.createApp(window.reduxStore),
|
||||||
});
|
document.body
|
||||||
window.owsDesktopApp.appView = appView;
|
);
|
||||||
|
const hideMenuBar = window.storage.get('hide-menu-bar', false);
|
||||||
|
window.setAutoHideMenuBar(hideMenuBar);
|
||||||
|
window.setMenuBarVisibility(!hideMenuBar);
|
||||||
|
|
||||||
window.Whisper.WallClockListener.init(window.Whisper.events);
|
window.Whisper.WallClockListener.init(window.Whisper.events);
|
||||||
window.Whisper.ExpiringMessagesListener.init(window.Whisper.events);
|
window.Whisper.ExpiringMessagesListener.init(window.Whisper.events);
|
||||||
|
@ -1723,22 +1726,14 @@ export async function startApp(): Promise<void> {
|
||||||
|
|
||||||
if (window.Signal.Util.Registration.everDone()) {
|
if (window.Signal.Util.Registration.everDone()) {
|
||||||
connect();
|
connect();
|
||||||
appView.openInbox({
|
window.reduxActions.app.openInbox();
|
||||||
initialLoadComplete,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
appView.openInstaller();
|
window.reduxActions.app.openInstaller();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.Whisper.events.on('showDebugLog', () => {
|
|
||||||
appView.openDebugLog();
|
|
||||||
});
|
|
||||||
window.Whisper.events.on('unauthorized', () => {
|
|
||||||
appView.inboxView.networkStatusView.update();
|
|
||||||
});
|
|
||||||
window.Whisper.events.on('contactsync', () => {
|
window.Whisper.events.on('contactsync', () => {
|
||||||
if (appView.installView) {
|
if (window.reduxStore.getState().app.isShowingInstaller) {
|
||||||
appView.openInbox();
|
window.reduxActions.app.openInbox();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1747,20 +1742,12 @@ export async function startApp(): Promise<void> {
|
||||||
window.Whisper.Notifications.fastClear()
|
window.Whisper.Notifications.fastClear()
|
||||||
);
|
);
|
||||||
|
|
||||||
window.Whisper.events.on('showConversation', (id, messageId) => {
|
|
||||||
if (appView) {
|
|
||||||
appView.openConversation(id, messageId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.Whisper.Notifications.on('click', (id, messageId) => {
|
window.Whisper.Notifications.on('click', (id, messageId) => {
|
||||||
window.showWindow();
|
window.showWindow();
|
||||||
if (id) {
|
if (id) {
|
||||||
appView.openConversation(id, messageId);
|
window.Whisper.events.trigger('showConversation', id, messageId);
|
||||||
} else {
|
} else {
|
||||||
appView.openInbox({
|
window.reduxActions.app.openInbox();
|
||||||
initialLoadComplete,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2291,11 +2278,6 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeTheme() {
|
function onChangeTheme() {
|
||||||
const view = window.owsDesktopApp.appView;
|
|
||||||
if (view) {
|
|
||||||
view.applyTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.reduxActions && window.reduxActions.user) {
|
if (window.reduxActions && window.reduxActions.user) {
|
||||||
const theme = window.Events.getThemeSetting();
|
const theme = window.Events.getThemeSetting();
|
||||||
window.reduxActions.user.userChanged({
|
window.reduxActions.user.userChanged({
|
||||||
|
@ -2352,7 +2334,6 @@ export async function startApp(): Promise<void> {
|
||||||
window.flushAllWaitBatchers(),
|
window.flushAllWaitBatchers(),
|
||||||
]);
|
]);
|
||||||
window.log.info('onEmpty: All outstanding database requests complete');
|
window.log.info('onEmpty: All outstanding database requests complete');
|
||||||
initialLoadComplete = true;
|
|
||||||
window.readyForUpdates();
|
window.readyForUpdates();
|
||||||
|
|
||||||
// Start listeners here, after we get through our queue.
|
// Start listeners here, after we get through our queue.
|
||||||
|
@ -2370,13 +2351,8 @@ export async function startApp(): Promise<void> {
|
||||||
window.Whisper.Notifications.enable();
|
window.Whisper.Notifications.enable();
|
||||||
|
|
||||||
await onAppView;
|
await onAppView;
|
||||||
const view = window.owsDesktopApp.appView;
|
|
||||||
if (!view) {
|
|
||||||
throw new Error('Expected `appView` to be initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
window.reduxActions.app.initialLoadComplete();
|
||||||
view.onEmpty();
|
|
||||||
|
|
||||||
window.logAppLoadedEvent({
|
window.logAppLoadedEvent({
|
||||||
processedCount: messageReceiver && messageReceiver.getProcessedCount(),
|
processedCount: messageReceiver && messageReceiver.getProcessedCount(),
|
||||||
|
@ -2456,10 +2432,7 @@ export async function startApp(): Promise<void> {
|
||||||
`incrementProgress: Message count is ${initialStartupCount}`
|
`incrementProgress: Message count is ${initialStartupCount}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const view = window.owsDesktopApp.appView;
|
window.Whisper.events.trigger('loadingProgress', initialStartupCount);
|
||||||
if (view) {
|
|
||||||
view.onProgress(initialStartupCount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.Whisper.events.on('manualConnect', manualConnect);
|
window.Whisper.events.on('manualConnect', manualConnect);
|
||||||
|
|
42
ts/components/App.tsx
Normal file
42
ts/components/App.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { AppViewType } from '../state/ducks/app';
|
||||||
|
import { Inbox } from './Inbox';
|
||||||
|
import { Install } from './Install';
|
||||||
|
import { StandaloneRegistration } from './StandaloneRegistration';
|
||||||
|
import { ThemeType } from '../types/Util';
|
||||||
|
|
||||||
|
export type PropsType = {
|
||||||
|
appView: AppViewType;
|
||||||
|
hasInitialLoadCompleted: boolean;
|
||||||
|
theme: ThemeType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const App = ({
|
||||||
|
appView,
|
||||||
|
hasInitialLoadCompleted,
|
||||||
|
theme,
|
||||||
|
}: PropsType): JSX.Element => {
|
||||||
|
let contents;
|
||||||
|
|
||||||
|
if (appView === AppViewType.Installer) {
|
||||||
|
contents = <Install />;
|
||||||
|
} else if (appView === AppViewType.Standalone) {
|
||||||
|
contents = <StandaloneRegistration />;
|
||||||
|
} else if (appView === AppViewType.Inbox) {
|
||||||
|
contents = <Inbox hasInitialLoadCompleted={hasInitialLoadCompleted} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames({
|
||||||
|
App: true,
|
||||||
|
'light-theme': theme === ThemeType.light,
|
||||||
|
'dark-theme': theme === ThemeType.dark,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{contents}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
35
ts/components/BackboneHost.tsx
Normal file
35
ts/components/BackboneHost.tsx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import * as Backbone from 'backbone';
|
||||||
|
|
||||||
|
type PropsType = {
|
||||||
|
View: typeof Backbone.View;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BackboneHost = ({ View, className }: PropsType): JSX.Element => {
|
||||||
|
const hostRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const viewRef = useRef<Backbone.View | undefined>(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const view = new View({
|
||||||
|
el: hostRef.current,
|
||||||
|
});
|
||||||
|
|
||||||
|
viewRef.current = view;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (!viewRef || !viewRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewRef.current.remove();
|
||||||
|
viewRef.current = undefined;
|
||||||
|
};
|
||||||
|
}, [View]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={className} ref={hostRef} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
48
ts/components/Inbox.tsx
Normal file
48
ts/components/Inbox.tsx
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import * as Backbone from 'backbone';
|
||||||
|
|
||||||
|
type InboxViewType = Backbone.View & {
|
||||||
|
onEmpty?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type InboxViewOptionsType = Backbone.ViewOptions & {
|
||||||
|
initialLoadComplete: boolean;
|
||||||
|
window: typeof window;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PropsType = {
|
||||||
|
hasInitialLoadCompleted: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Inbox = ({ hasInitialLoadCompleted }: PropsType): JSX.Element => {
|
||||||
|
const hostRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const viewRef = useRef<InboxViewType | undefined>(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const viewOptions: InboxViewOptionsType = {
|
||||||
|
el: hostRef.current,
|
||||||
|
initialLoadComplete: false,
|
||||||
|
window,
|
||||||
|
};
|
||||||
|
const view = new window.Whisper.InboxView(viewOptions);
|
||||||
|
|
||||||
|
viewRef.current = view;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (!viewRef || !viewRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewRef.current.remove();
|
||||||
|
viewRef.current = undefined;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hasInitialLoadCompleted && viewRef.current && viewRef.current.onEmpty) {
|
||||||
|
viewRef.current.onEmpty();
|
||||||
|
}
|
||||||
|
}, [hasInitialLoadCompleted, viewRef]);
|
||||||
|
|
||||||
|
return <div className="inbox index" ref={hostRef} />;
|
||||||
|
};
|
11
ts/components/Install.tsx
Normal file
11
ts/components/Install.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { BackboneHost } from './BackboneHost';
|
||||||
|
|
||||||
|
export const Install = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<BackboneHost
|
||||||
|
className="full-screen-flow"
|
||||||
|
View={window.Whisper.InstallView}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
11
ts/components/StandaloneRegistration.tsx
Normal file
11
ts/components/StandaloneRegistration.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { BackboneHost } from './BackboneHost';
|
||||||
|
|
||||||
|
export const StandaloneRegistration = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<BackboneHost
|
||||||
|
className="full-screen-flow"
|
||||||
|
View={window.Whisper.StandaloneRegistrationView}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2019-2020 Signal Messenger, LLC
|
// Copyright 2019-2020 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { actions as app } from './ducks/app';
|
||||||
import { actions as audioPlayer } from './ducks/audioPlayer';
|
import { actions as audioPlayer } from './ducks/audioPlayer';
|
||||||
import { actions as calling } from './ducks/calling';
|
import { actions as calling } from './ducks/calling';
|
||||||
import { actions as conversations } from './ducks/conversations';
|
import { actions as conversations } from './ducks/conversations';
|
||||||
|
@ -17,6 +18,7 @@ import { actions as updates } from './ducks/updates';
|
||||||
import { actions as user } from './ducks/user';
|
import { actions as user } from './ducks/user';
|
||||||
|
|
||||||
export const mapDispatchToProps = {
|
export const mapDispatchToProps = {
|
||||||
|
...app,
|
||||||
...audioPlayer,
|
...audioPlayer,
|
||||||
...calling,
|
...calling,
|
||||||
...conversations,
|
...conversations,
|
||||||
|
|
155
ts/state/ducks/app.ts
Normal file
155
ts/state/ducks/app.ts
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { ThunkAction } from 'redux-thunk';
|
||||||
|
import { StateType as RootStateType } from '../reducer';
|
||||||
|
|
||||||
|
// State
|
||||||
|
|
||||||
|
export enum AppViewType {
|
||||||
|
Blank = 'Blank',
|
||||||
|
Inbox = 'Inbox',
|
||||||
|
Installer = 'Installer',
|
||||||
|
Standalone = 'Standalone',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppStateType = {
|
||||||
|
appView: AppViewType;
|
||||||
|
hasInitialLoadCompleted: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
|
||||||
|
const INITIAL_LOAD_COMPLETE = 'app/INITIAL_LOAD_COMPLETE';
|
||||||
|
const OPEN_INBOX = 'app/OPEN_INBOX';
|
||||||
|
const OPEN_INSTALLER = 'app/OPEN_INSTALLER';
|
||||||
|
const OPEN_STANDALONE = 'app/OPEN_STANDALONE';
|
||||||
|
|
||||||
|
type InitialLoadCompleteActionType = {
|
||||||
|
type: typeof INITIAL_LOAD_COMPLETE;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OpenInboxActionType = {
|
||||||
|
type: typeof OPEN_INBOX;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OpenInstallerActionType = {
|
||||||
|
type: typeof OPEN_INSTALLER;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OpenStandaloneActionType = {
|
||||||
|
type: typeof OPEN_STANDALONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AppActionType =
|
||||||
|
| InitialLoadCompleteActionType
|
||||||
|
| OpenInboxActionType
|
||||||
|
| OpenInstallerActionType
|
||||||
|
| OpenStandaloneActionType;
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
initialLoadComplete,
|
||||||
|
openInbox,
|
||||||
|
openInstaller,
|
||||||
|
openStandalone,
|
||||||
|
};
|
||||||
|
|
||||||
|
function initialLoadComplete(): InitialLoadCompleteActionType {
|
||||||
|
return {
|
||||||
|
type: INITIAL_LOAD_COMPLETE,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function openInbox(): ThunkAction<
|
||||||
|
void,
|
||||||
|
RootStateType,
|
||||||
|
unknown,
|
||||||
|
OpenInboxActionType
|
||||||
|
> {
|
||||||
|
return async dispatch => {
|
||||||
|
window.log.info('open inbox');
|
||||||
|
|
||||||
|
await window.ConversationController.loadPromise();
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: OPEN_INBOX,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function openInstaller(): ThunkAction<
|
||||||
|
void,
|
||||||
|
RootStateType,
|
||||||
|
unknown,
|
||||||
|
OpenInstallerActionType
|
||||||
|
> {
|
||||||
|
return dispatch => {
|
||||||
|
window.addSetupMenuItems();
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: OPEN_INSTALLER,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function openStandalone(): ThunkAction<
|
||||||
|
void,
|
||||||
|
RootStateType,
|
||||||
|
unknown,
|
||||||
|
OpenStandaloneActionType
|
||||||
|
> {
|
||||||
|
return dispatch => {
|
||||||
|
if (window.getEnvironment() === 'production') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addSetupMenuItems();
|
||||||
|
dispatch({
|
||||||
|
type: OPEN_STANDALONE,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reducer
|
||||||
|
|
||||||
|
export function getEmptyState(): AppStateType {
|
||||||
|
return {
|
||||||
|
appView: AppViewType.Blank,
|
||||||
|
hasInitialLoadCompleted: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reducer(
|
||||||
|
state: Readonly<AppStateType> = getEmptyState(),
|
||||||
|
action: Readonly<AppActionType>
|
||||||
|
): AppStateType {
|
||||||
|
if (action.type === OPEN_INBOX) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
appView: AppViewType.Inbox,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === INITIAL_LOAD_COMPLETE) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
hasInitialLoadCompleted: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === OPEN_INSTALLER) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
appView: AppViewType.Installer,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === OPEN_STANDALONE) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
appView: AppViewType.Standalone,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import { combineReducers } from 'redux';
|
import { combineReducers } from 'redux';
|
||||||
|
|
||||||
|
import { reducer as app } from './ducks/app';
|
||||||
import { reducer as audioPlayer } from './ducks/audioPlayer';
|
import { reducer as audioPlayer } from './ducks/audioPlayer';
|
||||||
import { reducer as calling } from './ducks/calling';
|
import { reducer as calling } from './ducks/calling';
|
||||||
import { reducer as conversations } from './ducks/conversations';
|
import { reducer as conversations } from './ducks/conversations';
|
||||||
|
@ -19,6 +20,7 @@ import { reducer as updates } from './ducks/updates';
|
||||||
import { reducer as user } from './ducks/user';
|
import { reducer as user } from './ducks/user';
|
||||||
|
|
||||||
export const reducer = combineReducers({
|
export const reducer = combineReducers({
|
||||||
|
app,
|
||||||
audioPlayer,
|
audioPlayer,
|
||||||
calling,
|
calling,
|
||||||
conversations,
|
conversations,
|
||||||
|
|
15
ts/state/roots/createApp.tsx
Normal file
15
ts/state/roots/createApp.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React, { ReactElement } from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
|
import { Store } from 'redux';
|
||||||
|
|
||||||
|
import { SmartApp } from '../smart/App';
|
||||||
|
|
||||||
|
export const createApp = (store: Store): ReactElement => (
|
||||||
|
<Provider store={store}>
|
||||||
|
<SmartApp />
|
||||||
|
</Provider>
|
||||||
|
);
|
21
ts/state/smart/App.ts
Normal file
21
ts/state/smart/App.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { App } from '../../components/App';
|
||||||
|
import { StateType } from '../reducer';
|
||||||
|
import { getIntl, getTheme } from '../selectors/user';
|
||||||
|
import { mapDispatchToProps } from '../actions';
|
||||||
|
|
||||||
|
const mapStateToProps = (state: StateType) => {
|
||||||
|
return {
|
||||||
|
...state.app,
|
||||||
|
i18n: getIntl(state),
|
||||||
|
theme: getTheme(state),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const smart = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
|
export const SmartApp = smart(App);
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2020 Signal Messenger, LLC
|
// Copyright 2020 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { actions as app } from './ducks/app';
|
||||||
import { actions as audioPlayer } from './ducks/audioPlayer';
|
import { actions as audioPlayer } from './ducks/audioPlayer';
|
||||||
import { actions as calling } from './ducks/calling';
|
import { actions as calling } from './ducks/calling';
|
||||||
import { actions as conversations } from './ducks/conversations';
|
import { actions as conversations } from './ducks/conversations';
|
||||||
|
@ -17,6 +18,7 @@ import { actions as updates } from './ducks/updates';
|
||||||
import { actions as user } from './ducks/user';
|
import { actions as user } from './ducks/user';
|
||||||
|
|
||||||
export type ReduxActions = {
|
export type ReduxActions = {
|
||||||
|
app: typeof app;
|
||||||
audioPlayer: typeof audioPlayer;
|
audioPlayer: typeof audioPlayer;
|
||||||
calling: typeof calling;
|
calling: typeof calling;
|
||||||
conversations: typeof conversations;
|
conversations: typeof conversations;
|
||||||
|
|
|
@ -252,30 +252,6 @@
|
||||||
"updated": "2020-08-21T11:29:29.636Z",
|
"updated": "2020-08-21T11:29:29.636Z",
|
||||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"rule": "DOM-innerHTML",
|
|
||||||
"path": "js/views/app_view.js",
|
|
||||||
"line": " this.el.innerHTML = '';",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2018-09-15T00:38:04.183Z",
|
|
||||||
"reasonDetail": "Hard-coded string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-append(",
|
|
||||||
"path": "js/views/app_view.js",
|
|
||||||
"line": " this.el.append(view.el);",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2018-09-19T18:13:29.628Z",
|
|
||||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-appendTo(",
|
|
||||||
"path": "js/views/app_view.js",
|
|
||||||
"line": " this.debugLogView.$el.appendTo(this.el);",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2018-09-19T18:13:29.628Z",
|
|
||||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"rule": "jQuery-$(",
|
"rule": "jQuery-$(",
|
||||||
"path": "js/views/banner_view.js",
|
"path": "js/views/banner_view.js",
|
||||||
|
@ -13510,6 +13486,20 @@
|
||||||
"updated": "2020-10-26T19:12:24.410Z",
|
"updated": "2020-10-26T19:12:24.410Z",
|
||||||
"reasonDetail": "Only used to focus the element."
|
"reasonDetail": "Only used to focus the element."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"rule": "React-useRef",
|
||||||
|
"path": "ts/components/BackboneHost.js",
|
||||||
|
"line": " const hostRef = react_1.useRef(null);",
|
||||||
|
"reasonCategory": "usageTrusted",
|
||||||
|
"updated": "2021-06-09T04:02:08.305Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rule": "React-useRef",
|
||||||
|
"path": "ts/components/BackboneHost.js",
|
||||||
|
"line": " const viewRef = react_1.useRef(undefined);",
|
||||||
|
"reasonCategory": "usageTrusted",
|
||||||
|
"updated": "2021-06-09T04:02:08.305Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
"path": "ts/components/CallNeedPermissionScreen.js",
|
"path": "ts/components/CallNeedPermissionScreen.js",
|
||||||
|
@ -13812,6 +13802,20 @@
|
||||||
"updated": "2021-03-05T16:51:54.214Z",
|
"updated": "2021-03-05T16:51:54.214Z",
|
||||||
"reasonDetail": "Used to handle an <input> element. Only updates the value and selection state."
|
"reasonDetail": "Used to handle an <input> element. Only updates the value and selection state."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"rule": "React-useRef",
|
||||||
|
"path": "ts/components/Inbox.js",
|
||||||
|
"line": " const hostRef = react_1.useRef(null);",
|
||||||
|
"reasonCategory": "usageTrusted",
|
||||||
|
"updated": "2021-06-08T02:49:25.154Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rule": "React-useRef",
|
||||||
|
"path": "ts/components/Inbox.js",
|
||||||
|
"line": " const viewRef = react_1.useRef(undefined);",
|
||||||
|
"reasonCategory": "usageTrusted",
|
||||||
|
"updated": "2021-06-08T02:49:25.154Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"rule": "jQuery-$(",
|
"rule": "jQuery-$(",
|
||||||
"path": "ts/components/Intl.js",
|
"path": "ts/components/Intl.js",
|
||||||
|
|
10
ts/window.d.ts
vendored
10
ts/window.d.ts
vendored
|
@ -42,6 +42,7 @@ import * as Errors from '../js/modules/types/errors';
|
||||||
import { ConversationController } from './ConversationController';
|
import { ConversationController } from './ConversationController';
|
||||||
import { ReduxActions } from './state/types';
|
import { ReduxActions } from './state/types';
|
||||||
import { createStore } from './state/createStore';
|
import { createStore } from './state/createStore';
|
||||||
|
import { createApp } from './state/roots/createApp';
|
||||||
import { createCallManager } from './state/roots/createCallManager';
|
import { createCallManager } from './state/roots/createCallManager';
|
||||||
import { createChatColorPicker } from './state/roots/createChatColorPicker';
|
import { createChatColorPicker } from './state/roots/createChatColorPicker';
|
||||||
import { createCompositionArea } from './state/roots/createCompositionArea';
|
import { createCompositionArea } from './state/roots/createCompositionArea';
|
||||||
|
@ -62,6 +63,7 @@ import { createShortcutGuideModal } from './state/roots/createShortcutGuideModal
|
||||||
import { createStickerManager } from './state/roots/createStickerManager';
|
import { createStickerManager } from './state/roots/createStickerManager';
|
||||||
import { createStickerPreviewModal } from './state/roots/createStickerPreviewModal';
|
import { createStickerPreviewModal } from './state/roots/createStickerPreviewModal';
|
||||||
import { createTimeline } from './state/roots/createTimeline';
|
import { createTimeline } from './state/roots/createTimeline';
|
||||||
|
import * as appDuck from './state/ducks/app';
|
||||||
import * as callingDuck from './state/ducks/calling';
|
import * as callingDuck from './state/ducks/calling';
|
||||||
import * as conversationsDuck from './state/ducks/conversations';
|
import * as conversationsDuck from './state/ducks/conversations';
|
||||||
import * as emojisDuck from './state/ducks/emojis';
|
import * as emojisDuck from './state/ducks/emojis';
|
||||||
|
@ -163,6 +165,7 @@ declare global {
|
||||||
) => void
|
) => void
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
|
addSetupMenuItems: () => void;
|
||||||
attachmentDownloadQueue: Array<MessageModel> | undefined;
|
attachmentDownloadQueue: Array<MessageModel> | undefined;
|
||||||
startupProcessingQueue: StartupQueue | undefined;
|
startupProcessingQueue: StartupQueue | undefined;
|
||||||
baseAttachmentsPath: string;
|
baseAttachmentsPath: string;
|
||||||
|
@ -229,7 +232,6 @@ declare global {
|
||||||
nodeSetImmediate: typeof setImmediate;
|
nodeSetImmediate: typeof setImmediate;
|
||||||
normalizeUuids: (obj: any, paths: Array<string>, context: string) => void;
|
normalizeUuids: (obj: any, paths: Array<string>, context: string) => void;
|
||||||
onFullScreenChange: (fullScreen: boolean) => void;
|
onFullScreenChange: (fullScreen: boolean) => void;
|
||||||
owsDesktopApp: WhatIsThis;
|
|
||||||
platform: string;
|
platform: string;
|
||||||
preloadedImages: Array<WhatIsThis>;
|
preloadedImages: Array<WhatIsThis>;
|
||||||
reduxActions: ReduxActions;
|
reduxActions: ReduxActions;
|
||||||
|
@ -507,6 +509,7 @@ declare global {
|
||||||
bindActionCreators: typeof bindActionCreators;
|
bindActionCreators: typeof bindActionCreators;
|
||||||
createStore: typeof createStore;
|
createStore: typeof createStore;
|
||||||
Roots: {
|
Roots: {
|
||||||
|
createApp: typeof createApp;
|
||||||
createCallManager: typeof createCallManager;
|
createCallManager: typeof createCallManager;
|
||||||
createChatColorPicker: typeof createChatColorPicker;
|
createChatColorPicker: typeof createChatColorPicker;
|
||||||
createCompositionArea: typeof createCompositionArea;
|
createCompositionArea: typeof createCompositionArea;
|
||||||
|
@ -529,6 +532,7 @@ declare global {
|
||||||
createTimeline: typeof createTimeline;
|
createTimeline: typeof createTimeline;
|
||||||
};
|
};
|
||||||
Ducks: {
|
Ducks: {
|
||||||
|
app: typeof appDuck;
|
||||||
calling: typeof callingDuck;
|
calling: typeof callingDuck;
|
||||||
conversations: typeof conversationsDuck;
|
conversations: typeof conversationsDuck;
|
||||||
emojis: typeof emojisDuck;
|
emojis: typeof emojisDuck;
|
||||||
|
@ -695,13 +699,15 @@ export type WhisperType = {
|
||||||
ConversationArchivedToast: WhatIsThis;
|
ConversationArchivedToast: WhatIsThis;
|
||||||
ConversationUnarchivedToast: WhatIsThis;
|
ConversationUnarchivedToast: WhatIsThis;
|
||||||
ConversationMarkedUnreadToast: WhatIsThis;
|
ConversationMarkedUnreadToast: WhatIsThis;
|
||||||
AppView: WhatIsThis;
|
|
||||||
WallClockListener: WhatIsThis;
|
WallClockListener: WhatIsThis;
|
||||||
MessageRequests: WhatIsThis;
|
MessageRequests: WhatIsThis;
|
||||||
BannerView: any;
|
BannerView: any;
|
||||||
RecorderView: any;
|
RecorderView: any;
|
||||||
GroupMemberList: any;
|
GroupMemberList: any;
|
||||||
GroupLinkCopiedToast: typeof Backbone.View;
|
GroupLinkCopiedToast: typeof Backbone.View;
|
||||||
|
InboxView: typeof window.Whisper.View;
|
||||||
|
InstallView: typeof window.Whisper.View;
|
||||||
|
StandaloneRegistrationView: typeof window.Whisper.View;
|
||||||
KeyVerificationPanelView: any;
|
KeyVerificationPanelView: any;
|
||||||
SafetyNumberChangeDialogView: any;
|
SafetyNumberChangeDialogView: any;
|
||||||
BodyRangesType: BodyRangesType;
|
BodyRangesType: BodyRangesType;
|
||||||
|
|
Loading…
Reference in a new issue