Removes some Backbone views

This commit is contained in:
Josh Perez 2021-06-17 17:15:09 -04:00 committed by GitHub
parent 93bc094342
commit 94d116c621
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 160 additions and 656 deletions

View file

@ -61,7 +61,6 @@
<script type='text/x-tmpl-mustache' id='two-column'> <script type='text/x-tmpl-mustache' id='two-column'>
<div class='module-title-bar-drag-area'></div> <div class='module-title-bar-drag-area'></div>
<div class='call-manager-placeholder'></div>
<div class='inbox-container'> <div class='inbox-container'>
<div class='gutter'> <div class='gutter'>
<div class='left-pane-placeholder'></div> <div class='left-pane-placeholder'></div>
@ -120,10 +119,6 @@
<button class='finish' tabIndex='1'><span class='icon'></span></button> <button class='finish' tabIndex='1'><span class='icon'></span></button>
</script> </script>
<script type='text/x-tmpl-mustache' id='safety-number-change-dialog'>
<div class='safety-number-change-dialog-wrapper'></div>
</script>
<script type='text/x-tmpl-mustache' id='identicon-svg'> <script type='text/x-tmpl-mustache' id='identicon-svg'>
<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100'> <svg xmlns='http://www.w3.org/2000/svg' width='100' height='100'>
<circle cx='50' cy='50' r='40' fill='{{ color }}' /> <circle cx='50' cy='50' r='40' fill='{{ color }}' />
@ -342,18 +337,13 @@
<script type='text/javascript' src='js/expiring_tap_to_view_messages.js'></script> <script type='text/javascript' src='js/expiring_tap_to_view_messages.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/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/recorder_view.js'></script> <script type='text/javascript' src='js/views/recorder_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>
<script type='text/javascript' src='js/views/install_view.js'></script> <script type='text/javascript' src='js/views/install_view.js'></script>
<script type='text/javascript' src='js/views/banner_view.js'></script>
<script type="text/javascript" src="js/views/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/standalone_registration_view.js'></script> <script type='text/javascript' src='js/views/standalone_registration_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>

View file

