diff --git a/package.json b/package.json
index c4ad55fe5..3fb60e1ba 100644
--- a/package.json
+++ b/package.json
@@ -98,9 +98,9 @@
"pify": "3.0.0",
"protobufjs": "^6.9.0",
"rc-slider": "^8.7.1",
- "react": "^16.13.1",
+ "react": "^17.0.2",
"react-contexify": "5.0.0",
- "react-dom": "16.8.3",
+ "react-dom": "^17.0.2",
"react-emoji": "^0.5.0",
"react-emoji-render": "^1.2.4",
"react-h5-audio-player": "^3.2.0",
@@ -112,7 +112,6 @@
"react-toastify": "^6.0.9",
"react-use": "^17.2.1",
"react-virtualized": "9.22.3",
- "react-window-infinite-loader": "^1.0.7",
"read-last-lines": "1.3.0",
"redux": "4.0.1",
"redux-logger": "3.0.6",
@@ -158,8 +157,8 @@
"@types/pify": "3.0.2",
"@types/qs": "6.5.1",
"@types/rc-slider": "^8.6.5",
- "@types/react": "16.8.5",
- "@types/react-dom": "16.8.2",
+ "@types/react": "^17.0.15",
+ "@types/react-dom": "^17.0.2",
"@types/react-mentions": "^4.1.1",
"@types/react-mic": "^12.4.1",
"@types/react-portal": "^4.0.2",
diff --git a/ts/components/Avatar.tsx b/ts/components/Avatar.tsx
index 4da4d87e9..a06865576 100644
--- a/ts/components/Avatar.tsx
+++ b/ts/components/Avatar.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useCallback, useState } from 'react';
import classNames from 'classnames';
import { AvatarPlaceHolder, ClosedGroupAvatar } from './AvatarPlaceHolder';
@@ -71,6 +71,11 @@ const AvatarImage = (props: {
}) => {
const { avatarPath, base64Data, name, imageBroken, handleImageError } = props;
+ const onDragStart = useCallback((e: any) => {
+ e.preventDefault();
+ return false;
+ }, []);
+
if ((!avatarPath && !base64Data) || imageBroken) {
return null;
}
@@ -79,6 +84,7 @@ const AvatarImage = (props: {
return (
diff --git a/ts/components/Lightbox.tsx b/ts/components/Lightbox.tsx
index a60127d3a..542c436d5 100644
--- a/ts/components/Lightbox.tsx
+++ b/ts/components/Lightbox.tsx
@@ -1,6 +1,6 @@
// tslint:disable:react-a11y-anchors
-import React, { useEffect, useRef } from 'react';
+import React, { useCallback, useEffect, useRef } from 'react';
import is from '@sindresorhus/is';
@@ -187,7 +187,7 @@ const Icon = ({
}) => (
{
+ e.preventDefault();
+ return false; }, []);
+
const playVideo = () => {
if (!videoRef) {
return;
@@ -245,7 +249,14 @@ export const LightboxObject = ({
});
if (isImageTypeSupported) {
- return
;
+ return (
+
+ );
}
const isVideoTypeSupported = GoogleChrome.isVideoTypeSupported(contentType);
@@ -259,7 +270,7 @@ export const LightboxObject = ({
ref={videoRef}
onClick={playVideo}
controls={true}
- style={styles.object}
+ style={styles.object as any}
key={urlToLoad}
>
@@ -302,8 +313,8 @@ export const Lightbox = (props: Props) => {
};
return (
-
-
+
+
{
ref={containerRef}
role="button"
>
-
+
{!is.undefined(contentType) ? (
{
onObjectClick={onObjectClick}
/>
) : null}
- {caption ? {caption}
: null}
+ {caption ? {caption}
: null}
-
-
+
{onPrevious ? (
) : (
diff --git a/ts/components/MessageBodyHighlight.tsx b/ts/components/MessageBodyHighlight.tsx
index 2bf6e39ec..f42cfe1f8 100644
--- a/ts/components/MessageBodyHighlight.tsx
+++ b/ts/components/MessageBodyHighlight.tsx
@@ -8,9 +8,9 @@ import { SizeClassType } from '../util/emoji';
import { RenderTextCallbackType } from '../types/Util';
-interface Props {
+type Props = {
text: string;
-}
+};
const renderNewLines: RenderTextCallbackType = ({ text, key }) => (
@@ -28,56 +28,27 @@ const renderEmoji = ({
renderNonEmoji: RenderTextCallbackType;
}) =>
;
-export class MessageBodyHighlight extends React.Component
{
- public render() {
- const { text } = this.props;
- const results: Array = [];
- const FIND_BEGIN_END = /<>(.+?)<>/g;
+export const MessageBodyHighlight = (props: Props) => {
+ const { text } = props;
+ const results: Array = [];
+ const FIND_BEGIN_END = /<>(.+?)<>/g;
- let match = FIND_BEGIN_END.exec(text);
- let last = 0;
- let count = 1;
+ let match = FIND_BEGIN_END.exec(text);
+ let last = 0;
+ let count = 1;
- if (!match) {
- return ;
- }
-
- const sizeClass = '';
-
- while (match) {
- if (last < match.index) {
- const beforeText = text.slice(last, match.index);
- results.push(
- renderEmoji({
- text: beforeText,
- sizeClass,
- key: count++,
- renderNonEmoji: renderNewLines,
- })
- );
- }
-
- const [, toHighlight] = match;
- results.push(
-
- {renderEmoji({
- text: toHighlight,
- sizeClass,
- key: count++,
- renderNonEmoji: renderNewLines,
- })}
-
- );
+ if (!match) {
+ return ;
+ }
- // @ts-ignore
- last = FIND_BEGIN_END.lastIndex;
- match = FIND_BEGIN_END.exec(text);
- }
+ const sizeClass = '';
- if (last < text.length) {
+ while (match) {
+ if (last < match.index) {
+ const beforeText = text.slice(last, match.index);
results.push(
renderEmoji({
- text: text.slice(last),
+ text: beforeText,
sizeClass,
key: count++,
renderNonEmoji: renderNewLines,
@@ -85,6 +56,33 @@ export class MessageBodyHighlight extends React.Component {
);
}
- return results;
+ const [, toHighlight] = match;
+ results.push(
+
+ {renderEmoji({
+ text: toHighlight,
+ sizeClass,
+ key: count++,
+ renderNonEmoji: renderNewLines,
+ })}
+
+ );
+
+ // @ts-ignore
+ last = FIND_BEGIN_END.lastIndex;
+ match = FIND_BEGIN_END.exec(text);
}
-}
+
+ if (last < text.length) {
+ results.push(
+ renderEmoji({
+ text: text.slice(last),
+ sizeClass,
+ key: count++,
+ renderNonEmoji: renderNewLines,
+ })
+ );
+ }
+
+ return results;
+};
diff --git a/ts/components/conversation/ContactName.tsx b/ts/components/conversation/ContactName.tsx
index b11ab60f7..0bceee81b 100644
--- a/ts/components/conversation/ContactName.tsx
+++ b/ts/components/conversation/ContactName.tsx
@@ -34,7 +34,7 @@ export const ContactName = (props: Props) => {
: {}) as React.CSSProperties;
const textProfile = profileName || name || window.i18n('anonymous');
const profileElement = shouldShowProfile ? (
-
+
) : null;
diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx
index 115d7eb2b..8063c76a6 100644
--- a/ts/components/conversation/ConversationHeader.tsx
+++ b/ts/components/conversation/ConversationHeader.tsx
@@ -219,6 +219,8 @@ export type ConversationHeaderTitleProps = {
const ConversationHeaderTitle = () => {
const headerTitleProps = useSelector(getConversationHeaderTitleProps);
+ const notificationSetting = useSelector(getCurrentNotificationSettingText);
+ const marginXS = useTheme().common.margins.xs;
if (!headerTitleProps) {
return null;
}
@@ -256,12 +258,10 @@ const ConversationHeaderTitle = () => {
memberCountText = i18n('members', [count]);
}
- const notificationSetting = useSelector(getCurrentNotificationSettingText);
const notificationSubtitle = notificationSetting
? window.i18n('notificationSubtitle', notificationSetting)
: null;
const title = profileName || name || phoneNumber;
- const marginXS = useTheme().common.margins.xs;
return (
diff --git a/ts/components/conversation/Emojify.tsx b/ts/components/conversation/Emojify.tsx
index 9fa41a70d..5a83619c9 100644
--- a/ts/components/conversation/Emojify.tsx
+++ b/ts/components/conversation/Emojify.tsx
@@ -43,6 +43,7 @@ export class Emojify extends React.Component
{
while (match) {
if (last < match.index) {
const textWithNoEmoji = text.slice(last, match.index);
+
results.push(
renderNonEmoji({
text: textWithNoEmoji,
diff --git a/ts/components/conversation/Image.tsx b/ts/components/conversation/Image.tsx
index 1dd4f9a0e..e1c59b736 100644
--- a/ts/components/conversation/Image.tsx
+++ b/ts/components/conversation/Image.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useCallback } from 'react';
import classNames from 'classnames';
import { Spinner } from '../basic/Spinner';
@@ -57,6 +57,11 @@ export const Image = (props: Props) => {
width,
} = props;
+ const onDragStart = useCallback((e: any) => {
+ e.preventDefault();
+ return false;
+ }, []);
+
const { caption, pending } = attachment || { caption: null, pending: true };
const canClick = onClick && !pending;
const role = canClick ? 'button' : undefined;
@@ -106,6 +111,7 @@ export const Image = (props: Props) => {
height={height}
width={width}
src={srcData}
+ onDragStart={onDragStart}
/>
)}
{caption ? (
@@ -113,6 +119,7 @@ export const Image = (props: Props) => {
className="module-image__caption-icon"
src="images/caption-shadow.svg"
alt={window.i18n('imageCaptionIconAlt')}
+ onDragStart={onDragStart}
/>
) : null}
{
clearInterval(this.expirationCheckInterval);
}
if (this.expiredTimeout) {
- clearTimeout(this.expiredTimeout);
+ global.clearTimeout(this.expiredTimeout);
}
}
- public componentDidUpdate(prevProps: Props) {
+ public componentDidUpdate() {
this.checkExpired();
}
@@ -726,14 +726,15 @@ class MessageInner extends React.PureComponent
{
private onQuoteClick(e: any) {
const { quote, multiSelectMode, id } = this.props;
+ e.preventDefault();
+ e.stopPropagation();
if (!quote) {
window.log.warn('onQuoteClick: quote not valid');
return;
}
const quoteId = _.toNumber(quote.messageId);
const { authorPhoneNumber, referencedMessageNotFound } = quote;
- e.preventDefault();
- e.stopPropagation();
+
if (multiSelectMode && id) {
window.inboxStore?.dispatch(toggleSelectedMessageId(id));
@@ -810,7 +811,7 @@ class MessageInner extends React.PureComponent {
const { timestamp, serverTimestamp, authorPhoneNumber, attachments, convoId } = this.props;
e.stopPropagation();
-
+ e.preventDefault();
if (!attachments?.length) {
return;
}
@@ -828,6 +829,7 @@ class MessageInner extends React.PureComponent {
private onClickOnMessageOuterContainer(event: any) {
const { multiSelectMode, id } = this.props;
+
const selection = window.getSelection();
// Text is being selected
if (selection && selection.type === 'Range') {
@@ -839,7 +841,8 @@ class MessageInner extends React.PureComponent {
if ((!multiSelectMode && target.className === 'text-selectable') || window.contextMenuShown) {
return;
}
-
+ event.preventDefault();
+ event.stopPropagation();
if (id) {
window.inboxStore?.dispatch(toggleSelectedMessageId(id));
}
diff --git a/ts/components/conversation/Quote.tsx b/ts/components/conversation/Quote.tsx
index 557aaf2ee..1fc4b4c66 100644
--- a/ts/components/conversation/Quote.tsx
+++ b/ts/components/conversation/Quote.tsx
@@ -1,6 +1,6 @@
// tslint:disable:react-this-binding-issue
-import React, { useState } from 'react';
+import React, { useCallback, useState } from 'react';
import classNames from 'classnames';
import * as MIME from '../../../ts/types/MIME';
@@ -113,6 +113,11 @@ export const QuoteImage = (props: any) => {
const { loading, urlToLoad } = useEncryptedFileFetch(url, contentType);
const srcData = !loading ? urlToLoad : '';
+ const onDragStart = useCallback((e:any) => {
+ e.preventDefault();
+ return false;
+ }, []);
+
const iconElement = icon ? (
@@ -128,7 +133,12 @@ export const QuoteImage = (props: any) => {
return (
-
+
{iconElement}
);
diff --git a/ts/components/conversation/media-gallery/MediaGridItem.tsx b/ts/components/conversation/media-gallery/MediaGridItem.tsx
index 49f722445..9383a3467 100644
--- a/ts/components/conversation/media-gallery/MediaGridItem.tsx
+++ b/ts/components/conversation/media-gallery/MediaGridItem.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useCallback, useState } from 'react';
import classNames from 'classnames';
import { isImageTypeSupported, isVideoTypeSupported } from '../../../util/GoogleChrome';
@@ -21,6 +21,11 @@ const MediaGridItemContent = (props: Props) => {
const [imageBroken, setImageBroken] = useState(false);
const { loading, urlToLoad } = useEncryptedFileFetch(urlToDecrypt, contentType);
+
+ const onDragStart = useCallback((e: any) => {
+ e.preventDefault();
+ return false;
+ }, []);
// data will be url if loading is finished and '' if not
const srcData = !loading ? urlToLoad : '';
@@ -52,6 +57,7 @@ const MediaGridItemContent = (props: Props) => {
className="module-media-grid-item__image"
src={srcData}
onError={onImageError}
+ onDragStart={onDragStart}
/>
);
} else if (contentType && isVideoTypeSupported(contentType)) {
@@ -73,6 +79,7 @@ const MediaGridItemContent = (props: Props) => {
className="module-media-grid-item__image"
src={srcData}
onError={onImageError}
+ onDragStart={onDragStart}
/>
diff --git a/ts/components/session/ActionsPanel.tsx b/ts/components/session/ActionsPanel.tsx
index c94a8d1bc..6ae2da3c8 100644
--- a/ts/components/session/ActionsPanel.tsx
+++ b/ts/components/session/ActionsPanel.tsx
@@ -249,9 +249,9 @@ export const ActionsPanel = () => {
// wait for cleanUpMediasInterval and then start cleaning up medias
// this would be way easier to just be able to not trigger a call with the setInterval
useEffect(() => {
- const timeout = global.setTimeout(() => setStartCleanUpMedia(true), cleanUpMediasInterval);
+ const timeout = setTimeout(() => setStartCleanUpMedia(true), cleanUpMediasInterval);
- return () => global.clearTimeout(timeout);
+ return () => clearTimeout(timeout);
}, []);
useInterval(
diff --git a/ts/components/session/conversation/SessionCompositionBox.tsx b/ts/components/session/conversation/SessionCompositionBox.tsx
index d831f2c5b..4465b2208 100644
--- a/ts/components/session/conversation/SessionCompositionBox.tsx
+++ b/ts/components/session/conversation/SessionCompositionBox.tsx
@@ -964,6 +964,63 @@ class SessionCompositionBoxInner extends React.Component
{
this.setState({ message });
}
+ private getSelectionBasedOnMentions(index: number) {
+ // we have to get the real selectionStart/end of an index in the mentions box.
+ // this is kind of a pain as the mentions box has two inputs, one with the real text, and one with the extracted mentions
+
+ // the index shown to the user is actually just the visible part of the mentions (so the part between ᅲ...ᅭ
+ const matches = this.state.message.match(this.mentionsRegex);
+
+ let lastMatchStartIndex = 0;
+ let lastMatchEndIndex = 0;
+ let lastRealMatchEndIndex = 0;
+
+ if (!matches) {
+ return index;
+ }
+ const mapStartToLengthOfMatches = matches.map(match => {
+ const displayNameStart = match.indexOf('\uFFD7') + 1;
+ const displayNameEnd = match.lastIndexOf('\uFFD2');
+ const displayName = match.substring(displayNameStart, displayNameEnd);
+
+ const currentMatchStartIndex = this.state.message.indexOf(match) + lastMatchStartIndex;
+ lastMatchStartIndex = currentMatchStartIndex;
+ lastMatchEndIndex = currentMatchStartIndex + match.length;
+
+ const realLength = displayName.length + 1;
+ lastRealMatchEndIndex = lastRealMatchEndIndex + realLength;
+
+ // the +1 is for the @
+ return {
+ length: displayName.length + 1,
+ lastRealMatchEndIndex,
+ start: lastMatchStartIndex,
+ end: lastMatchEndIndex,
+ };
+ });
+
+ const beforeFirstMatch = index < mapStartToLengthOfMatches[0].start;
+ if (beforeFirstMatch) {
+ // those first char are always just char, so the mentions logic does not come into account
+ return index;
+ }
+ const lastMatchMap = _.last(mapStartToLengthOfMatches);
+
+ if (!lastMatchMap) {
+ return Number.MAX_SAFE_INTEGER;
+ }
+
+ const indexIsAfterEndOfLastMatch = lastMatchMap.lastRealMatchEndIndex <= index;
+ if (indexIsAfterEndOfLastMatch) {
+ const lastEnd = lastMatchMap.end;
+ const diffBetweenEndAndLastRealEnd = index - lastMatchMap.lastRealMatchEndIndex;
+ return lastEnd + diffBetweenEndAndLastRealEnd - 1;
+ }
+ // now this is the hard part, the cursor is currently between the end of the first match and the start of the last match
+ // for now, just append it to the end
+ return Number.MAX_SAFE_INTEGER;
+ }
+
private onEmojiClick({ colons }: { colons: string }) {
const messageBox = this.textarea.current;
if (!messageBox) {
@@ -973,10 +1030,12 @@ class SessionCompositionBoxInner extends React.Component {
const { message } = this.state;
const currentSelectionStart = Number(messageBox.selectionStart);
- const currentSelectionEnd = Number(messageBox.selectionEnd);
- const before = message.slice(0, currentSelectionStart);
- const end = message.slice(currentSelectionEnd);
+ const realSelectionStart = this.getSelectionBasedOnMentions(currentSelectionStart);
+
+ const before = message.slice(0, realSelectionStart);
+ const end = message.slice(realSelectionStart);
+
const newMessage = `${before}${colons}${end}`;
this.setState({ message: newMessage }, () => {
diff --git a/ts/components/session/conversation/SessionEmojiPanel.tsx b/ts/components/session/conversation/SessionEmojiPanel.tsx
index 9657c15ca..b9b174712 100644
--- a/ts/components/session/conversation/SessionEmojiPanel.tsx
+++ b/ts/components/session/conversation/SessionEmojiPanel.tsx
@@ -3,42 +3,27 @@ import classNames from 'classnames';
import { Picker } from 'emoji-mart';
import { Constants } from '../../../session';
-interface Props {
+type Props = {
onEmojiClicked: (emoji: any) => void;
show: boolean;
-}
+};
-interface State {
- // FIXME Use Emoji-Mart categories
- category: null;
-}
+export const SessionEmojiPanel = (props: Props) => {
+ const { onEmojiClicked, show } = props;
-export class SessionEmojiPanel extends React.Component {
- constructor(props: Props) {
- super(props);
-
- this.state = {
- category: null,
- };
- }
-
- public render() {
- const { onEmojiClicked, show } = this.props;
-
- return (
-
-
'./images/emoji/emoji-sheet-twitter-32.png'}
- set={'twitter'}
- sheetSize={32}
- darkMode={true}
- color={Constants.UI.COLORS.GREEN}
- showPreview={true}
- title={''}
- onSelect={onEmojiClicked}
- autoFocus={true}
- />
-
- );
- }
-}
+ return (
+
+
'./images/emoji/emoji-sheet-twitter-32.png'}
+ set={'twitter'}
+ sheetSize={32}
+ darkMode={true}
+ color={Constants.UI.COLORS.GREEN}
+ showPreview={true}
+ title={''}
+ onSelect={onEmojiClicked}
+ autoFocus={true}
+ />
+
+ );
+};
diff --git a/ts/components/session/conversation/SessionMessagesListContainer.tsx b/ts/components/session/conversation/SessionMessagesListContainer.tsx
index 606e24d4c..b47e7115f 100644
--- a/ts/components/session/conversation/SessionMessagesListContainer.tsx
+++ b/ts/components/session/conversation/SessionMessagesListContainer.tsx
@@ -62,7 +62,7 @@ class SessionMessagesListContainerInner extends React.Component {
public componentWillUnmount() {
if (this.timeoutResetQuotedScroll) {
- clearTimeout(this.timeoutResetQuotedScroll);
+ global.clearTimeout(this.timeoutResetQuotedScroll);
}
}
@@ -170,7 +170,7 @@ class SessionMessagesListContainerInner extends React.Component {
*/
private setupTimeoutResetQuotedHighlightedMessage(messageId: string | undefined) {
if (this.timeoutResetQuotedScroll) {
- clearTimeout(this.timeoutResetQuotedScroll);
+ global.clearTimeout(this.timeoutResetQuotedScroll);
}
if (messageId !== undefined) {
diff --git a/ts/components/session/icon/SessionIcon.tsx b/ts/components/session/icon/SessionIcon.tsx
index e97ab2363..c09b9f5ad 100644
--- a/ts/components/session/icon/SessionIcon.tsx
+++ b/ts/components/session/icon/SessionIcon.tsx
@@ -118,13 +118,13 @@ const animation = (props: {
};
//tslint:disable no-unnecessary-callback-wrapper
-const Svg = styled.svg`
+const Svg = React.memo(styled.svg`
width: ${props => props.width};
transform: ${props => `rotate(${props.iconRotation}deg)`};
animation: ${props => animation(props)};
border-radius: ${props => props.borderRadius};
filter: ${props => (props.noScale ? `drop-shadow(0px 0px 4px ${props.iconColor})` : '')};
-`;
+`);
//tslint:enable no-unnecessary-callback-wrapper
const SessionSvg = (props: {
diff --git a/ts/data/data.ts b/ts/data/data.ts
index 52c652fcf..11f1eaf53 100644
--- a/ts/data/data.ts
+++ b/ts/data/data.ts
@@ -310,7 +310,7 @@ function _removeJob(id: number) {
}
if (_jobs[id].timer) {
- clearTimeout(_jobs[id].timer);
+ global.clearTimeout(_jobs[id].timer);
_jobs[id].timer = null;
}
diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts
index d693dda3e..e598d8860 100644
--- a/ts/interactions/conversationInteractions.ts
+++ b/ts/interactions/conversationInteractions.ts
@@ -361,6 +361,7 @@ export async function uploadOurAvatar(newAvatarDecrypted?: ArrayBuffer) {
getConversationController()
.get(UserUtils.getOurPubKeyStrFromCache())
?.get('profileKey') || null;
+
profileKey = ourConvoProfileKey ? fromHexToArray(ourConvoProfileKey) : null;
if (!profileKey) {
window.log.info('our profileKey not found. Not reuploading our avatar');
diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts
index c063918ec..7195fd6a4 100644
--- a/ts/models/conversation.ts
+++ b/ts/models/conversation.ts
@@ -20,7 +20,7 @@ import {
saveMessages,
updateConversation,
} from '../../ts/data/data';
-import { fromArrayBufferToBase64, fromBase64ToArrayBuffer, toHex } from '../session/utils/String';
+import { toHex } from '../session/utils/String';
import {
actions as conversationActions,
conversationChanged,
@@ -300,7 +300,7 @@ export class ConversationModel extends Backbone.Model {
public setTypingRefreshTimer() {
if (this.typingRefreshTimer) {
- clearTimeout(this.typingRefreshTimer);
+ global.clearTimeout(this.typingRefreshTimer);
}
this.typingRefreshTimer = global.setTimeout(this.onTypingRefreshTimeout.bind(this), 10 * 1000);
}
@@ -315,7 +315,7 @@ export class ConversationModel extends Backbone.Model {
public setTypingPauseTimer() {
if (this.typingPauseTimer) {
- clearTimeout(this.typingPauseTimer);
+ global.clearTimeout(this.typingPauseTimer);
}
this.typingPauseTimer = global.setTimeout(this.onTypingPauseTimeout.bind(this), 10 * 1000);
}
@@ -329,11 +329,11 @@ export class ConversationModel extends Backbone.Model {
public clearTypingTimers() {
if (this.typingPauseTimer) {
- clearTimeout(this.typingPauseTimer);
+ global.clearTimeout(this.typingPauseTimer);
this.typingPauseTimer = null;
}
if (this.typingRefreshTimer) {
- clearTimeout(this.typingRefreshTimer);
+ global.clearTimeout(this.typingRefreshTimer);
this.typingRefreshTimer = null;
}
}
@@ -1469,7 +1469,7 @@ export class ConversationModel extends Backbone.Model {
const wasTyping = !!this.typingTimer;
if (this.typingTimer) {
- clearTimeout(this.typingTimer);
+ global.clearTimeout(this.typingTimer);
this.typingTimer = null;
}
@@ -1498,7 +1498,7 @@ export class ConversationModel extends Backbone.Model {
public async clearContactTypingTimer(sender: string) {
if (!!this.typingTimer) {
- clearTimeout(this.typingTimer);
+ global.clearTimeout(this.typingTimer);
this.typingTimer = null;
// User was previously typing, but timed out or we received message. State change!
diff --git a/ts/session/constants.ts b/ts/session/constants.ts
index f65b4e708..0ed905b8f 100644
--- a/ts/session/constants.ts
+++ b/ts/session/constants.ts
@@ -20,7 +20,7 @@ export const TTL_DEFAULT = {
export const SWARM_POLLING_TIMEOUT = {
ACTIVE: DURATION.SECONDS * 5,
MEDIUM_ACTIVE: DURATION.SECONDS * 60,
- INACTIVE: DURATION.MINUTES * 60,
+ INACTIVE: DURATION.SECONDS * 120,
};
export const PROTOCOLS = {
diff --git a/ts/session/group/index.ts b/ts/session/group/index.ts
index 4e59db40b..1b11436a4 100644
--- a/ts/session/group/index.ts
+++ b/ts/session/group/index.ts
@@ -98,7 +98,7 @@ export async function initiateGroupUpdate(
name: groupName,
members,
// remove from the zombies list the zombies not which are not in the group anymore
- zombies: convo.get('zombies').filter(z => members.includes(z)),
+ zombies: convo.get('zombies')?.filter(z => members.includes(z)),
activeAt: Date.now(),
expireTimer: convo.get('expireTimer'),
avatar,
@@ -119,23 +119,28 @@ export async function initiateGroupUpdate(
if (diff.newName?.length) {
const nameOnlyDiff: GroupDiff = { newName: diff.newName };
const dbMessageName = await addUpdateMessage(convo, nameOnlyDiff, 'outgoing', Date.now());
- getMessageController().register(dbMessageName.id, dbMessageName);
- await sendNewName(convo, diff.newName, dbMessageName.id);
+ getMessageController().register(dbMessageName.id as string, dbMessageName);
+ await sendNewName(convo, diff.newName, dbMessageName.id as string);
}
if (diff.joiningMembers?.length) {
const joiningOnlyDiff: GroupDiff = { joiningMembers: diff.joiningMembers };
const dbMessageAdded = await addUpdateMessage(convo, joiningOnlyDiff, 'outgoing', Date.now());
- getMessageController().register(dbMessageAdded.id, dbMessageAdded);
- await sendAddedMembers(convo, diff.joiningMembers, dbMessageAdded.id, updateObj);
+ getMessageController().register(dbMessageAdded.id as string, dbMessageAdded);
+ await sendAddedMembers(convo, diff.joiningMembers, dbMessageAdded.id as string, updateObj);
}
if (diff.leavingMembers?.length) {
const leavingOnlyDiff: GroupDiff = { leavingMembers: diff.leavingMembers };
const dbMessageLeaving = await addUpdateMessage(convo, leavingOnlyDiff, 'outgoing', Date.now());
- getMessageController().register(dbMessageLeaving.id, dbMessageLeaving);
+ getMessageController().register(dbMessageLeaving.id as string, dbMessageLeaving);
const stillMembers = members;
- await sendRemovedMembers(convo, diff.leavingMembers, stillMembers, dbMessageLeaving.id);
+ await sendRemovedMembers(
+ convo,
+ diff.leavingMembers,
+ stillMembers,
+ dbMessageLeaving.id as string
+ );
}
await convo.commit();
}
@@ -262,7 +267,7 @@ export async function updateOrCreateClosedGroup(details: GroupInfo) {
const isBlocked = details.blocked || false;
if (conversation.isClosedGroup() || conversation.isMediumGroup()) {
- await BlockedNumberController.setGroupBlocked(conversation.id, isBlocked);
+ await BlockedNumberController.setGroupBlocked(conversation.id as string, isBlocked);
}
if (details.admins?.length) {
@@ -327,12 +332,12 @@ export async function leaveClosedGroup(groupId: string) {
received_at: now,
expireTimer: 0,
});
- getMessageController().register(dbMessage.id, dbMessage);
+ getMessageController().register(dbMessage.id as string, dbMessage);
// Send the update to the group
const ourLeavingMessage = new ClosedGroupMemberLeftMessage({
timestamp: Date.now(),
groupId,
- identifier: dbMessage.id,
+ identifier: dbMessage.id as string,
});
window?.log?.info(`We are leaving the group ${groupId}. Sending our leaving message.`);
@@ -357,7 +362,7 @@ async function sendNewName(convo: ConversationModel, name: string, messageId: st
// Send the update to the group
const nameChangeMessage = new ClosedGroupNameChangeMessage({
timestamp: Date.now(),
- groupId,
+ groupId: groupId as string,
identifier: messageId,
name,
});
@@ -427,7 +432,7 @@ export async function sendRemovedMembers(
}
const ourNumber = UserUtils.getOurPubKeyFromCache();
const admins = convo.get('groupAdmins') || [];
- const groupId = convo.get('id');
+ const groupId = convo.get('id') as string;
const isCurrentUserAdmin = admins.includes(ourNumber.key);
const isUserLeaving = removedMembers.includes(ourNumber.key);
diff --git a/ts/session/snode_api/SNodeAPI.ts b/ts/session/snode_api/SNodeAPI.ts
index 18b21a452..492d35db3 100644
--- a/ts/session/snode_api/SNodeAPI.ts
+++ b/ts/session/snode_api/SNodeAPI.ts
@@ -565,7 +565,7 @@ export async function retrieveNextMessages(
}
if (result.status !== 200) {
- window.log('retrieve result is not 200');
+ window?.log?.warn('retrieve result is not 200');
return [];
}
diff --git a/ts/session/snode_api/onions.ts b/ts/session/snode_api/onions.ts
index 13778012a..feca249f0 100644
--- a/ts/session/snode_api/onions.ts
+++ b/ts/session/snode_api/onions.ts
@@ -909,7 +909,7 @@ export async function lokiOnionFetch(
throw new Error(ERROR_CODE_NO_CONNECT);
}
if (e?.message === CLOCK_OUT_OF_SYNC_MESSAGE_ERROR) {
- window?.log?.warn('Its an clock out of sync error ');
+ window?.log?.warn('Its a clock out of sync error ');
throw new pRetry.AbortError(CLOCK_OUT_OF_SYNC_MESSAGE_ERROR);
}
throw e;
diff --git a/ts/session/snode_api/swarmPolling.ts b/ts/session/snode_api/swarmPolling.ts
index 512b0c7e2..9a4f64b80 100644
--- a/ts/session/snode_api/swarmPolling.ts
+++ b/ts/session/snode_api/swarmPolling.ts
@@ -202,6 +202,9 @@ export class SwarmPolling {
const messages = _.uniqBy(_.flatten(results), (x: any) => x.hash);
if (isGroup) {
+ window?.log?.info(
+ `Polled for group(${pubkey}): group.pubkey, got ${messages.length} messages back.`
+ );
// update the last fetched timestamp
this.groupPolling = this.groupPolling.map(group => {
if (PubKey.isEqual(pubkey, group.pubkey)) {
diff --git a/ts/session/utils/AttachmentsDownload.ts b/ts/session/utils/AttachmentsDownload.ts
index 9df66264e..4cdc693ca 100644
--- a/ts/session/utils/AttachmentsDownload.ts
+++ b/ts/session/utils/AttachmentsDownload.ts
@@ -47,7 +47,7 @@ export async function start(options: any = {}) {
export function stop() {
enabled = false;
if (timeout) {
- clearTimeout(timeout);
+ global.clearTimeout(timeout);
timeout = null;
}
}
diff --git a/ts/session/utils/TaskWithTimeout.ts b/ts/session/utils/TaskWithTimeout.ts
index 83da297bc..28e1747c7 100644
--- a/ts/session/utils/TaskWithTimeout.ts
+++ b/ts/session/utils/TaskWithTimeout.ts
@@ -23,7 +23,7 @@ export const createTaskWithTimeout = (task: any, id: string, givenTimeout?: numb
const localTimer = timer;
if (localTimer) {
timer = null;
- clearTimeout(localTimer);
+ global.clearTimeout(localTimer);
}
} catch (error) {
window?.log?.error(
diff --git a/ts/session/utils/syncUtils.ts b/ts/session/utils/syncUtils.ts
index c2fb62956..e8e373e22 100644
--- a/ts/session/utils/syncUtils.ts
+++ b/ts/session/utils/syncUtils.ts
@@ -122,7 +122,7 @@ const getValidClosedGroups = async (convos: Array) => {
c =>
!!c.get('active_at') &&
c.isMediumGroup() &&
- c.get('members').includes(ourPubKey) &&
+ c.get('members')?.includes(ourPubKey) &&
!c.get('left') &&
!c.get('isKickedFromGroup') &&
!c.isBlocked() &&
@@ -138,7 +138,7 @@ const getValidClosedGroups = async (convos: Array) => {
}
return new ConfigurationMessageClosedGroup({
- publicKey: groupPubKey,
+ publicKey: groupPubKey as string,
name: c.get('name') || '',
members: c.get('members') || [],
admins: c.get('groupAdmins') || [],
@@ -188,7 +188,7 @@ const getValidContacts = (convos: Array) => {
}
return new ConfigurationMessageContact({
- publicKey: c.id,
+ publicKey: c.id as string,
displayName: c.getLokiProfile()?.displayName,
profilePictureURL: c.get('avatarPointer'),
profileKey: !profileKeyForContact?.length ? undefined : profileKeyForContact,
diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts
index 0af0de3a3..ac13fe817 100644
--- a/ts/state/ducks/conversations.ts
+++ b/ts/state/ducks/conversations.ts
@@ -16,6 +16,7 @@ import {
import { LightBoxOptions } from '../../components/session/conversation/SessionConversation';
import { ReplyingToMessageProps } from '../../components/session/conversation/SessionCompositionBox';
import { QuotedAttachmentType } from '../../components/conversation/Quote';
+import { perfEnd, perfStart } from '../../session/utils/Performance';
export type MessageModelProps = {
propsForMessage: PropsForMessage;
@@ -599,10 +600,12 @@ const conversationsSlice = createSlice({
}>
>
) {
+ perfStart('messagesAdded');
action.payload.forEach(added => {
// tslint:disable-next-line: no-parameter-reassignment
state = handleMessageAdded(state, added);
});
+ perfEnd('messagesAdded', 'messagesAdded');
return state;
},
diff --git a/ts/test/session/unit/swarm_polling/SwarmPolling_test.ts b/ts/test/session/unit/swarm_polling/SwarmPolling_test.ts
index 5ff3eca58..8a18dd1a9 100644
--- a/ts/test/session/unit/swarm_polling/SwarmPolling_test.ts
+++ b/ts/test/session/unit/swarm_polling/SwarmPolling_test.ts
@@ -80,7 +80,7 @@ describe('SwarmPolling', () => {
ConversationTypeEnum.GROUP
);
convo.set('active_at', Date.now() - 3555);
- expect(swarmPolling.TEST_getPollingTimeout(PubKey.cast(convo.id))).to.eq(
+ expect(swarmPolling.TEST_getPollingTimeout(PubKey.cast(convo.id as string))).to.eq(
SWARM_POLLING_TIMEOUT.ACTIVE
);
});
@@ -91,7 +91,7 @@ describe('SwarmPolling', () => {
ConversationTypeEnum.GROUP
);
convo.set('active_at', undefined);
- expect(swarmPolling.TEST_getPollingTimeout(PubKey.cast(convo.id))).to.eq(
+ expect(swarmPolling.TEST_getPollingTimeout(PubKey.cast(convo.id as string))).to.eq(
SWARM_POLLING_TIMEOUT.INACTIVE
);
});
@@ -102,7 +102,7 @@ describe('SwarmPolling', () => {
ConversationTypeEnum.GROUP
);
convo.set('active_at', Date.now() - 1000 * 3600 * 23);
- expect(swarmPolling.TEST_getPollingTimeout(PubKey.cast(convo.id))).to.eq(
+ expect(swarmPolling.TEST_getPollingTimeout(PubKey.cast(convo.id as string))).to.eq(
SWARM_POLLING_TIMEOUT.MEDIUM_ACTIVE
);
});
@@ -113,7 +113,7 @@ describe('SwarmPolling', () => {
ConversationTypeEnum.GROUP
);
convo.set('active_at', Date.now() - 1000 * 3600 * 25);
- expect(swarmPolling.TEST_getPollingTimeout(PubKey.cast(convo.id))).to.eq(
+ expect(swarmPolling.TEST_getPollingTimeout(PubKey.cast(convo.id as string))).to.eq(
SWARM_POLLING_TIMEOUT.INACTIVE
);
});
@@ -150,7 +150,7 @@ describe('SwarmPolling', () => {
ConversationTypeEnum.GROUP
);
convo.set('active_at', Date.now());
- const groupConvoPubkey = PubKey.cast(convo.id);
+ const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
@@ -167,7 +167,7 @@ describe('SwarmPolling', () => {
);
convo.set('active_at', 1);
- const groupConvoPubkey = PubKey.cast(convo.id);
+ const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
@@ -184,7 +184,7 @@ describe('SwarmPolling', () => {
);
convo.set('active_at', 1);
- const groupConvoPubkey = PubKey.cast(convo.id);
+ const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
@@ -202,7 +202,7 @@ describe('SwarmPolling', () => {
);
convo.set('active_at', Date.now());
- const groupConvoPubkey = PubKey.cast(convo.id);
+ const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
clock.tick(6000);
@@ -222,7 +222,7 @@ describe('SwarmPolling', () => {
);
convo.set('active_at', Date.now());
- const groupConvoPubkey = PubKey.cast(convo.id);
+ const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
@@ -243,7 +243,7 @@ describe('SwarmPolling', () => {
);
convo.set('active_at', Date.now());
- const groupConvoPubkey = PubKey.cast(convo.id);
+ const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
@@ -268,7 +268,7 @@ describe('SwarmPolling', () => {
);
convo.set('active_at', Date.now());
- groupConvoPubkey = PubKey.cast(convo.id);
+ groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
});
diff --git a/yarn.lock b/yarn.lock
index fb639bed3..9469bdaa9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -692,10 +692,10 @@
dependencies:
"@types/react" "*"
-"@types/react-dom@16.8.2":
- version "16.8.2"
- resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.2.tgz#9bd7d33f908b243ff0692846ef36c81d4941ad12"
- integrity sha512-MX7n1wq3G/De15RGAAqnmidzhr2Y9O/ClxPxyqaNg96pGyeXUYPSvujgzEVpLo9oIP4Wn1UETl+rxTN02KEpBw==
+"@types/react-dom@^17.0.2":
+ version "17.0.9"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.9.tgz#441a981da9d7be117042e1a6fd3dac4b30f55add"
+ integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==
dependencies:
"@types/react" "*"
@@ -753,14 +753,6 @@
"@types/prop-types" "*"
csstype "^2.2.0"
-"@types/react@16.8.5":
- version "16.8.5"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.5.tgz#03b9a6597bc20f6eaaed43f377a160f7e41c2b90"
- integrity sha512-8LRySaaSJVLNZb2dbOGvGmzn88cbAfrgDpuWy+6lLgQ0OJFgHHvyuaCX4/7ikqJlpmCPf4uazJAZcfTQRdJqdQ==
- dependencies:
- "@types/prop-types" "*"
- csstype "^2.2.0"
-
"@types/react@^16.8.3":
version "16.14.7"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.7.tgz#b62bd8cc4675d6fe3976126cdd208deda267f1fb"
@@ -770,6 +762,15 @@
"@types/scheduler" "*"
csstype "^3.0.2"
+"@types/react@^17.0.15":
+ version "17.0.15"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.15.tgz#c7533dc38025677e312606502df7656a6ea626d0"
+ integrity sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
"@types/redux-logger@3.0.7":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@types/redux-logger/-/redux-logger-3.0.7.tgz#163f6f6865c69c21d56f9356dc8d741718ec0db0"
@@ -7496,15 +7497,14 @@ react-contexify@5.0.0:
dependencies:
clsx "^1.1.1"
-react-dom@16.8.3:
- version "16.8.3"
- resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.3.tgz#ae236029e66210783ac81999d3015dfc475b9c32"
- integrity sha512-ttMem9yJL4/lpItZAQ2NTFAbV7frotHk5DZEHXUOws2rMmrsvh1Na7ThGT0dTzUIl6pqTOi5tYREfL8AEna3lA==
+react-dom@^17.0.2:
+ version "17.0.2"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
+ integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
- prop-types "^15.6.2"
- scheduler "^0.13.3"
+ scheduler "^0.20.2"
react-emoji-render@^1.2.4:
version "1.2.4"
@@ -7649,19 +7649,13 @@ react-virtualized@9.22.3:
prop-types "^15.7.2"
react-lifecycles-compat "^3.0.4"
-react-window-infinite-loader@^1.0.7:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/react-window-infinite-loader/-/react-window-infinite-loader-1.0.7.tgz#958ef1a689d20dce122ef377583acd987760aee8"
- integrity sha512-wg3LWkUpG21lhv+cZvNy+p0+vtclZw+9nP2vO6T9PKT50EN1cUq37Dq6FzcM38h/c2domE0gsUhb6jHXtGogAA==
-
-react@^16.13.1:
- version "16.13.1"
- resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
- integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
+react@^17.0.2:
+ version "17.0.2"
+ resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
+ integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
- prop-types "^15.6.2"
read-config-file@6.0.0:
version "6.0.0"
@@ -8213,10 +8207,10 @@ sax@^1.2.4:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
-scheduler@^0.13.3:
- version "0.13.6"
- resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889"
- integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==
+scheduler@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
+ integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"