diff --git a/package.json b/package.json index c8f862263..22f31733f 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "postinstall": "electron-builder install-app-deps && rimraf node_modules/dtrace-provider", "start": "cross-env NODE_APP_INSTANCE=$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:dev": "yarn clean-transpile; yarn grunt dev --force", "generate": "yarn grunt --force", @@ -118,7 +117,6 @@ "redux": "4.0.1", "redux-logger": "3.0.6", "redux-persist": "^6.0.0", - "redux-promise-middleware": "6.1.0", "reselect": "4.0.0", "rimraf": "2.6.2", "sanitize.css": "^12.0.1", diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 8d82c77b0..349fa036e 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -199,7 +199,7 @@ const MessageItem = (props: { if (!lastMessage && !isTyping) { return null; } - const text = lastMessage && lastMessage.text ? lastMessage.text : ''; + const text = lastMessage?.text || ''; if (isEmpty(text)) { return null; @@ -280,7 +280,7 @@ const ConversationListItem = (props: Props) => { const membersAvatar = useMembersAvatars(props); const openConvo = useCallback( - async (e: any) => { + async (e: React.MouseEvent) => { // mousedown is invoked sooner than onClick, but for both right and left click if (e.button === 0) { await openConversationWithMessages({ conversationKey: conversationId }); @@ -294,6 +294,10 @@ const ConversationListItem = (props: Props) => {
{ + e.stopPropagation(); + e.preventDefault(); + }} onContextMenu={(e: any) => { contextMenu.show({ id: triggerId, diff --git a/ts/components/SessionMainPanel.tsx b/ts/components/SessionMainPanel.tsx index d3cfb3e0e..6fb233853 100644 --- a/ts/components/SessionMainPanel.tsx +++ b/ts/components/SessionMainPanel.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { useSelector } from 'react-redux'; +import { useAppIsFocused } from '../hooks/useAppFocused'; import { getFocusedSettingsSection } from '../state/selectors/section'; import { SmartSessionConversation } from '../state/smart/SessionConversation'; @@ -11,6 +12,9 @@ export const SessionMainPanel = () => { const focusedSettingsSection = useSelector(getFocusedSettingsSection); const isSettingsView = focusedSettingsSection !== undefined; + // even if it looks like this does nothing, this does update the redux store. + useAppIsFocused(); + if (isSettingsView) { return ; } diff --git a/ts/components/conversation/ReadableMessage.tsx b/ts/components/conversation/ReadableMessage.tsx index 096e1fba5..614e6ca86 100644 --- a/ts/components/conversation/ReadableMessage.tsx +++ b/ts/components/conversation/ReadableMessage.tsx @@ -3,7 +3,6 @@ import React, { useCallback } from 'react'; import { InView } from 'react-intersection-observer'; import { useDispatch, useSelector } from 'react-redux'; import { getMessageById } from '../../data/data'; -import { useAppIsFocused } from '../../hooks/useAppFocused'; import { Constants } from '../../session'; import { getConversationController } from '../../session/conversations'; import { @@ -19,6 +18,7 @@ import { getOldestMessageId, getSelectedConversationKey, } from '../../state/selectors/conversations'; +import { getIsAppFocused } from '../../state/selectors/section'; type ReadableMessageProps = { children: React.ReactNode; @@ -45,7 +45,7 @@ const debouncedTriggerLoadMore = _.debounce( export const ReadableMessage = (props: ReadableMessageProps) => { const { messageId, onContextMenu, className, receivedAt, isUnread } = props; - const isAppFocused = useAppIsFocused(); + const isAppFocused = useSelector(getIsAppFocused); const dispatch = useDispatch(); // onVisible={haveDoneFirstScrollProp ? onVisible : noop} diff --git a/ts/components/dialog/EditProfileDialog.tsx b/ts/components/dialog/EditProfileDialog.tsx index 6be11f1be..5b9c63cce 100644 --- a/ts/components/dialog/EditProfileDialog.tsx +++ b/ts/components/dialog/EditProfileDialog.tsx @@ -49,10 +49,16 @@ export class EditProfileDialog extends React.Component<{}, State> { }; this.inputEl = React.createRef(); + } + public componentDidMount() { window.addEventListener('keyup', this.onKeyUp); } + public componentWillUnmount() { + window.removeEventListener('keyup', this.onKeyUp); + } + public render() { const i18n = window.i18n; diff --git a/ts/components/dialog/InviteContactsDialog.tsx b/ts/components/dialog/InviteContactsDialog.tsx index fb8e87925..8307fc562 100644 --- a/ts/components/dialog/InviteContactsDialog.tsx +++ b/ts/components/dialog/InviteContactsDialog.tsx @@ -13,6 +13,8 @@ import { SessionWrapperModal } from '../session/SessionWrapperModal'; import { SpacerLG } from '../basic/Text'; import { useDispatch } from 'react-redux'; import { updateInviteContactModal } from '../../state/ducks/modalDialog'; +// tslint:disable-next-line: no-submodule-imports +import useKey from 'react-use/lib/useKey'; type Props = { conversationId: string; @@ -67,7 +69,6 @@ const InviteContactsDialogInner = (props: Props) => { ); const closeDialog = () => { - window.removeEventListener('keyup', onKeyUp); dispatch(updateInviteContactModal(null)); }; @@ -87,19 +88,13 @@ const InviteContactsDialogInner = (props: Props) => { closeDialog(); }; - const onKeyUp = (event: any) => { - switch (event.key) { - case 'Enter': - onClickOK(); - break; - case 'Esc': - case 'Escape': - closeDialog(); - break; - default: - } - }; - window.addEventListener('keyup', onKeyUp); + useKey((event: KeyboardEvent) => { + return event.key === 'Enter'; + }, onClickOK); + + useKey((event: KeyboardEvent) => { + return event.key === 'Esc' || event.key === 'Escape'; + }, closeDialog); const titleText = `${window.i18n('addingContacts')} ${chatName}`; const cancelText = window.i18n('cancel'); diff --git a/ts/components/dialog/SessionModal.tsx b/ts/components/dialog/SessionModal.tsx index 505b8514e..0a42e6384 100644 --- a/ts/components/dialog/SessionModal.tsx +++ b/ts/components/dialog/SessionModal.tsx @@ -48,15 +48,17 @@ export class SessionModal extends React.PureComponent { this.close = this.close.bind(this); this.onKeyUp = this.onKeyUp.bind(this); this.node = null; - - window.addEventListener('keyup', this.onKeyUp); } public componentDidMount() { + window.addEventListener('keyup', this.onKeyUp); + document.addEventListener('mousedown', this.handleClick, false); } public componentWillUnmount() { + window.removeEventListener('keyup', this.onKeyUp); + document.removeEventListener('mousedown', this.handleClick, false); } @@ -118,7 +120,6 @@ export class SessionModal extends React.PureComponent { isVisible: false, }); - window.removeEventListener('keyup', this.onKeyUp); document.removeEventListener('mousedown', this.handleClick, false); if (this.props.onClose) { diff --git a/ts/components/dialog/UpdateGroupMembersDialog.tsx b/ts/components/dialog/UpdateGroupMembersDialog.tsx index e7c0961e7..4c8bc5cfe 100644 --- a/ts/components/dialog/UpdateGroupMembersDialog.tsx +++ b/ts/components/dialog/UpdateGroupMembersDialog.tsx @@ -109,10 +109,16 @@ export class UpdateGroupMembersDialog extends React.Component { admins, isAdmin, }; + } + public componentDidMount() { window.addEventListener('keyup', this.onKeyUp); } + public componentWillUnmount() { + window.removeEventListener('keyup', this.onKeyUp); + } + public onClickOK() { const members = this.getWouldBeMembers(this.state.contactList).map(d => d.id); @@ -243,8 +249,6 @@ export class UpdateGroupMembersDialog extends React.Component { } private closeDialog() { - window.removeEventListener('keyup', this.onKeyUp); - window.inboxStore?.dispatch(updateGroupMembersModal(null)); } diff --git a/ts/components/dialog/UpdateGroupNameDialog.tsx b/ts/components/dialog/UpdateGroupNameDialog.tsx index bd51f9845..7409ef923 100644 --- a/ts/components/dialog/UpdateGroupNameDialog.tsx +++ b/ts/components/dialog/UpdateGroupNameDialog.tsx @@ -39,9 +39,16 @@ export class UpdateGroupNameDialog extends React.Component { avatar: this.convo.getAvatarPath(), }; this.inputEl = React.createRef(); + } + + public componentDidMount() { window.addEventListener('keyup', this.onKeyUp); } + public componentWillUnmount() { + window.removeEventListener('keyup', this.onKeyUp); + } + public onClickOK() { if (!this.state.groupName.trim()) { this.onShowError(window.i18n('emptyGroupNameError')); diff --git a/ts/components/session/ActionsPanel.tsx b/ts/components/session/ActionsPanel.tsx index ef28ca18b..d12d6cb15 100644 --- a/ts/components/session/ActionsPanel.tsx +++ b/ts/components/session/ActionsPanel.tsx @@ -173,7 +173,7 @@ const removeAllV1OpenGroups = async () => { if (window.inboxStore) { window.inboxStore?.dispatch(conversationRemoved(v1Convo.id)); window.inboxStore?.dispatch( - conversationChanged({ id: v1Convo.id, data: v1Convo.getProps() }) + conversationChanged({ id: v1Convo.id, data: v1Convo.getConversationModelProps() }) ); } } catch (e) { diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index bbf0b4e00..ec1168465 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -49,14 +49,19 @@ export class SessionClosableOverlay extends React.Component { this.onKeyUp = this.onKeyUp.bind(this); this.onGroupNameChanged = this.onGroupNameChanged.bind(this); - window.addEventListener('keyup', this.onKeyUp); } public componentDidMount() { + window.addEventListener('keyup', this.onKeyUp); + if (this.inputRef.current) { this.inputRef.current.focus(); } } + public componentWillUnmount() { + window.removeEventListener('keyup', this.onKeyUp); + + } public getContacts() { const { overlayMode } = this.props; diff --git a/ts/components/session/SessionInboxView.tsx b/ts/components/session/SessionInboxView.tsx index 36187b644..21fa0df6a 100644 --- a/ts/components/session/SessionInboxView.tsx +++ b/ts/components/session/SessionInboxView.tsx @@ -76,7 +76,7 @@ export class SessionInboxView extends React.Component { // Here we set up a full redux store with initial state for our LeftPane Root const convoCollection = getConversationController().getConversations(); const conversations = convoCollection.map((conversation: ConversationModel) => - conversation.getProps() + conversation.getConversationModelProps() ); const filledConversations = conversations.map((conv: any) => { diff --git a/ts/components/session/conversation/SessionCompositionBox.tsx b/ts/components/session/conversation/SessionCompositionBox.tsx index 89cdeb7dc..653d89079 100644 --- a/ts/components/session/conversation/SessionCompositionBox.tsx +++ b/ts/components/session/conversation/SessionCompositionBox.tsx @@ -166,7 +166,7 @@ class SessionCompositionBoxInner extends React.Component { } public componentDidMount() { - setTimeout(this.focusCompositionBox, 100); + setTimeout(this.focusCompositionBox, 500); const div = this.container; div?.addEventListener('paste', this.handlePaste); diff --git a/ts/components/session/conversation/SessionMessagesList.tsx b/ts/components/session/conversation/SessionMessagesList.tsx index 81ddf314b..7ec79af26 100644 --- a/ts/components/session/conversation/SessionMessagesList.tsx +++ b/ts/components/session/conversation/SessionMessagesList.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { useSelector } from 'react-redux'; import { QuoteClickOptions } from '../../../models/messageType'; -import { SortedMessageModelProps } from '../../../state/ducks/conversations'; -import { getSortedMessagesOfSelectedConversation } from '../../../state/selectors/conversations'; +import { getSortedMessagesTypesOfSelectedConversation } from '../../../state/selectors/conversations'; import { DataExtractionNotificationItem, GenericMessageItem, @@ -14,61 +13,57 @@ import { export const SessionMessagesList = (props: { scrollToQuoteMessage: (options: QuoteClickOptions) => Promise; }) => { - const messagesProps = useSelector(getSortedMessagesOfSelectedConversation); + const messagesProps = useSelector(getSortedMessagesTypesOfSelectedConversation); return ( <> - {messagesProps.map((messageProps: SortedMessageModelProps) => { - const timerProps = messageProps.propsForTimerNotification; - const propsForGroupInvitation = messageProps.propsForGroupInvitation; - const propsForDataExtractionNotification = messageProps.propsForDataExtractionNotification; - - const groupNotificationProps = messageProps.propsForGroupNotification; - - if (groupNotificationProps) { + {messagesProps.map(messageProps => { + if (messageProps.messageType === 'group-notification') { return ( ); } - if (propsForGroupInvitation) { + if (messageProps.messageType === 'group-invitation') { return ( ); } - if (propsForDataExtractionNotification) { + if (messageProps.messageType === 'data-extraction') { return ( ); } - if (timerProps) { + if (messageProps.messageType === 'timer-notification') { return ( - + ); } if (!messageProps) { - return; + return null; } // firstMessageOfSeries tells us to render the avatar only for the first message // in a series of messages from the same user return ( ); diff --git a/ts/components/session/conversation/SessionMessagesTypes.tsx b/ts/components/session/conversation/SessionMessagesTypes.tsx index ba6cee963..bd6c75918 100644 --- a/ts/components/session/conversation/SessionMessagesTypes.tsx +++ b/ts/components/session/conversation/SessionMessagesTypes.tsx @@ -5,7 +5,6 @@ import { PropsForExpirationTimer, PropsForGroupInvitation, PropsForGroupUpdate, - SortedMessageModelProps, } from '../../../state/ducks/conversations'; import { getFirstUnreadMessageId } from '../../../state/selectors/conversations'; import { DataExtractionNotification } from '../../conversation/DataExtractionNotification'; @@ -77,18 +76,13 @@ export const TimerNotificationItem = (props: { timerProps: PropsForExpirationTim export const GenericMessageItem = (props: { messageId: string; - messageProps: SortedMessageModelProps; scrollToQuoteMessage: (options: QuoteClickOptions) => Promise; }) => { const messageId = props.messageId; - const onQuoteClick = props.messageProps.propsForMessage.quote - ? props.scrollToQuoteMessage - : undefined; - return ( - + ); diff --git a/ts/components/session/settings/SessionSettings.tsx b/ts/components/session/settings/SessionSettings.tsx index 4fde93db9..5d37087e8 100644 --- a/ts/components/session/settings/SessionSettings.tsx +++ b/ts/components/session/settings/SessionSettings.tsx @@ -93,16 +93,21 @@ class SettingsViewInner extends React.Component { void this.hasPassword(); this.onKeyUp = this.onKeyUp.bind(this); - window.addEventListener('keyup', this.onKeyUp); } public componentDidMount() { + window.addEventListener('keyup', this.onKeyUp); + const mediaSetting = window.getSettingValue('media-permissions'); this.setState({ mediaSetting }); setTimeout(() => ($('#password-lock-input') as any).focus(), 100); } + public componentWillUnmount() { + window.removeEventListener('keyup', this.onKeyUp); + } + /* tslint:disable-next-line:max-func-body-length */ public renderSettingInCategory() { const { category } = this.props; diff --git a/ts/hooks/useAppFocused.ts b/ts/hooks/useAppFocused.ts index f676613f8..96cd9613c 100644 --- a/ts/hooks/useAppFocused.ts +++ b/ts/hooks/useAppFocused.ts @@ -1,31 +1,29 @@ 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 { setIsAppFocused } from '../state/ducks/section'; +import { getIsAppFocused } from '../state/selectors/section'; export function useAppIsFocused() { - const [isAppFocused, setIsAppFocused] = useState(false); + const dispatch = useDispatch(); + const isFocused = useSelector(getIsAppFocused); useEffect(() => { - setIsAppFocused(isElectronWindowFocused()); + dispatch(setIsAppFocused(isElectronWindowFocused())); }, []); - const onFocusCallback = useCallback( - (_event, win) => { - if (win.webContents.id === 1) { - setIsAppFocused(true); - } - }, - [setIsAppFocused] - ); + const onFocusCallback = useCallback((_event, win) => { + if (win.webContents.id === 1) { + dispatch(setIsAppFocused(true)); + } + }, []); - const onBlurCallback = useCallback( - (_event, win) => { - if (win.webContents.id === 1) { - setIsAppFocused(false); - } - }, - [setIsAppFocused] - ); + const onBlurCallback = useCallback((_event, win) => { + if (win.webContents.id === 1) { + dispatch(setIsAppFocused(false)); + } + }, []); useEffect(() => { remote.app.on('browser-window-focus', onFocusCallback); @@ -36,5 +34,5 @@ export function useAppIsFocused() { }; }); - return isAppFocused; + return isFocused; } diff --git a/ts/hooks/useEncryptedFileFetch.ts b/ts/hooks/useEncryptedFileFetch.ts index b6912c742..e1fb5988b 100644 --- a/ts/hooks/useEncryptedFileFetch.ts +++ b/ts/hooks/useEncryptedFileFetch.ts @@ -1,6 +1,7 @@ import { useEffect, useRef, useState } from 'react'; import { getDecryptedMediaUrl } from '../session/crypto/DecryptedAttachmentsManager'; +import { perfEnd, perfStart } from '../session/utils/Performance'; export const useEncryptedFileFetch = (url: string, contentType: string) => { // tslint:disable-next-line: no-bitwise @@ -10,7 +11,11 @@ export const useEncryptedFileFetch = (url: string, contentType: string) => { const mountedRef = useRef(true); async function fetchUrl() { + perfStart(`getDecryptedMediaUrl${url}`); + const decryptedUrl = await getDecryptedMediaUrl(url, contentType); + perfEnd(`getDecryptedMediaUrl${url}`, 'getDecryptedMediaUrl'); + if (mountedRef.current) { setUrlToLoad(decryptedUrl); diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 92210b393..0cebdaf04 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -223,7 +223,9 @@ export class ConversationModel extends Backbone.Model { this.typingRefreshTimer = null; this.typingPauseTimer = null; 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() { @@ -407,7 +409,7 @@ export class ConversationModel extends Backbone.Model { return this.get('moderators'); } - public getProps(): ReduxConversationType { + public getConversationModelProps(): ReduxConversationType { const groupAdmins = this.getGroupAdmins(); const members = this.isGroup() && !this.isPublic() ? this.get('members') : []; const ourNumber = UserUtils.getOurPubKeyStrFromCache(); @@ -804,13 +806,8 @@ export class ConversationModel extends Backbone.Model { lastMessageStatus: lastMessageStatusModel, 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); - if (this.hasChanged()) { - await this.commit(); - } + await this.commit(); } public async updateExpireTimer( @@ -917,7 +914,7 @@ export class ConversationModel extends Backbone.Model { conversationChanged({ id: this.id, data: { - ...this.getProps(), + ...this.getConversationModelProps(), isSelected: false, }, }) @@ -945,7 +942,7 @@ export class ConversationModel extends Backbone.Model { window.inboxStore?.dispatch( conversationActions.messageAdded({ conversationKey: this.id, - messageModelProps: model.getProps(), + messageModelProps: model.getMessageModelProps(), }) ); const unreadCount = await this.getUnreadCount(); @@ -1006,7 +1003,7 @@ export class ConversationModel extends Backbone.Model { const allProps: Array = []; for (const nowRead of oldUnreadNowRead) { - allProps.push(nowRead.getProps()); + allProps.push(nowRead.getMessageModelProps()); } if (allProps.length) { diff --git a/ts/models/message.ts b/ts/models/message.ts index 67341cd22..e2a9783de 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -80,10 +80,10 @@ export class MessageModel extends Backbone.Model { window.contextMenuShown = false; - this.getProps(); + this.getMessageModelProps(); } - public getProps(): MessageModelPropsWithoutConvoProps { + public getMessageModelProps(): MessageModelPropsWithoutConvoProps { perfStart(`getPropsMessage-${this.id}`); const messageProps: MessageModelPropsWithoutConvoProps = { propsForMessage: this.getPropsForMessage(), @@ -1121,7 +1121,7 @@ export class MessageModel extends Backbone.Model { } } private dispatchMessageUpdate() { - updatesToDispatch.set(this.id, this.getProps()); + updatesToDispatch.set(this.id, this.getMessageModelProps()); trotthledAllMessagesDispatch(); } } diff --git a/ts/receiver/errors.ts b/ts/receiver/errors.ts index 15f68636c..fde949139 100644 --- a/ts/receiver/errors.ts +++ b/ts/receiver/errors.ts @@ -38,7 +38,7 @@ export async function onError(ev: any) { window.inboxStore?.dispatch( conversationActions.messageAdded({ conversationKey: conversation.id, - messageModelProps: message.getProps(), + messageModelProps: message.getMessageModelProps(), }) ); diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index 9baf219d2..62ed2239f 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -454,7 +454,7 @@ export async function handleMessageJob( updatesToDispatch.set(message.id, { conversationKey: conversation.id, - messageModelProps: message.getProps(), + messageModelProps: message.getMessageModelProps(), }); trotthledAllMessagesAddedDispatch(); if (message.get('unread')) { diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index 88c238a00..08f3e39a0 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -120,7 +120,7 @@ export class ConversationController { window.inboxStore?.dispatch( conversationActions.conversationAdded({ id: conversation.id, - data: conversation.getProps(), + data: conversation.getConversationModelProps(), }) ); } @@ -249,7 +249,7 @@ export class ConversationController { window.inboxStore?.dispatch( conversationActions.conversationChanged({ id: conversation.id, - data: conversation.getProps(), + data: conversation.getConversationModelProps(), }) ); window.inboxStore?.dispatch(conversationActions.conversationRemoved(conversation.id)); diff --git a/ts/state/createStore.ts b/ts/state/createStore.ts index 93240e04c..6415d096e 100644 --- a/ts/state/createStore.ts +++ b/ts/state/createStore.ts @@ -1,4 +1,3 @@ -import promise from 'redux-promise-middleware'; import { createLogger } from 'redux-logger'; import { configureStore } from '@reduxjs/toolkit'; import { rootReducer } from './reducer'; @@ -6,7 +5,6 @@ import { persistReducer } from 'redux-persist'; // tslint:disable-next-line: no-submodule-imports match-default-export-name import storage from 'redux-persist/lib/storage'; - // @ts-ignore const env = window.getEnvironment(); diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 4f8f1d6c5..2d285c87b 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -300,7 +300,7 @@ async function getMessages( }); const messageProps: Array = messageSet.models.map(m => - m.getProps() + m.getMessageModelProps() ); return messageProps; } @@ -326,11 +326,11 @@ export const fetchMessagesForConversation = createAsyncThunk( }): Promise => { const beforeTimestamp = Date.now(); // tslint:disable-next-line: no-console - console.time('fetchMessagesForConversation'); + perfStart('fetchMessagesForConversation'); const messagesProps = await getMessages(conversationKey, count); const afterTimestamp = Date.now(); // tslint:disable-next-line: no-console - console.timeEnd('fetchMessagesForConversation'); + perfEnd('fetchMessagesForConversation', 'fetchMessagesForConversation'); const time = afterTimestamp - beforeTimestamp; window?.log?.info(`Loading ${messagesProps.length} messages took ${time}ms to load.`); @@ -824,10 +824,16 @@ export async function openConversationWithMessages(args: { messageId?: string; }) { const { conversationKey, messageId } = args; + perfStart('getFirstUnreadMessageIdInConversation'); const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(conversationKey); + perfEnd('getFirstUnreadMessageIdInConversation', 'getFirstUnreadMessageIdInConversation'); // preload 30 messages + perfStart('getMessages'); + const initialMessages = await getMessages(conversationKey, 30); + perfEnd('getMessages', 'getMessages'); + console.warn('initialMessages', initialMessages); window.inboxStore?.dispatch( actions.openConversationExternal({ diff --git a/ts/state/ducks/section.tsx b/ts/state/ducks/section.tsx index 6c6092cc3..0d3d2b95c 100644 --- a/ts/state/ducks/section.tsx +++ b/ts/state/ducks/section.tsx @@ -2,6 +2,7 @@ import { SessionSettingCategory } from '../../components/session/settings/Sessio export const FOCUS_SECTION = 'FOCUS_SECTION'; export const FOCUS_SETTINGS_SECTION = 'FOCUS_SETTINGS_SECTION'; +export const IS_APP_FOCUSED = 'IS_APP_FOCUSED'; export enum SectionType { Profile, @@ -23,6 +24,11 @@ type FocusSettingsSectionActionType = { payload: SessionSettingCategory; }; +type IsAppFocusedActionType = { + type: 'IS_APP_FOCUSED'; + payload: boolean; +}; + export function showLeftPaneSection(section: SectionType): FocusSectionActionType { return { type: FOCUS_SECTION, @@ -32,6 +38,13 @@ export function showLeftPaneSection(section: SectionType): FocusSectionActionTyp type SectionActionTypes = FocusSectionActionType | FocusSettingsSectionActionType; +export function setIsAppFocused(focused: boolean): IsAppFocusedActionType { + return { + type: IS_APP_FOCUSED, + payload: focused, + }; +} + export function showSettingsSection( category: SessionSettingCategory ): FocusSettingsSectionActionType { @@ -46,14 +59,16 @@ export const actions = { showSettingsSection, }; -export const initialSectionState = { +export const initialSectionState: SectionStateType = { focusedSection: SectionType.Message, focusedSettingsSection: undefined, + isAppFocused: false, }; export type SectionStateType = { focusedSection: SectionType; focusedSettingsSection?: SessionSettingCategory; + isAppFocused: boolean; }; export const reducer = ( @@ -73,6 +88,7 @@ export const reducer = ( if (castedPayload !== SectionType.Settings) { return { + ...state, focusedSection: castedPayload, focusedSettingsSection: undefined, }; @@ -89,6 +105,12 @@ export const reducer = ( ...state, focusedSettingsSection: payload, }; + + case IS_APP_FOCUSED: + return { + ...state, + isAppFocused: payload, + }; default: return state; } diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index ff3fc7ea7..aa06744cd 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -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( conversation: ReduxConversationType, testingi18n?: LocalizerType diff --git a/ts/state/selectors/section.ts b/ts/state/selectors/section.ts index 049ad4983..65558bd8c 100644 --- a/ts/state/selectors/section.ts +++ b/ts/state/selectors/section.ts @@ -15,3 +15,8 @@ export const getFocusedSettingsSection = createSelector( getSection, (state: SectionStateType): SessionSettingCategory | undefined => state.focusedSettingsSection ); + +export const getIsAppFocused = createSelector( + getSection, + (state: SectionStateType): boolean => state.isAppFocused +); diff --git a/ts/util/index.ts b/ts/util/index.ts index c793b193a..e70796d58 100644 --- a/ts/util/index.ts +++ b/ts/util/index.ts @@ -2,7 +2,6 @@ import * as GoogleChrome from './GoogleChrome'; import { arrayBufferToObjectURL } from './arrayBufferToObjectURL'; import { isFileDangerous } from './isFileDangerous'; import { missingCaseError } from './missingCaseError'; -import { migrateColor } from './migrateColor'; import { makeLookup } from './makeLookup'; import * as PasswordUtil from './passwordUtils'; import * as AttachmentUtil from './attachmentsUtil'; @@ -15,7 +14,6 @@ export { GoogleChrome, isFileDangerous, makeLookup, - migrateColor, missingCaseError, PasswordUtil, AttachmentUtil, diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index c5d939843..56cb3a4fd 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -290,15 +290,6 @@ "updated": "2018-09-19T21:59:32.770Z", "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-$(", "path": "js/views/inbox_view.js", diff --git a/ts/util/migrateColor.ts b/ts/util/migrateColor.ts deleted file mode 100644 index 1eeaece1d..000000000 --- a/ts/util/migrateColor.ts +++ /dev/null @@ -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'; - } -} diff --git a/yarn.lock b/yarn.lock index bc690523a..ad91656ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7542,11 +7542,6 @@ redux-persist@^6.0.0: resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8" 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: version "2.3.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"