@ -51,9 +51,6 @@ const {
} = require('../../ts/components/conversation/MessageDetail'); } = require('../../ts/components/conversation/MessageDetail');
const { Quote } = require('../../ts/components/conversation/Quote'); const { Quote } = require('../../ts/components/conversation/Quote');
const { ProgressModal } = require('../../ts/components/ProgressModal'); const { ProgressModal } = require('../../ts/components/ProgressModal');
const {
SafetyNumberChangeDialog,
} = require('../../ts/components/SafetyNumberChangeDialog');
const { const {
StagedLinkPreview, StagedLinkPreview,
} = require('../../ts/components/conversation/StagedLinkPreview'); } = require('../../ts/components/conversation/StagedLinkPreview');
@ -79,13 +76,9 @@ const {
createConversationHeader, createConversationHeader,
} = require('../../ts/state/roots/createConversationHeader'); } = require('../../ts/state/roots/createConversationHeader');
const { createApp } = require('../../ts/state/roots/createApp'); const { createApp } = require('../../ts/state/roots/createApp');
const { createCallManager } = require('../../ts/state/roots/createCallManager');
const { const {
createForwardMessageModal, createForwardMessageModal,
} = require('../../ts/state/roots/createForwardMessageModal'); } = require('../../ts/state/roots/createForwardMessageModal');
const {
createGlobalModalContainer,
} = require('../../ts/state/roots/createGlobalModalContainer');
const { const {
createGroupLinkManagement, createGroupLinkManagement,
} = require('../../ts/state/roots/createGroupLinkManagement'); } = require('../../ts/state/roots/createGroupLinkManagement');
@ -347,7 +340,6 @@ exports.setup = (options = {}) => {
MessageDetail, MessageDetail,
Quote, Quote,
ProgressModal, ProgressModal,
SafetyNumberChangeDialog,
StagedLinkPreview, StagedLinkPreview,
DisappearingTimeDialog, DisappearingTimeDialog,
Types: { Types: {
@ -357,14 +349,12 @@ exports.setup = (options = {}) => {
const Roots = { const Roots = {
createApp, createApp,
createCallManager,
createChatColorPicker, createChatColorPicker,
createCompositionArea, createCompositionArea,
createContactModal, createContactModal,
createConversationDetails, createConversationDetails,
createConversationHeader, createConversationHeader,
createForwardMessageModal, createForwardMessageModal,
createGlobalModalContainer,
createGroupLinkManagement, createGroupLinkManagement,
createGroupV1MigrationModal, createGroupV1MigrationModal,
createGroupV2JoinModal, createGroupV2JoinModal,

View file

@ -1,38 +0,0 @@
// Copyright 2017-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper, $ */
// eslint-disable-next-line func-names
(function () {
window.Whisper = window.Whisper || {};
Whisper.BannerView = Whisper.View.extend({
className: 'banner',
template: () => $('#banner').html(),
events: {
'click .dismiss': 'onDismiss',
'click .body': 'onClick',
},
initialize(options) {
this.message = options.message;
this.callbacks = {
onDismiss: options.onDismiss,
onClick: options.onClick,
};
this.render();
},
render_attributes() {
return {
message: this.message,
};
},
onDismiss(e) {
this.callbacks.onDismiss();
e.stopPropagation();
},
onClick() {
this.callbacks.onClick();
},
});
})();

View file

@ -1,49 +0,0 @@
// Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper, textsecure, $ */
// eslint-disable-next-line func-names
(function () {
window.Whisper = window.Whisper || {};
Whisper.ContactListView = Whisper.ListView.extend({
tagName: 'div',
itemView: Whisper.View.extend({
tagName: 'div',
className: 'contact',
template: () => $('#contact').html(),
initialize(options) {
this.ourNumber = textsecure.storage.user.getNumber();
this.listenBack = options.listenBack;
this.loading = false;
this.conversation = options.conversation;
this.listenTo(this.model, 'change', this.render);
},
render() {
if (this.contactView) {
this.contactView.remove();
this.contactView = null;
}
const formattedContact = this.model.format();
this.contactView = new Whisper.ReactWrapperView({
className: 'contact-wrapper',
Component: window.Signal.Components.ContactListItem,
props: {
...formattedContact,
onClick: () =>
this.conversation.trigger(
'show-contact-modal',
formattedContact.id
),
},
});
this.$el.append(this.contactView.el);
return this;
},
}),
});
})();

View file

@ -1,42 +0,0 @@
// Copyright 2015-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper, i18n, $ */
// eslint-disable-next-line func-names
(function () {
window.Whisper = window.Whisper || {};
Whisper.GroupMemberList = Whisper.View.extend({
className: 'group-member-list panel',
template: () => $('#group-member-list').html(),
initialize(options) {
this.needVerify = options.needVerify;
this.render();
this.member_list_view = new Whisper.ContactListView({
collection: this.model,
className: 'members',
toInclude: {
listenBack: options.listenBack,
conversation: options.conversation,
},
});
this.member_list_view.render();
this.$('.container').append(this.member_list_view.el);
},
render_attributes() {
let summary;
if (this.needVerify) {
summary = i18n('membersNeedingVerification');
}
return {
members: i18n('groupMembers'),
summary,
};
},
});
})();

View file

@ -137,8 +137,6 @@
this.startConnectionListener(); this.startConnectionListener();
} else { } else {
this.setupLeftPane(); this.setupLeftPane();
this.setupCallManagerUI();
this.setupGlobalModalContainer();
} }
Whisper.events.on('pack-install-failed', () => { Whisper.events.on('pack-install-failed', () => {
@ -155,28 +153,6 @@
events: { events: {
click: 'onClick', click: 'onClick',
}, },
setupCallManagerUI() {
if (this.callManagerView) {
return;
}
this.callManagerView = new Whisper.ReactWrapperView({
className: 'call-manager-wrapper',
JSX: Signal.State.Roots.createCallManager(window.reduxStore),
});
this.$('.call-manager-placeholder').append(this.callManagerView.el);
},
setupGlobalModalContainer() {
if (this.globalModalContainerView) {
return;
}
this.globalModalContainerView = new Whisper.ReactWrapperView({
JSX: Signal.State.Roots.createGlobalModalContainer(window.reduxStore),
});
const node = document.querySelector('.inbox-container');
if (node) {
node.appendChild(this.globalModalContainerView.el);
}
},
setupLeftPane() { setupLeftPane() {
if (this.leftPaneView) { if (this.leftPaneView) {
return; return;
@ -217,8 +193,6 @@
}, },
onEmpty() { onEmpty() {
this.setupLeftPane(); this.setupLeftPane();
this.setupCallManagerUI();
this.setupGlobalModalContainer();
const view = this.appLoadingScreen; const view = this.appLoadingScreen;
if (view) { if (view) {
@ -241,14 +215,6 @@
this.$('#header, .gutter').addClass('inactive'); this.$('#header, .gutter').addClass('inactive');
this.$('.conversation-stack').removeClass('inactive'); this.$('.conversation-stack').removeClass('inactive');
}, },
focusHeader() {
this.$('.conversation-stack').addClass('inactive');
this.$('#header, .gutter').removeClass('inactive');
this.$('.conversation:first .menu').trigger('close');
},
reloadBackgroundPage() {
window.location.reload();
},
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;

View file

@ -1,43 +0,0 @@
// Copyright 2014-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* global Backbone, Whisper, _ */
// eslint-disable-next-line func-names
(function () {
window.Whisper = window.Whisper || {};
/*
* Generic list view that watches a given collection, wraps its members in
* a given child view and adds the child view elements to its own element.
*/
Whisper.ListView = Backbone.View.extend({
tagName: 'ul',
itemView: Backbone.View,
initialize(options) {
this.options = options || {};
this.listenTo(this.collection, 'add', this.addOne);
this.listenTo(this.collection, 'reset', this.addAll);
},
addOne(model) {
if (this.itemView) {
const options = _.extend({}, this.options.toInclude, { model });
// eslint-disable-next-line new-cap
const view = new this.itemView(options);
this.$el.append(view.render().el);
this.$el.trigger('add');
}
},
addAll() {
this.$el.html('');
this.collection.each(this.addOne, this);
},
render() {
this.addAll();
return this;
},
});
})();

View file

@ -1,41 +0,0 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* global Whisper, Signal, $ */
// eslint-disable-next-line func-names
(function () {
window.Whisper = window.Whisper || {};
Whisper.SafetyNumberChangeDialogView = Whisper.View.extend({
template: () => $('#safety-number-change-dialog').html(),
initialize(options) {
const dialog = new Whisper.ReactWrapperView({
Component: window.Signal.Components.SafetyNumberChangeDialog,
props: {
confirmText: options.confirmText,
contacts: options.contacts.map(contact => contact.format()),
i18n: window.i18n,
onCancel: () => {
dialog.remove();
this.remove();
options.reject();
},
onConfirm: () => {
dialog.remove();
this.remove();
options.resolve();
},
renderSafetyNumber(props) {
return Signal.State.Roots.createSafetyNumberViewer(
window.reduxStore,
props
);
},
},
});
this.$('.safety-number-change-dialog-wrapper').append(dialog.el);
},
});
})();

View file

@ -43,7 +43,6 @@
<script type='text/x-tmpl-mustache' id='two-column'> <script type='text/x-tmpl-mustache' id='two-column'>
<div class='module-title-bar-drag-area'></div> <div class='module-title-bar-drag-area'></div>
<div class='call-manager-placeholder'></div>
<div class='inbox-container'> <div class='inbox-container'>
<div class='gutter'> <div class='gutter'>
<div class='left-pane-placeholder'></div> <div class='left-pane-placeholder'></div>
@ -92,28 +91,12 @@
<script type='text/x-tmpl-mustache' id='conversation'> <script type='text/x-tmpl-mustache' id='conversation'>
<div class='conversation-header'></div> <div class='conversation-header'></div>
<div class='main panel'> <div class='main panel'>
<div class='discussion-container'> <div class='timeline-placeholder' aria-live='polite'></div>
<div class='bar-container hide'>
<div class='bar active progress-bar-striped progress-bar'></div>
</div>
</div>
<div class='bottom-bar' id='footer'> <div class='bottom-bar' id='footer'>
<div class='emoji-panel-container'></div>
<div class='attachment-list'></div>
<div class='compose'> <div class='compose'>
<form class='send clearfix file-input'> <form class='send clearfix file-input'>
<div class='flex'> <input type="file" class="file-input" multiple="multiple">
<button class='emoji'></button> <div class='composition-area-placeholder'></div>
<textarea class='send-message' placeholder='{{ send-message }}' rows='1' dir='auto'></textarea>
<div class='capture-audio'>
<button class='microphone'></button>
</div>
<div class='choose-file'>
<button class='paperclip thumbnail'></button>
<input type='file' class='file-input' multiple='multiple'>
</div>
</div>
</form> </form>
</div> </div>
</div> </div>
@ -131,10 +114,6 @@
<button class='close'><span class='icon'></span></button> <button class='close'><span class='icon'></span></button>
</script> </script>
<script type='text/x-tmpl-mustache' id='safety-number-change-dialog'>
<div class='safety-number-change-dialog-wrapper'></div>
</script>
<script type='text/x-tmpl-mustache' id='identicon-svg'> <script type='text/x-tmpl-mustache' id='identicon-svg'>
<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100'> <svg xmlns='http://www.w3.org/2000/svg' width='100' height='100'>
<circle cx='50' cy='50' r='40' fill='{{ color }}' /> <circle cx='50' cy='50' r='40' fill='{{ color }}' />

View file

@ -1,26 +0,0 @@
// Copyright 2014-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* global Backbone, Whisper */
describe('ListView', () => {
let collection;
beforeEach(() => {
collection = new Backbone.Collection();
});
it('should add children to the list element as they are added to the collection', () => {
const view = new Whisper.ListView({ collection });
collection.add('hello');
assert.equal(view.$el.children().length, 1);
collection.add('world');
assert.equal(view.$el.children().length, 2);
});
it('should add all the children to the list element on reset', () => {
const view = new Whisper.ListView({ collection });
collection.reset(['goodbye', 'world']);
assert.equal(view.$el.children().length, 2);
});
});

View file

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { AppViewType } from '../state/ducks/app'; import { AppViewType } from '../state/ducks/app';
@ -10,12 +10,16 @@ import { ThemeType } from '../types/Util';
export type PropsType = { export type PropsType = {
appView: AppViewType; appView: AppViewType;
hasInitialLoadCompleted: boolean; hasInitialLoadCompleted: boolean;
renderCallManager: () => JSX.Element;
renderGlobalModalContainer: () => JSX.Element;
theme: ThemeType; theme: ThemeType;
}; };
export const App = ({ export const App = ({
appView, appView,
hasInitialLoadCompleted, hasInitialLoadCompleted,
renderCallManager,
renderGlobalModalContainer,
theme, theme,
}: PropsType): JSX.Element => { }: PropsType): JSX.Element => {
let contents; let contents;
@ -28,6 +32,20 @@ export const App = ({
contents = <Inbox hasInitialLoadCompleted={hasInitialLoadCompleted} />; contents = <Inbox hasInitialLoadCompleted={hasInitialLoadCompleted} />;
} }
// This is here so that themes are properly applied to anything that is
// created in a portal and exists outside of the <App /> container.
useEffect(() => {
document.body.classList.remove('light-theme');
document.body.classList.remove('dark-theme');
if (theme === ThemeType.dark) {
document.body.classList.add('dark-theme');
}
if (theme === ThemeType.light) {
document.body.classList.add('light-theme');
}
}, [theme]);
return ( return (
<div <div
className={classNames({ className={classNames({
@ -36,6 +54,8 @@ export const App = ({
'dark-theme': theme === ThemeType.dark, 'dark-theme': theme === ThemeType.dark,
})} })}
> >
{renderGlobalModalContainer()}
{renderCallManager()}
{contents} {contents}
</div> </div>
); );

View file

@ -19,14 +19,13 @@ export type GroupV2Membership = {
export type Props = { export type Props = {
canAddNewMembers: boolean; canAddNewMembers: boolean;
i18n: LocalizerType;
maxShownMemberCount?: number;
memberships: Array<GroupV2Membership>; memberships: Array<GroupV2Membership>;
showContactModal: (conversationId: string) => void; showContactModal: (conversationId: string) => void;
startAddingNewMembers: () => void; startAddingNewMembers?: () => void;
i18n: LocalizerType;
}; };
const MAX_MEMBER_COUNT = 5;
const collator = new Intl.Collator(undefined, { sensitivity: 'base' }); const collator = new Intl.Collator(undefined, { sensitivity: 'base' });
function sortConversationTitles( function sortConversationTitles(
left: GroupV2Membership, left: GroupV2Membership,
@ -68,18 +67,20 @@ function sortMemberships(
export const ConversationDetailsMembershipList: React.ComponentType<Props> = ({ export const ConversationDetailsMembershipList: React.ComponentType<Props> = ({
canAddNewMembers, canAddNewMembers,
i18n,
maxShownMemberCount = 5,
memberships, memberships,
showContactModal, showContactModal,
startAddingNewMembers, startAddingNewMembers,
i18n,
}) => { }) => {
const [showAllMembers, setShowAllMembers] = React.useState<boolean>(false); const [showAllMembers, setShowAllMembers] = React.useState<boolean>(false);
const sortedMemberships = sortMemberships(memberships); const sortedMemberships = sortMemberships(memberships);
const shouldHideRestMembers = sortedMemberships.length - MAX_MEMBER_COUNT > 1; const shouldHideRestMembers =
sortedMemberships.length - maxShownMemberCount > 1;
const membersToShow = const membersToShow =
shouldHideRestMembers && !showAllMembers shouldHideRestMembers && !showAllMembers
? MAX_MEMBER_COUNT ? maxShownMemberCount
: sortedMemberships.length; : sortedMemberships.length;
return ( return (
@ -94,7 +95,7 @@ export const ConversationDetailsMembershipList: React.ComponentType<Props> = ({
<div className="module-conversation-details-membership-list__add-members-icon" /> <div className="module-conversation-details-membership-list__add-members-icon" />
} }
label={i18n('ConversationDetailsMembershipList--add-members')} label={i18n('ConversationDetailsMembershipList--add-members')}
onClick={startAddingNewMembers} onClick={() => startAddingNewMembers?.()}
/> />
)} )}
{sortedMemberships.slice(0, membersToShow).map(({ isAdmin, member }) => ( {sortedMemberships.slice(0, membersToShow).map(({ isAdmin, member }) => (

View file

@ -5405,23 +5405,3 @@ const sortConversationTitles = (
) => { ) => {
return collator.compare(left.getTitle(), right.getTitle()); return collator.compare(left.getTitle(), right.getTitle());
}; };
// We need a custom collection here to get the sorting we need
window.Whisper.GroupConversationCollection = window.Backbone.Collection.extend({
model: window.Whisper.GroupMemberConversation,
initialize() {
this.collator = new Intl.Collator(undefined, { sensitivity: 'base' });
},
comparator(left: WhatIsThis, right: WhatIsThis) {
if (left.isAdmin && !right.isAdmin) {
return -1;
}
if (!left.isAdmin && right.isAdmin) {
return 1;
}
return sortConversationTitles(left, right, this.collator);
},
});

View file

@ -17,8 +17,8 @@ type ConfirmationDialogViewProps = {
resolve: () => void; resolve: () => void;
}; };
let confirmationDialogViewNode: HTMLElement | null = null; let confirmationDialogViewNode: HTMLElement | undefined;
let confirmationDialogPreviousFocus: HTMLElement | null = null; let confirmationDialogPreviousFocus: HTMLElement | undefined;
function removeConfirmationDialog() { function removeConfirmationDialog() {
if (!confirmationDialogViewNode) { if (!confirmationDialogViewNode) {
@ -34,7 +34,7 @@ function removeConfirmationDialog() {
) { ) {
confirmationDialogPreviousFocus.focus(); confirmationDialogPreviousFocus.focus();
} }
confirmationDialogViewNode = null; confirmationDialogViewNode = undefined;
} }
function showConfirmationDialog(options: ConfirmationDialogViewProps) { function showConfirmationDialog(options: ConfirmationDialogViewProps) {

View file

@ -0,0 +1,67 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
// This file is here temporarily while we're switching off of Backbone into
// React. In the future, and in React-land, please just import and use
// the component directly. This is the thin API layer to bridge the gap
// while we convert things over. Please delete this file once all usages are
// ported over.
import React from 'react';
import { unmountComponentAtNode, render } from 'react-dom';
import { ConversationModel } from '../models/conversations';
import { SafetyNumberChangeDialog } from '../components/SafetyNumberChangeDialog';
export type SafetyNumberChangeViewProps = {
confirmText?: string;
contacts: Array<ConversationModel>;
reject: () => void;
resolve: () => void;
};
let dialogContainerNode: HTMLElement | undefined;
function removeDialog() {
if (!dialogContainerNode) {
return;
}
unmountComponentAtNode(dialogContainerNode);
document.body.removeChild(dialogContainerNode);
dialogContainerNode = undefined;
}
export function showSafetyNumberChangeDialog(
options: SafetyNumberChangeViewProps
): void {
if (dialogContainerNode) {
removeDialog();
}
dialogContainerNode = document.createElement('div');
document.body.appendChild(dialogContainerNode);
render(
<SafetyNumberChangeDialog
confirmText={options.confirmText}
contacts={options.contacts.map(contact => contact.format())}
i18n={window.i18n}
onCancel={() => {
options.reject();
removeDialog();
}}
onConfirm={() => {
options.resolve();
removeDialog();
}}
renderSafetyNumber={props => {
return window.Signal.State.Roots.createSafetyNumberViewer(
window.reduxStore,
props
);
}}
/>,
dialogContainerNode
);
}

View file

@ -1,15 +0,0 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { Provider } from 'react-redux';
import { Store } from 'redux';
import { SmartCallManager } from '../smart/CallManager';
export const createCallManager = (store: Store): React.ReactElement => (
<Provider store={store}>
<SmartCallManager />
</Provider>
);

View file

@ -1,17 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { Provider } from 'react-redux';
import { Store } from 'redux';
import { SmartGlobalModalContainer } from '../smart/GlobalModalContainer';
export const createGlobalModalContainer = (
store: Store
): React.ReactElement => (
<Provider store={store}>
<SmartGlobalModalContainer />
</Provider>
);

View file

@ -1,21 +0,0 @@
// 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);

25
ts/state/smart/App.tsx Normal file
View file

@ -0,0 +1,25 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { connect } from 'react-redux';
import { App, PropsType } from '../../components/App';
import { SmartCallManager } from './CallManager';
import { SmartGlobalModalContainer } from './GlobalModalContainer';
import { StateType } from '../reducer';
import { getTheme } from '../selectors/user';
import { mapDispatchToProps } from '../actions';
const mapStateToProps = (state: StateType): PropsType => {
return {
...state.app,
renderCallManager: () => <SmartCallManager />,
renderGlobalModalContainer: () => <SmartGlobalModalContainer />,
theme: getTheme(state),
};
};
const smart = connect(mapStateToProps, mapDispatchToProps);
export const SmartApp = smart(App);

View file

@ -252,22 +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": "jQuery-$(",
"path": "js/views/banner_view.js",
"line": " template: () => $('#banner').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{
"rule": "jQuery-html(",
"path": "js/views/banner_view.js",
"line": " template: () => $('#banner').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/clear_data_view.js", "path": "js/views/clear_data_view.js",
@ -284,30 +268,6 @@
"updated": "2021-02-26T18:44:56.450Z", "updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access" "reasonDetail": "Static selector, read-only access"
}, },
{
"rule": "jQuery-$(",
"path": "js/views/contact_list_view.js",
"line": " template: () => $('#contact').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{
"rule": "jQuery-append(",
"path": "js/views/contact_list_view.js",
"line": " this.$el.append(this.contactView.el);",
"reasonCategory": "usageTrusted",
"updated": "2019-07-31T00:19:18.696Z",
"reasonDetail": "Known DOM elements"
},
{
"rule": "jQuery-html(",
"path": "js/views/contact_list_view.js",
"line": " template: () => $('#contact').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
@ -420,38 +380,6 @@
"updated": "2021-02-26T18:44:56.450Z", "updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access" "reasonDetail": "Static selector, read-only access"
}, },
{
"rule": "jQuery-$(",
"path": "js/views/group_member_list_view.js",
"line": " this.$('.container').append(this.member_list_view.el);",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, adding sub-view to DOM"
},
{
"rule": "jQuery-$(",
"path": "js/views/group_member_list_view.js",
"line": " template: () => $('#group-member-list').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{
"rule": "jQuery-append(",
"path": "js/views/group_member_list_view.js",
"line": " this.$('.container').append(this.member_list_view.el);",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, adding sub-view to DOM"
},
{
"rule": "jQuery-html(",
"path": "js/views/group_member_list_view.js",
"line": " template: () => $('#group-member-list').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/identicon_svg_view.js", "path": "js/views/identicon_svg_view.js",
@ -476,14 +404,6 @@
"updated": "2021-02-26T18:44:56.450Z", "updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access" "reasonDetail": "Static selector, read-only access"
}, },
{
"rule": "jQuery-$(",
"path": "js/views/inbox_view.js",
"line": " this.$('.call-manager-placeholder').append(this.callManagerView.el);",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Adding sub-view to DOM"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/inbox_view.js", "path": "js/views/inbox_view.js",
@ -500,14 +420,6 @@
"updated": "2021-02-26T18:44:56.450Z", "updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Providing reference to DOM for sub-view" "reasonDetail": "Providing reference to DOM for sub-view"
}, },
{
"rule": "jQuery-$(",
"path": "js/views/inbox_view.js",
"line": " this.$('#header, .gutter').addClass('inactive');",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, adding or removing classes"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/inbox_view.js", "path": "js/views/inbox_view.js",
@ -516,22 +428,6 @@
"updated": "2021-02-26T18:44:56.450Z", "updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, adding or removing classes" "reasonDetail": "Static selector, adding or removing classes"
}, },
{
"rule": "jQuery-$(",
"path": "js/views/inbox_view.js",
"line": " this.$('.conversation-stack').addClass('inactive');",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, adding or removing classes"
},
{
"rule": "jQuery-$(",
"path": "js/views/inbox_view.js",
"line": " this.$('#header, .gutter').removeClass('inactive');",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, adding or removing classes"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/inbox_view.js", "path": "js/views/inbox_view.js",
@ -583,18 +479,9 @@
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/inbox_view.js", "path": "js/views/inbox_view.js",
"line": " this.$('.conversation:first .menu').trigger('close');", "line": " this.$('#header, .gutter').addClass('inactive');",
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z", "updated": "2021-06-15T23:46:51.629Z"
"reasonDetail": "Static selector, trigging DOM event"
},
{
"rule": "jQuery-append(",
"path": "js/views/inbox_view.js",
"line": " this.$('.call-manager-placeholder').append(this.callManagerView.el);",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Adding sub-view to DOM"
}, },
{ {
"rule": "jQuery-append(", "rule": "jQuery-append(",
@ -812,22 +699,6 @@
"updated": "2021-02-26T18:44:56.450Z", "updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access" "reasonDetail": "Static selector, read-only access"
}, },
{
"rule": "jQuery-append(",
"path": "js/views/list_view.js",
"line": " this.$el.append(view.render().el);",
"reasonCategory": "usageTrusted",
"updated": "2018-09-19T18:13:29.628Z",
"reasonDetail": "Interacting with already-existing DOM nodes"
},
{
"rule": "jQuery-html(",
"path": "js/views/list_view.js",
"line": " this.$el.html('');",
"reasonCategory": "usageTrusted",
"updated": "2018-09-15T00:38:04.183Z",
"reasonDetail": "Hard-coded value"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/phone-input-view.js", "path": "js/views/phone-input-view.js",
@ -932,36 +803,6 @@
"updated": "2021-02-26T18:44:56.450Z", "updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access" "reasonDetail": "Static selector, read-only access"
}, },
{
"rule": "jQuery-$(",
"path": "js/views/safety_number_change_dialog_view.js",
"line": " this.$('.safety-number-change-dialog-wrapper').append(dialog.el);",
"reasonCategory": "usageTrusted",
"updated": "2020-06-23T06:48:06.829Z"
},
{
"rule": "jQuery-$(",
"path": "js/views/safety_number_change_dialog_view.js",
"line": " template: () => $('#safety-number-change-dialog').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{
"rule": "jQuery-append(",
"path": "js/views/safety_number_change_dialog_view.js",
"line": " this.$('.safety-number-change-dialog-wrapper').append(dialog.el);",
"reasonCategory": "usageTrusted",
"updated": "2020-06-23T06:48:06.829Z"
},
{
"rule": "jQuery-html(",
"path": "js/views/safety_number_change_dialog_view.js",
"line": " template: () => $('#safety-number-change-dialog').html(),",
"reasonCategory": "usageTrusted",
"updated": "2021-02-26T18:44:56.450Z",
"reasonDetail": "Static selector, read-only access"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/settings_view.js", "path": "js/views/settings_view.js",

View file

@ -21,7 +21,6 @@ import { GroupNameCollisionsWithIdsByTitle } from '../util/groupMemberNameCollis
import { import {
isDirectConversation, isDirectConversation,
isGroupV1, isGroupV1,
isGroupV2,
isMe, isMe,
} from '../util/whatTypeOfConversation'; } from '../util/whatTypeOfConversation';
import { import {
@ -31,6 +30,8 @@ import {
isOutgoing, isOutgoing,
isTapToView, isTapToView,
} from '../state/selectors/message'; } from '../state/selectors/message';
import { ConversationDetailsMembershipList } from '../components/conversation/conversation-details/ConversationDetailsMembershipList';
import { showSafetyNumberChangeDialog } from '../shims/showSafetyNumberChangeDialog';
type GetLinkPreviewImageResult = { type GetLinkPreviewImageResult = {
data: ArrayBuffer; data: ArrayBuffer;
@ -367,7 +368,6 @@ Whisper.ConversationView = Whisper.View.extend({
// Events on Conversation model // Events on Conversation model
this.listenTo(this.model, 'destroy', this.stopListening); this.listenTo(this.model, 'destroy', this.stopListening);
this.listenTo(this.model, 'change:verified', this.onVerifiedChange);
this.listenTo(this.model, 'newmessage', this.lazyUpdateVerified); this.listenTo(this.model, 'newmessage', this.lazyUpdateVerified);
// These are triggered by InboxView // These are triggered by InboxView
@ -573,8 +573,8 @@ Whisper.ConversationView = Whisper.View.extend({
onShowAllMedia: () => { onShowAllMedia: () => {
this.showAllMedia(); this.showAllMedia();
}, },
onShowGroupMembers: async () => { onShowGroupMembers: () => {
await this.showMembers(); this.showGV1Members();
}, },
onGoBack: () => { onGoBack: () => {
this.resetPanel(); this.resetPanel();
@ -1446,9 +1446,6 @@ Whisper.ConversationView = Whisper.View.extend({
if (this.captureAudioView) { if (this.captureAudioView) {
this.captureAudioView.remove(); this.captureAudioView.remove();
} }
if (this.banner) {
this.banner.remove();
}
if (this.lastSeenIndicator) { if (this.lastSeenIndicator) {
this.lastSeenIndicator.remove(); this.lastSeenIndicator.remove();
} }
@ -2132,55 +2129,6 @@ Whisper.ConversationView = Whisper.View.extend({
return Promise.all(untrusted.map((contact: any) => contact.setApproved())); return Promise.all(untrusted.map((contact: any) => contact.setApproved()));
}, },
openSafetyNumberScreens(unverified: any) {
if (unverified.length === 1) {
this.showSafetyNumber(unverified.at(0).id);
return;
}
this.showMembers(null, unverified, { needVerify: true });
},
onVerifiedChange() {
const { model }: { model: ConversationModel } = this;
if (model.isUnverified()) {
const unverified = model.getUnverified();
let message;
if (!unverified.length) {
return;
}
if (unverified.length > 1) {
message = window.i18n('multipleNoLongerVerified');
} else {
message = window.i18n('noLongerVerified', [
unverified.at(0).getTitle(),
]);
}
// Need to re-add, since unverified set may have changed
if (this.banner) {
this.banner.remove();
this.banner = null;
}
this.banner = new Whisper.BannerView({
message,
onDismiss: () => {
this.markAllAsVerifiedDefault(unverified);
},
onClick: () => {
this.openSafetyNumberScreens(unverified);
},
});
const container = this.$('.discussion-container');
container.append(this.banner.el);
} else if (this.banner) {
this.banner.remove();
this.banner = null;
}
},
toggleMicrophone() { toggleMicrophone() {
this.compositionApi.current.setShowMic(!this.hasFiles()); this.compositionApi.current.setShowMic(!this.hasFiles());
}, },
@ -2314,7 +2262,6 @@ Whisper.ConversationView = Whisper.View.extend({
this.statusFetch = statusPromise.then(() => this.statusFetch = statusPromise.then(() =>
// eslint-disable-next-line more/no-then // eslint-disable-next-line more/no-then
model.updateVerified().then(() => { model.updateVerified().then(() => {
this.onVerifiedChange();
this.statusFetch = null; this.statusFetch = null;
}) })
); );
@ -2736,35 +2683,32 @@ Whisper.ConversationView = Whisper.View.extend({
this.compositionApi.current.resetEmojiResults(false); this.compositionApi.current.resetEmojiResults(false);
}, },
async showMembers( showGV1Members() {
_e: unknown,
providedMembers: void | Backbone.Collection<ConversationModel>,
options: any = {}
) {
const { model }: { model: ConversationModel } = this; const { model }: { model: ConversationModel } = this;
window._.defaults(options, { needVerify: false }); const { contactCollection } = model;
let contactCollection = providedMembers || model.contactCollection; const memberships =
contactCollection?.map((conversation: ConversationModel) => {
return {
isAdmin: false,
member: conversation.format(),
};
}) || [];
if (!providedMembers && isGroupV2(model.attributes)) { const view = new Whisper.ReactWrapperView({
contactCollection = new Whisper.GroupConversationCollection( className: 'group-member-list panel',
this.model.get('membersV2').map(({ conversationId, role }: any) => ({ Component: ConversationDetailsMembershipList,
conversation: window.ConversationController.get(conversationId), props: {
isAdmin: canAddNewMembers: false,
role === window.textsecure.protobuf.Member.Role.ADMINISTRATOR, i18n: window.i18n,
})) maxShownMemberCount: 32,
); memberships,
} showContactModal: this.showContactModal.bind(this),
},
const view = new Whisper.GroupMemberList({
model: contactCollection,
// we pass this in to allow nested panels
listenBack: this.listenBack.bind(this),
needVerify: options.needVerify,
conversation: model,
}); });
this.listenBack(view); this.listenBack(view);
view.render();
}, },
forceSend({ forceSend({
@ -3695,7 +3639,7 @@ Whisper.ConversationView = Whisper.View.extend({
confirmText?: string confirmText?: string
) { ) {
return new Promise(resolve => { return new Promise(resolve => {
const dialog = new Whisper.SafetyNumberChangeDialogView({ showSafetyNumberChangeDialog({
confirmText, confirmText,
contacts, contacts,
reject: () => { reject: () => {
@ -3705,8 +3649,6 @@ Whisper.ConversationView = Whisper.View.extend({
resolve(true); resolve(true);
}, },
}); });
this.$el.prepend(dialog.el);
}); });
}, },

5
ts/window.d.ts vendored
View file

@ -48,14 +48,12 @@ 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 { createApp } from './state/roots/createApp';
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';
import { createContactModal } from './state/roots/createContactModal'; import { createContactModal } from './state/roots/createContactModal';
import { createConversationDetails } from './state/roots/createConversationDetails'; import { createConversationDetails } from './state/roots/createConversationDetails';
import { createConversationHeader } from './state/roots/createConversationHeader'; import { createConversationHeader } from './state/roots/createConversationHeader';
import { createForwardMessageModal } from './state/roots/createForwardMessageModal'; import { createForwardMessageModal } from './state/roots/createForwardMessageModal';
import { createGlobalModalContainer } from './state/roots/createGlobalModalContainer';
import { createGroupLinkManagement } from './state/roots/createGroupLinkManagement'; import { createGroupLinkManagement } from './state/roots/createGroupLinkManagement';
import { createGroupV1MigrationModal } from './state/roots/createGroupV1MigrationModal'; import { createGroupV1MigrationModal } from './state/roots/createGroupV1MigrationModal';
import { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal'; import { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
@ -480,14 +478,12 @@ declare global {
createStore: typeof createStore; createStore: typeof createStore;
Roots: { Roots: {
createApp: typeof createApp; createApp: typeof createApp;
createCallManager: typeof createCallManager;
createChatColorPicker: typeof createChatColorPicker; createChatColorPicker: typeof createChatColorPicker;
createCompositionArea: typeof createCompositionArea; createCompositionArea: typeof createCompositionArea;
createContactModal: typeof createContactModal; createContactModal: typeof createContactModal;
createConversationDetails: typeof createConversationDetails; createConversationDetails: typeof createConversationDetails;
createConversationHeader: typeof createConversationHeader; createConversationHeader: typeof createConversationHeader;
createForwardMessageModal: typeof createForwardMessageModal; createForwardMessageModal: typeof createForwardMessageModal;
createGlobalModalContainer: typeof createGlobalModalContainer;
createGroupLinkManagement: typeof createGroupLinkManagement; createGroupLinkManagement: typeof createGroupLinkManagement;
createGroupV1MigrationModal: typeof createGroupV1MigrationModal; createGroupV1MigrationModal: typeof createGroupV1MigrationModal;
createGroupV2JoinModal: typeof createGroupV2JoinModal; createGroupV2JoinModal: typeof createGroupV2JoinModal;
@ -658,7 +654,6 @@ export type WhisperType = {
reject: Function reject: Function
) => void; ) => void;
}; };
GroupConversationCollection: typeof ConversationModelCollectionType;
ConversationCollection: typeof ConversationModelCollectionType; ConversationCollection: typeof ConversationModelCollectionType;
ConversationCollectionType: ConversationModelCollectionType; ConversationCollectionType: ConversationModelCollectionType;
Conversation: typeof ConversationModel; Conversation: typeof ConversationModel;