do not pass props not needed between message list components

pull/1884/head
audric 4 years ago
parent ea2dbb4a69
commit 7fa50b4a73

@ -17,7 +17,6 @@
"postinstall": "electron-builder install-app-deps && rimraf node_modules/dtrace-provider", "postinstall": "electron-builder install-app-deps && rimraf node_modules/dtrace-provider",
"start": "cross-env NODE_APP_INSTANCE=$MULTI electron .", "start": "cross-env NODE_APP_INSTANCE=$MULTI electron .",
"start-prod": "cross-env NODE_ENV=production NODE_APP_INSTANCE=devprod$MULTI electron .", "start-prod": "cross-env NODE_ENV=production NODE_APP_INSTANCE=devprod$MULTI electron .",
"start-prod2": "cross-env NODE_ENV=production NODE_APP_INSTANCE=devprod2 electron .",
"grunt": "grunt", "grunt": "grunt",
"grunt:dev": "yarn clean-transpile; yarn grunt dev --force", "grunt:dev": "yarn clean-transpile; yarn grunt dev --force",
"generate": "yarn grunt --force", "generate": "yarn grunt --force",
@ -118,7 +117,6 @@
"redux": "4.0.1", "redux": "4.0.1",
"redux-logger": "3.0.6", "redux-logger": "3.0.6",
"redux-persist": "^6.0.0", "redux-persist": "^6.0.0",
"redux-promise-middleware": "6.1.0",
"reselect": "4.0.0", "reselect": "4.0.0",
"rimraf": "2.6.2", "rimraf": "2.6.2",
"sanitize.css": "^12.0.1", "sanitize.css": "^12.0.1",

@ -199,7 +199,7 @@ const MessageItem = (props: {
if (!lastMessage && !isTyping) { if (!lastMessage && !isTyping) {
return null; return null;
} }
const text = lastMessage && lastMessage.text ? lastMessage.text : ''; const text = lastMessage?.text || '';
if (isEmpty(text)) { if (isEmpty(text)) {
return null; return null;
@ -280,7 +280,7 @@ const ConversationListItem = (props: Props) => {
const membersAvatar = useMembersAvatars(props); const membersAvatar = useMembersAvatars(props);
const openConvo = useCallback( const openConvo = useCallback(
async (e: any) => { async (e: React.MouseEvent<HTMLDivElement>) => {
// mousedown is invoked sooner than onClick, but for both right and left click // mousedown is invoked sooner than onClick, but for both right and left click
if (e.button === 0) { if (e.button === 0) {
await openConversationWithMessages({ conversationKey: conversationId }); await openConversationWithMessages({ conversationKey: conversationId });
@ -294,6 +294,10 @@ const ConversationListItem = (props: Props) => {
<div <div
role="button" role="button"
onMouseDown={openConvo} onMouseDown={openConvo}
onMouseUp={e => {
e.stopPropagation();
e.preventDefault();
}}
onContextMenu={(e: any) => { onContextMenu={(e: any) => {
contextMenu.show({ contextMenu.show({
id: triggerId, id: triggerId,

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useAppIsFocused } from '../hooks/useAppFocused';
import { getFocusedSettingsSection } from '../state/selectors/section'; import { getFocusedSettingsSection } from '../state/selectors/section';
import { SmartSessionConversation } from '../state/smart/SessionConversation'; import { SmartSessionConversation } from '../state/smart/SessionConversation';
@ -11,6 +12,9 @@ export const SessionMainPanel = () => {
const focusedSettingsSection = useSelector(getFocusedSettingsSection); const focusedSettingsSection = useSelector(getFocusedSettingsSection);
const isSettingsView = focusedSettingsSection !== undefined; const isSettingsView = focusedSettingsSection !== undefined;
// even if it looks like this does nothing, this does update the redux store.
useAppIsFocused();
if (isSettingsView) { if (isSettingsView) {
return <FilteredSettingsView category={focusedSettingsSection} />; return <FilteredSettingsView category={focusedSettingsSection} />;
} }

@ -3,7 +3,6 @@ import React, { useCallback } from 'react';
import { InView } from 'react-intersection-observer'; import { InView } from 'react-intersection-observer';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { getMessageById } from '../../data/data'; import { getMessageById } from '../../data/data';
import { useAppIsFocused } from '../../hooks/useAppFocused';
import { Constants } from '../../session'; import { Constants } from '../../session';
import { getConversationController } from '../../session/conversations'; import { getConversationController } from '../../session/conversations';
import { import {
@ -19,6 +18,7 @@ import {
getOldestMessageId, getOldestMessageId,
getSelectedConversationKey, getSelectedConversationKey,
} from '../../state/selectors/conversations'; } from '../../state/selectors/conversations';
import { getIsAppFocused } from '../../state/selectors/section';
type ReadableMessageProps = { type ReadableMessageProps = {
children: React.ReactNode; children: React.ReactNode;
@ -45,7 +45,7 @@ const debouncedTriggerLoadMore = _.debounce(
export const ReadableMessage = (props: ReadableMessageProps) => { export const ReadableMessage = (props: ReadableMessageProps) => {
const { messageId, onContextMenu, className, receivedAt, isUnread } = props; const { messageId, onContextMenu, className, receivedAt, isUnread } = props;
const isAppFocused = useAppIsFocused(); const isAppFocused = useSelector(getIsAppFocused);
const dispatch = useDispatch(); const dispatch = useDispatch();
// onVisible={haveDoneFirstScrollProp ? onVisible : noop} // onVisible={haveDoneFirstScrollProp ? onVisible : noop}

@ -49,10 +49,16 @@ export class EditProfileDialog extends React.Component<{}, State> {
}; };
this.inputEl = React.createRef(); this.inputEl = React.createRef();
}
public componentDidMount() {
window.addEventListener('keyup', this.onKeyUp); window.addEventListener('keyup', this.onKeyUp);
} }
public componentWillUnmount() {
window.removeEventListener('keyup', this.onKeyUp);
}
public render() { public render() {
const i18n = window.i18n; const i18n = window.i18n;

@ -13,6 +13,8 @@ import { SessionWrapperModal } from '../session/SessionWrapperModal';
import { SpacerLG } from '../basic/Text'; import { SpacerLG } from '../basic/Text';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { updateInviteContactModal } from '../../state/ducks/modalDialog'; import { updateInviteContactModal } from '../../state/ducks/modalDialog';
// tslint:disable-next-line: no-submodule-imports
import useKey from 'react-use/lib/useKey';
type Props = { type Props = {
conversationId: string; conversationId: string;
@ -67,7 +69,6 @@ const InviteContactsDialogInner = (props: Props) => {
); );
const closeDialog = () => { const closeDialog = () => {
window.removeEventListener('keyup', onKeyUp);
dispatch(updateInviteContactModal(null)); dispatch(updateInviteContactModal(null));
}; };
@ -87,19 +88,13 @@ const InviteContactsDialogInner = (props: Props) => {
closeDialog(); closeDialog();
}; };
const onKeyUp = (event: any) => { useKey((event: KeyboardEvent) => {
switch (event.key) { return event.key === 'Enter';
case 'Enter': }, onClickOK);
onClickOK();
break; useKey((event: KeyboardEvent) => {
case 'Esc': return event.key === 'Esc' || event.key === 'Escape';
case 'Escape': }, closeDialog);
closeDialog();
break;
default:
}
};
window.addEventListener('keyup', onKeyUp);
const titleText = `${window.i18n('addingContacts')} ${chatName}`; const titleText = `${window.i18n('addingContacts')} ${chatName}`;
const cancelText = window.i18n('cancel'); const cancelText = window.i18n('cancel');

@ -48,15 +48,17 @@ export class SessionModal extends React.PureComponent<Props, State> {
this.close = this.close.bind(this); this.close = this.close.bind(this);
this.onKeyUp = this.onKeyUp.bind(this); this.onKeyUp = this.onKeyUp.bind(this);
this.node = null; this.node = null;
window.addEventListener('keyup', this.onKeyUp);
} }
public componentDidMount() { public componentDidMount() {
window.addEventListener('keyup', this.onKeyUp);
document.addEventListener('mousedown', this.handleClick, false); document.addEventListener('mousedown', this.handleClick, false);
} }
public componentWillUnmount() { public componentWillUnmount() {
window.removeEventListener('keyup', this.onKeyUp);
document.removeEventListener('mousedown', this.handleClick, false); document.removeEventListener('mousedown', this.handleClick, false);
} }
@ -118,7 +120,6 @@ export class SessionModal extends React.PureComponent<Props, State> {
isVisible: false, isVisible: false,
}); });
window.removeEventListener('keyup', this.onKeyUp);
document.removeEventListener('mousedown', this.handleClick, false); document.removeEventListener('mousedown', this.handleClick, false);
if (this.props.onClose) { if (this.props.onClose) {

@ -109,10 +109,16 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
admins, admins,
isAdmin, isAdmin,
}; };
}
public componentDidMount() {
window.addEventListener('keyup', this.onKeyUp); window.addEventListener('keyup', this.onKeyUp);
} }
public componentWillUnmount() {
window.removeEventListener('keyup', this.onKeyUp);
}
public onClickOK() { public onClickOK() {
const members = this.getWouldBeMembers(this.state.contactList).map(d => d.id); const members = this.getWouldBeMembers(this.state.contactList).map(d => d.id);
@ -243,8 +249,6 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
} }
private closeDialog() { private closeDialog() {
window.removeEventListener('keyup', this.onKeyUp);
window.inboxStore?.dispatch(updateGroupMembersModal(null)); window.inboxStore?.dispatch(updateGroupMembersModal(null));
} }

@ -39,9 +39,16 @@ export class UpdateGroupNameDialog extends React.Component<Props, State> {
avatar: this.convo.getAvatarPath(), avatar: this.convo.getAvatarPath(),
}; };
this.inputEl = React.createRef(); this.inputEl = React.createRef();
}
public componentDidMount() {
window.addEventListener('keyup', this.onKeyUp); window.addEventListener('keyup', this.onKeyUp);
} }
public componentWillUnmount() {
window.removeEventListener('keyup', this.onKeyUp);
}
public onClickOK() { public onClickOK() {
if (!this.state.groupName.trim()) { if (!this.state.groupName.trim()) {
this.onShowError(window.i18n('emptyGroupNameError')); this.onShowError(window.i18n('emptyGroupNameError'));

@ -173,7 +173,7 @@ const removeAllV1OpenGroups = async () => {
if (window.inboxStore) { if (window.inboxStore) {
window.inboxStore?.dispatch(conversationRemoved(v1Convo.id)); window.inboxStore?.dispatch(conversationRemoved(v1Convo.id));
window.inboxStore?.dispatch( window.inboxStore?.dispatch(
conversationChanged({ id: v1Convo.id, data: v1Convo.getProps() }) conversationChanged({ id: v1Convo.id, data: v1Convo.getConversationModelProps() })
); );
} }
} catch (e) { } catch (e) {

@ -49,14 +49,19 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
this.onKeyUp = this.onKeyUp.bind(this); this.onKeyUp = this.onKeyUp.bind(this);
this.onGroupNameChanged = this.onGroupNameChanged.bind(this); this.onGroupNameChanged = this.onGroupNameChanged.bind(this);
window.addEventListener('keyup', this.onKeyUp);
} }
public componentDidMount() { public componentDidMount() {
window.addEventListener('keyup', this.onKeyUp);
if (this.inputRef.current) { if (this.inputRef.current) {
this.inputRef.current.focus(); this.inputRef.current.focus();
} }
} }
public componentWillUnmount() {
window.removeEventListener('keyup', this.onKeyUp);
}
public getContacts() { public getContacts() {
const { overlayMode } = this.props; const { overlayMode } = this.props;

@ -76,7 +76,7 @@ export class SessionInboxView extends React.Component<any, State> {
// Here we set up a full redux store with initial state for our LeftPane Root // Here we set up a full redux store with initial state for our LeftPane Root
const convoCollection = getConversationController().getConversations(); const convoCollection = getConversationController().getConversations();
const conversations = convoCollection.map((conversation: ConversationModel) => const conversations = convoCollection.map((conversation: ConversationModel) =>
conversation.getProps() conversation.getConversationModelProps()
); );
const filledConversations = conversations.map((conv: any) => { const filledConversations = conversations.map((conv: any) => {

@ -166,7 +166,7 @@ class SessionCompositionBoxInner extends React.Component<Props, State> {
} }
public componentDidMount() { public componentDidMount() {
setTimeout(this.focusCompositionBox, 100); setTimeout(this.focusCompositionBox, 500);
const div = this.container; const div = this.container;
div?.addEventListener('paste', this.handlePaste); div?.addEventListener('paste', this.handlePaste);

@ -1,8 +1,7 @@
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { QuoteClickOptions } from '../../../models/messageType'; import { QuoteClickOptions } from '../../../models/messageType';
import { SortedMessageModelProps } from '../../../state/ducks/conversations'; import { getSortedMessagesTypesOfSelectedConversation } from '../../../state/selectors/conversations';
import { getSortedMessagesOfSelectedConversation } from '../../../state/selectors/conversations';
import { import {
DataExtractionNotificationItem, DataExtractionNotificationItem,
GenericMessageItem, GenericMessageItem,
@ -14,61 +13,57 @@ import {
export const SessionMessagesList = (props: { export const SessionMessagesList = (props: {
scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>; scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>;
}) => { }) => {
const messagesProps = useSelector(getSortedMessagesOfSelectedConversation); const messagesProps = useSelector(getSortedMessagesTypesOfSelectedConversation);
return ( return (
<> <>
{messagesProps.map((messageProps: SortedMessageModelProps) => { {messagesProps.map(messageProps => {
const timerProps = messageProps.propsForTimerNotification; if (messageProps.messageType === 'group-notification') {
const propsForGroupInvitation = messageProps.propsForGroupInvitation;
const propsForDataExtractionNotification = messageProps.propsForDataExtractionNotification;
const groupNotificationProps = messageProps.propsForGroupNotification;
if (groupNotificationProps) {
return ( return (
<GroupUpdateItem <GroupUpdateItem
key={messageProps.propsForMessage.id} key={messageProps.props.messageId}
groupNotificationProps={groupNotificationProps} groupNotificationProps={messageProps.props}
/> />
); );
} }
if (propsForGroupInvitation) { if (messageProps.messageType === 'group-invitation') {
return ( return (
<GroupInvitationItem <GroupInvitationItem
key={messageProps.propsForMessage.id} key={messageProps.props.messageId}
propsForGroupInvitation={propsForGroupInvitation} propsForGroupInvitation={messageProps.props}
/> />
); );
} }
if (propsForDataExtractionNotification) { if (messageProps.messageType === 'data-extraction') {
return ( return (
<DataExtractionNotificationItem <DataExtractionNotificationItem
key={messageProps.propsForMessage.id} key={messageProps.props.messageId}
propsForDataExtractionNotification={propsForDataExtractionNotification} propsForDataExtractionNotification={messageProps.props}
/> />
); );
} }
if (timerProps) { if (messageProps.messageType === 'timer-notification') {
return ( return (
<TimerNotificationItem key={messageProps.propsForMessage.id} timerProps={timerProps} /> <TimerNotificationItem
key={messageProps.props.messageId}
timerProps={messageProps.props}
/>
); );
} }
if (!messageProps) { if (!messageProps) {
return; return null;
} }
// firstMessageOfSeries tells us to render the avatar only for the first message // firstMessageOfSeries tells us to render the avatar only for the first message
// in a series of messages from the same user // in a series of messages from the same user
return ( return (
<GenericMessageItem <GenericMessageItem
key={messageProps.propsForMessage.id} key={messageProps.props.messageId}
messageId={messageProps.propsForMessage.id} messageId={messageProps.props.messageId}
messageProps={messageProps}
scrollToQuoteMessage={props.scrollToQuoteMessage} scrollToQuoteMessage={props.scrollToQuoteMessage}
/> />
); );

@ -5,7 +5,6 @@ import {
PropsForExpirationTimer, PropsForExpirationTimer,
PropsForGroupInvitation, PropsForGroupInvitation,
PropsForGroupUpdate, PropsForGroupUpdate,
SortedMessageModelProps,
} from '../../../state/ducks/conversations'; } from '../../../state/ducks/conversations';
import { getFirstUnreadMessageId } from '../../../state/selectors/conversations'; import { getFirstUnreadMessageId } from '../../../state/selectors/conversations';
import { DataExtractionNotification } from '../../conversation/DataExtractionNotification'; import { DataExtractionNotification } from '../../conversation/DataExtractionNotification';
@ -77,18 +76,13 @@ export const TimerNotificationItem = (props: { timerProps: PropsForExpirationTim
export const GenericMessageItem = (props: { export const GenericMessageItem = (props: {
messageId: string; messageId: string;
messageProps: SortedMessageModelProps;
scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>; scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>;
}) => { }) => {
const messageId = props.messageId; const messageId = props.messageId;
const onQuoteClick = props.messageProps.propsForMessage.quote
? props.scrollToQuoteMessage
: undefined;
return ( return (
<React.Fragment key={messageId}> <React.Fragment key={messageId}>
<Message messageId={messageId} onQuoteClick={onQuoteClick} key={messageId} /> <Message messageId={messageId} onQuoteClick={props.scrollToQuoteMessage} key={messageId} />
<UnreadIndicator messageId={messageId} /> <UnreadIndicator messageId={messageId} />
</React.Fragment> </React.Fragment>
); );

@ -93,16 +93,21 @@ class SettingsViewInner extends React.Component<SettingsViewProps, State> {
void this.hasPassword(); void this.hasPassword();
this.onKeyUp = this.onKeyUp.bind(this); this.onKeyUp = this.onKeyUp.bind(this);
window.addEventListener('keyup', this.onKeyUp);
} }
public componentDidMount() { public componentDidMount() {
window.addEventListener('keyup', this.onKeyUp);
const mediaSetting = window.getSettingValue('media-permissions'); const mediaSetting = window.getSettingValue('media-permissions');
this.setState({ mediaSetting }); this.setState({ mediaSetting });
setTimeout(() => ($('#password-lock-input') as any).focus(), 100); setTimeout(() => ($('#password-lock-input') as any).focus(), 100);
} }
public componentWillUnmount() {
window.removeEventListener('keyup', this.onKeyUp);
}
/* tslint:disable-next-line:max-func-body-length */ /* tslint:disable-next-line:max-func-body-length */
public renderSettingInCategory() { public renderSettingInCategory() {
const { category } = this.props; const { category } = this.props;

@ -1,31 +1,29 @@
import { remote } from 'electron'; import { remote } from 'electron';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { isElectronWindowFocused } from '../session/utils/WindowUtils'; import { isElectronWindowFocused } from '../session/utils/WindowUtils';
import { setIsAppFocused } from '../state/ducks/section';
import { getIsAppFocused } from '../state/selectors/section';
export function useAppIsFocused() { export function useAppIsFocused() {
const [isAppFocused, setIsAppFocused] = useState(false); const dispatch = useDispatch();
const isFocused = useSelector(getIsAppFocused);
useEffect(() => { useEffect(() => {
setIsAppFocused(isElectronWindowFocused()); dispatch(setIsAppFocused(isElectronWindowFocused()));
}, []); }, []);
const onFocusCallback = useCallback( const onFocusCallback = useCallback((_event, win) => {
(_event, win) => { if (win.webContents.id === 1) {
if (win.webContents.id === 1) { dispatch(setIsAppFocused(true));
setIsAppFocused(true); }
} }, []);
},
[setIsAppFocused]
);
const onBlurCallback = useCallback( const onBlurCallback = useCallback((_event, win) => {
(_event, win) => { if (win.webContents.id === 1) {
if (win.webContents.id === 1) { dispatch(setIsAppFocused(false));
setIsAppFocused(false); }
} }, []);
},
[setIsAppFocused]
);
useEffect(() => { useEffect(() => {
remote.app.on('browser-window-focus', onFocusCallback); remote.app.on('browser-window-focus', onFocusCallback);
@ -36,5 +34,5 @@ export function useAppIsFocused() {
}; };
}); });
return isAppFocused; return isFocused;
} }

@ -1,6 +1,7 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { getDecryptedMediaUrl } from '../session/crypto/DecryptedAttachmentsManager'; import { getDecryptedMediaUrl } from '../session/crypto/DecryptedAttachmentsManager';
import { perfEnd, perfStart } from '../session/utils/Performance';
export const useEncryptedFileFetch = (url: string, contentType: string) => { export const useEncryptedFileFetch = (url: string, contentType: string) => {
// tslint:disable-next-line: no-bitwise // tslint:disable-next-line: no-bitwise
@ -10,7 +11,11 @@ export const useEncryptedFileFetch = (url: string, contentType: string) => {
const mountedRef = useRef(true); const mountedRef = useRef(true);
async function fetchUrl() { async function fetchUrl() {
perfStart(`getDecryptedMediaUrl${url}`);
const decryptedUrl = await getDecryptedMediaUrl(url, contentType); const decryptedUrl = await getDecryptedMediaUrl(url, contentType);
perfEnd(`getDecryptedMediaUrl${url}`, 'getDecryptedMediaUrl');
if (mountedRef.current) { if (mountedRef.current) {
setUrlToLoad(decryptedUrl); setUrlToLoad(decryptedUrl);

@ -223,7 +223,9 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
this.typingRefreshTimer = null; this.typingRefreshTimer = null;
this.typingPauseTimer = null; this.typingPauseTimer = null;
this.lastReadTimestamp = 0; this.lastReadTimestamp = 0;
window.inboxStore?.dispatch(conversationChanged({ id: this.id, data: this.getProps() })); window.inboxStore?.dispatch(
conversationChanged({ id: this.id, data: this.getConversationModelProps() })
);
} }
public idForLogging() { public idForLogging() {
@ -407,7 +409,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
return this.get('moderators'); return this.get('moderators');
} }
public getProps(): ReduxConversationType { public getConversationModelProps(): ReduxConversationType {
const groupAdmins = this.getGroupAdmins(); const groupAdmins = this.getGroupAdmins();
const members = this.isGroup() && !this.isPublic() ? this.get('members') : []; const members = this.isGroup() && !this.isPublic() ? this.get('members') : [];
const ourNumber = UserUtils.getOurPubKeyStrFromCache(); const ourNumber = UserUtils.getOurPubKeyStrFromCache();
@ -804,13 +806,8 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
lastMessageStatus: lastMessageStatusModel, lastMessageStatus: lastMessageStatusModel,
lastMessageNotificationText: lastMessageModel ? lastMessageModel.getNotificationText() : null, lastMessageNotificationText: lastMessageModel ? lastMessageModel.getNotificationText() : null,
}); });
// Because we're no longer using Backbone-integrated saves, we need to manually
// clear the changed fields here so our hasChanged() check below is useful.
(this as any).changed = {};
this.set(lastMessageUpdate); this.set(lastMessageUpdate);
if (this.hasChanged()) { await this.commit();
await this.commit();
}
} }
public async updateExpireTimer( public async updateExpireTimer(
@ -917,7 +914,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
conversationChanged({ conversationChanged({
id: this.id, id: this.id,
data: { data: {
...this.getProps(), ...this.getConversationModelProps(),
isSelected: false, isSelected: false,
}, },
}) })
@ -945,7 +942,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
window.inboxStore?.dispatch( window.inboxStore?.dispatch(
conversationActions.messageAdded({ conversationActions.messageAdded({
conversationKey: this.id, conversationKey: this.id,
messageModelProps: model.getProps(), messageModelProps: model.getMessageModelProps(),
}) })
); );
const unreadCount = await this.getUnreadCount(); const unreadCount = await this.getUnreadCount();
@ -1006,7 +1003,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
const allProps: Array<MessageModelPropsWithoutConvoProps> = []; const allProps: Array<MessageModelPropsWithoutConvoProps> = [];
for (const nowRead of oldUnreadNowRead) { for (const nowRead of oldUnreadNowRead) {
allProps.push(nowRead.getProps()); allProps.push(nowRead.getMessageModelProps());
} }
if (allProps.length) { if (allProps.length) {

@ -80,10 +80,10 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
window.contextMenuShown = false; window.contextMenuShown = false;
this.getProps(); this.getMessageModelProps();
} }
public getProps(): MessageModelPropsWithoutConvoProps { public getMessageModelProps(): MessageModelPropsWithoutConvoProps {
perfStart(`getPropsMessage-${this.id}`); perfStart(`getPropsMessage-${this.id}`);
const messageProps: MessageModelPropsWithoutConvoProps = { const messageProps: MessageModelPropsWithoutConvoProps = {
propsForMessage: this.getPropsForMessage(), propsForMessage: this.getPropsForMessage(),
@ -1121,7 +1121,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
} }
} }
private dispatchMessageUpdate() { private dispatchMessageUpdate() {
updatesToDispatch.set(this.id, this.getProps()); updatesToDispatch.set(this.id, this.getMessageModelProps());
trotthledAllMessagesDispatch(); trotthledAllMessagesDispatch();
} }
} }

@ -38,7 +38,7 @@ export async function onError(ev: any) {
window.inboxStore?.dispatch( window.inboxStore?.dispatch(
conversationActions.messageAdded({ conversationActions.messageAdded({
conversationKey: conversation.id, conversationKey: conversation.id,
messageModelProps: message.getProps(), messageModelProps: message.getMessageModelProps(),
}) })
); );

@ -454,7 +454,7 @@ export async function handleMessageJob(
updatesToDispatch.set(message.id, { updatesToDispatch.set(message.id, {
conversationKey: conversation.id, conversationKey: conversation.id,
messageModelProps: message.getProps(), messageModelProps: message.getMessageModelProps(),
}); });
trotthledAllMessagesAddedDispatch(); trotthledAllMessagesAddedDispatch();
if (message.get('unread')) { if (message.get('unread')) {

@ -120,7 +120,7 @@ export class ConversationController {
window.inboxStore?.dispatch( window.inboxStore?.dispatch(
conversationActions.conversationAdded({ conversationActions.conversationAdded({
id: conversation.id, id: conversation.id,
data: conversation.getProps(), data: conversation.getConversationModelProps(),
}) })
); );
} }
@ -249,7 +249,7 @@ export class ConversationController {
window.inboxStore?.dispatch( window.inboxStore?.dispatch(
conversationActions.conversationChanged({ conversationActions.conversationChanged({
id: conversation.id, id: conversation.id,
data: conversation.getProps(), data: conversation.getConversationModelProps(),
}) })
); );
window.inboxStore?.dispatch(conversationActions.conversationRemoved(conversation.id)); window.inboxStore?.dispatch(conversationActions.conversationRemoved(conversation.id));

@ -1,4 +1,3 @@
import promise from 'redux-promise-middleware';
import { createLogger } from 'redux-logger'; import { createLogger } from 'redux-logger';
import { configureStore } from '@reduxjs/toolkit'; import { configureStore } from '@reduxjs/toolkit';
import { rootReducer } from './reducer'; import { rootReducer } from './reducer';
@ -6,7 +5,6 @@ import { persistReducer } from 'redux-persist';
// tslint:disable-next-line: no-submodule-imports match-default-export-name // tslint:disable-next-line: no-submodule-imports match-default-export-name
import storage from 'redux-persist/lib/storage'; import storage from 'redux-persist/lib/storage';
// @ts-ignore // @ts-ignore
const env = window.getEnvironment(); const env = window.getEnvironment();

@ -300,7 +300,7 @@ async function getMessages(
}); });
const messageProps: Array<MessageModelPropsWithoutConvoProps> = messageSet.models.map(m => const messageProps: Array<MessageModelPropsWithoutConvoProps> = messageSet.models.map(m =>
m.getProps() m.getMessageModelProps()
); );
return messageProps; return messageProps;
} }
@ -326,11 +326,11 @@ export const fetchMessagesForConversation = createAsyncThunk(
}): Promise<FetchedMessageResults> => { }): Promise<FetchedMessageResults> => {
const beforeTimestamp = Date.now(); const beforeTimestamp = Date.now();
// tslint:disable-next-line: no-console // tslint:disable-next-line: no-console
console.time('fetchMessagesForConversation'); perfStart('fetchMessagesForConversation');
const messagesProps = await getMessages(conversationKey, count); const messagesProps = await getMessages(conversationKey, count);
const afterTimestamp = Date.now(); const afterTimestamp = Date.now();
// tslint:disable-next-line: no-console // tslint:disable-next-line: no-console
console.timeEnd('fetchMessagesForConversation'); perfEnd('fetchMessagesForConversation', 'fetchMessagesForConversation');
const time = afterTimestamp - beforeTimestamp; const time = afterTimestamp - beforeTimestamp;
window?.log?.info(`Loading ${messagesProps.length} messages took ${time}ms to load.`); window?.log?.info(`Loading ${messagesProps.length} messages took ${time}ms to load.`);
@ -824,10 +824,16 @@ export async function openConversationWithMessages(args: {
messageId?: string; messageId?: string;
}) { }) {
const { conversationKey, messageId } = args; const { conversationKey, messageId } = args;
perfStart('getFirstUnreadMessageIdInConversation');
const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(conversationKey); const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(conversationKey);
perfEnd('getFirstUnreadMessageIdInConversation', 'getFirstUnreadMessageIdInConversation');
// preload 30 messages // preload 30 messages
perfStart('getMessages');
const initialMessages = await getMessages(conversationKey, 30); const initialMessages = await getMessages(conversationKey, 30);
perfEnd('getMessages', 'getMessages');
console.warn('initialMessages', initialMessages);
window.inboxStore?.dispatch( window.inboxStore?.dispatch(
actions.openConversationExternal({ actions.openConversationExternal({

@ -2,6 +2,7 @@ import { SessionSettingCategory } from '../../components/session/settings/Sessio
export const FOCUS_SECTION = 'FOCUS_SECTION'; export const FOCUS_SECTION = 'FOCUS_SECTION';
export const FOCUS_SETTINGS_SECTION = 'FOCUS_SETTINGS_SECTION'; export const FOCUS_SETTINGS_SECTION = 'FOCUS_SETTINGS_SECTION';
export const IS_APP_FOCUSED = 'IS_APP_FOCUSED';
export enum SectionType { export enum SectionType {
Profile, Profile,
@ -23,6 +24,11 @@ type FocusSettingsSectionActionType = {
payload: SessionSettingCategory; payload: SessionSettingCategory;
}; };
type IsAppFocusedActionType = {
type: 'IS_APP_FOCUSED';
payload: boolean;
};
export function showLeftPaneSection(section: SectionType): FocusSectionActionType { export function showLeftPaneSection(section: SectionType): FocusSectionActionType {
return { return {
type: FOCUS_SECTION, type: FOCUS_SECTION,
@ -32,6 +38,13 @@ export function showLeftPaneSection(section: SectionType): FocusSectionActionTyp
type SectionActionTypes = FocusSectionActionType | FocusSettingsSectionActionType; type SectionActionTypes = FocusSectionActionType | FocusSettingsSectionActionType;
export function setIsAppFocused(focused: boolean): IsAppFocusedActionType {
return {
type: IS_APP_FOCUSED,
payload: focused,
};
}
export function showSettingsSection( export function showSettingsSection(
category: SessionSettingCategory category: SessionSettingCategory
): FocusSettingsSectionActionType { ): FocusSettingsSectionActionType {
@ -46,14 +59,16 @@ export const actions = {
showSettingsSection, showSettingsSection,
}; };
export const initialSectionState = { export const initialSectionState: SectionStateType = {
focusedSection: SectionType.Message, focusedSection: SectionType.Message,
focusedSettingsSection: undefined, focusedSettingsSection: undefined,
isAppFocused: false,
}; };
export type SectionStateType = { export type SectionStateType = {
focusedSection: SectionType; focusedSection: SectionType;
focusedSettingsSection?: SessionSettingCategory; focusedSettingsSection?: SessionSettingCategory;
isAppFocused: boolean;
}; };
export const reducer = ( export const reducer = (
@ -73,6 +88,7 @@ export const reducer = (
if (castedPayload !== SectionType.Settings) { if (castedPayload !== SectionType.Settings) {
return { return {
...state,
focusedSection: castedPayload, focusedSection: castedPayload,
focusedSettingsSection: undefined, focusedSettingsSection: undefined,
}; };
@ -89,6 +105,12 @@ export const reducer = (
...state, ...state,
focusedSettingsSection: payload, focusedSettingsSection: payload,
}; };
case IS_APP_FOCUSED:
return {
...state,
isAppFocused: payload,
};
default: default:
return state; return state;
} }

@ -126,6 +126,58 @@ export const getSortedMessagesOfSelectedConversation = createSelector(
} }
); );
export type MessagePropsType =
| 'group-notification'
| 'group-invitation'
| 'data-extraction'
| 'timer-notification'
| 'regular-message';
export const getSortedMessagesTypesOfSelectedConversation = createSelector(
getMessagesOfSelectedConversation,
(
sortedMessages
): Array<{
messageType: MessagePropsType;
props: any;
}> => {
return sortedMessages.map(msg => {
if (msg.propsForDataExtractionNotification) {
return {
messageType: 'data-extraction',
props: { ...msg.propsForDataExtractionNotification, messageId: msg.propsForMessage.id },
};
}
if (msg.propsForGroupInvitation) {
return {
messageType: 'group-invitation',
props: { ...msg.propsForGroupInvitation, messageId: msg.propsForMessage.id },
};
}
if (msg.propsForGroupNotification) {
return {
messageType: 'group-notification',
props: { ...msg.propsForGroupNotification, messageId: msg.propsForMessage.id },
};
}
if (msg.propsForTimerNotification) {
return {
messageType: 'data-extraction',
props: { ...msg.propsForTimerNotification, messageId: msg.propsForMessage.id },
};
}
return {
messageType: 'regular-message',
props: { messageId: msg.propsForMessage.id },
};
});
}
);
function getConversationTitle( function getConversationTitle(
conversation: ReduxConversationType, conversation: ReduxConversationType,
testingi18n?: LocalizerType testingi18n?: LocalizerType

@ -15,3 +15,8 @@ export const getFocusedSettingsSection = createSelector(
getSection, getSection,
(state: SectionStateType): SessionSettingCategory | undefined => state.focusedSettingsSection (state: SectionStateType): SessionSettingCategory | undefined => state.focusedSettingsSection
); );
export const getIsAppFocused = createSelector(
getSection,
(state: SectionStateType): boolean => state.isAppFocused
);

@ -2,7 +2,6 @@ import * as GoogleChrome from './GoogleChrome';
import { arrayBufferToObjectURL } from './arrayBufferToObjectURL'; import { arrayBufferToObjectURL } from './arrayBufferToObjectURL';
import { isFileDangerous } from './isFileDangerous'; import { isFileDangerous } from './isFileDangerous';
import { missingCaseError } from './missingCaseError'; import { missingCaseError } from './missingCaseError';
import { migrateColor } from './migrateColor';
import { makeLookup } from './makeLookup'; import { makeLookup } from './makeLookup';
import * as PasswordUtil from './passwordUtils'; import * as PasswordUtil from './passwordUtils';
import * as AttachmentUtil from './attachmentsUtil'; import * as AttachmentUtil from './attachmentsUtil';
@ -15,7 +14,6 @@ export {
GoogleChrome, GoogleChrome,
isFileDangerous, isFileDangerous,
makeLookup, makeLookup,
migrateColor,
missingCaseError, missingCaseError,
PasswordUtil, PasswordUtil,
AttachmentUtil, AttachmentUtil,

@ -290,15 +290,6 @@
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
}, },
{
"rule": "jQuery-html(",
"path": "js/views/identicon_svg_view.js",
"line": " const html = this.render().$el.html();",
"lineNumber": 19,
"reasonCategory": "usageTrusted",
"updated": "2018-09-15T00:38:04.183Z",
"reasonDetail": "Getting the value, not setting it"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/inbox_view.js", "path": "js/views/inbox_view.js",

@ -1,82 +0,0 @@
// import { missingCaseError } from './missingCaseError';
type OldColor =
| 'amber'
| 'blue'
| 'blue_grey'
| 'brown'
| 'cyan'
| 'deep_orange'
| 'deep_purple'
| 'green'
| 'grey'
| 'indigo'
| 'lime'
| 'light_blue'
| 'light_green'
| 'orange'
| 'pink'
| 'purple'
| 'red'
| 'teal'
| 'yellow';
type NewColor =
| 'red'
| 'deep_orange'
| 'brown'
| 'pink'
| 'purple'
| 'indigo'
| 'blue'
| 'teal'
| 'green'
| 'light_green'
| 'blue_grey'
| 'grey';
export function migrateColor(color: OldColor): NewColor {
switch (color) {
// These colors no longer exist
case 'orange':
case 'amber':
return 'deep_orange';
case 'yellow':
return 'brown';
case 'deep_purple':
return 'purple';
case 'light_blue':
return 'blue';
case 'cyan':
return 'teal';
case 'lime':
return 'light_green';
// These can stay as they are
case 'red':
case 'deep_orange':
case 'brown':
case 'pink':
case 'purple':
case 'indigo':
case 'blue':
case 'teal':
case 'green':
case 'light_green':
case 'blue_grey':
case 'grey':
return color;
// Can uncomment this to ensure that we've covered all potential cases
// default:
// throw missingCaseError(color);
default:
return 'grey';
}
}

@ -7542,11 +7542,6 @@ redux-persist@^6.0.0:
resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8" resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8"
integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ== integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==
redux-promise-middleware@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/redux-promise-middleware/-/redux-promise-middleware-6.1.0.tgz#ecdb22488cdd673c1a3f0d278d82b48d92ca5d06"
integrity sha512-C62Ku3TgMwxFh5r3h1/iD+XPdsoizyHLT74dTkqhJ8c0LCbEVl1z9fm8zKitAjI16e6w6+h3mxf6wHdonaYXfQ==
redux-thunk@^2.3.0: redux-thunk@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"

Loading…
Cancel
Save