From 2318a291f068a74dd507fbc95af397a342221255 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 22 Mar 2022 11:00:49 +1100 Subject: [PATCH 01/10] bump to 1.7.9 without calls --- package.json | 2 +- preload.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 75d367f9f..b623cdb6d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "session-desktop", "productName": "Session", "description": "Private messaging from your desktop", - "version": "1.8.3", + "version": "1.7.9", "license": "GPL-3.0", "author": { "name": "Oxen Labs", diff --git a/preload.js b/preload.js index e0c56b2d6..e3f05df0f 100644 --- a/preload.js +++ b/preload.js @@ -31,7 +31,7 @@ window.getNodeVersion = () => config.node_version; window.sessionFeatureFlags = { useOnionRequests: true, - useCallMessage: true, + useCallMessage: false, }; window.versionInfo = { From 1e758c47968bd1781f1d612c403bbeb60c327fcf Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 22 Mar 2022 14:48:44 +1100 Subject: [PATCH 02/10] disable calls for 1.7.9 --- preload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preload.js b/preload.js index e0c56b2d6..e3f05df0f 100644 --- a/preload.js +++ b/preload.js @@ -31,7 +31,7 @@ window.getNodeVersion = () => config.node_version; window.sessionFeatureFlags = { useOnionRequests: true, - useCallMessage: true, + useCallMessage: false, }; window.versionInfo = { From 2a11d5e71f051b1974b2633132b348ff1eb913bf Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 28 Mar 2022 12:59:07 +1100 Subject: [PATCH 03/10] trigger a new offer on connect fail if we are caller the caller is just supposed to accept the offer and send an answer back --- ts/session/utils/calling/CallManager.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ts/session/utils/calling/CallManager.ts b/ts/session/utils/calling/CallManager.ts index 5ce99e7d8..1661d9a7a 100644 --- a/ts/session/utils/calling/CallManager.ts +++ b/ts/session/utils/calling/CallManager.ts @@ -32,7 +32,7 @@ import { approveConvoAndSendResponse } from '../../../interactions/conversationI export type InputItem = { deviceId: string; label: string }; -export const callTimeoutMs = 30000; +export const callTimeoutMs = 60000; /** * This uuid is set only once we accepted a call or started one. @@ -41,6 +41,8 @@ let currentCallUUID: string | undefined; let currentCallStartTimestamp: number | undefined; +let weAreCallerOnCurrentCall: boolean | undefined; + const rejectedCallUUIDS: Set = new Set(); export type CallManagerOptionsType = { @@ -498,6 +500,7 @@ export async function USER_callRecipient(recipient: string) { window.log.info('Sending preOffer message to ', ed25519Str(recipient)); const calledConvo = getConversationController().get(recipient); calledConvo.set('active_at', Date.now()); // addSingleOutgoingMessage does the commit for us on the convo + weAreCallerOnCurrentCall = true; await calledConvo?.addSingleOutgoingMessage({ sent_at: now, @@ -619,6 +622,7 @@ function handleConnectionStateChanged(pubkey: string) { function closeVideoCall() { window.log.info('closingVideoCall '); currentCallStartTimestamp = undefined; + weAreCallerOnCurrentCall = undefined; if (peerConnection) { peerConnection.ontrack = null; peerConnection.onicecandidate = null; @@ -747,11 +751,18 @@ function createOrGetPeerConnection(withPubkey: string) { if (peerConnection && peerConnection?.iceConnectionState === 'disconnected') { //this will trigger a negotation event with iceRestart set to true in the createOffer options set - global.setTimeout(() => { + global.setTimeout(async () => { window.log.info('onconnectionstatechange disconnected: restartIce()'); - if (peerConnection?.iceConnectionState === 'disconnected') { + if ( + peerConnection?.iceConnectionState === 'disconnected' && + withPubkey?.length && + weAreCallerOnCurrentCall === true + ) { + // we are the caller and the connection got dropped out, we need to send a new offer with iceRestart set to true. + // the recipient will get that new offer and send us a response back if he still online (peerConnection as any).restartIce(); + await createOfferAndSendIt(withPubkey); } }, 2000); } From ba53330afd26f596aff755e89a6dd917882fd38e Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 28 Mar 2022 13:31:03 +1100 Subject: [PATCH 04/10] add tests for getInitials --- .../AvatarPlaceHolder/AvatarPlaceHolder.tsx | 5 +- ts/test/session/unit/utils/Initials_test.ts | 82 +++++++++++++++++++ ts/util/getInitials.ts | 37 ++++++--- 3 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 ts/test/session/unit/utils/Initials_test.ts diff --git a/ts/components/avatar/AvatarPlaceHolder/AvatarPlaceHolder.tsx b/ts/components/avatar/AvatarPlaceHolder/AvatarPlaceHolder.tsx index 644647eef..f4baa8c11 100644 --- a/ts/components/avatar/AvatarPlaceHolder/AvatarPlaceHolder.tsx +++ b/ts/components/avatar/AvatarPlaceHolder/AvatarPlaceHolder.tsx @@ -97,10 +97,7 @@ export const AvatarPlaceHolder = (props: Props) => { ); } - let initials = getInitials(name)?.toLocaleUpperCase() || '0'; - if (name.indexOf(' ') === -1) { - initials = name.substring(0, 2).toLocaleUpperCase(); - } + const initials = getInitials(name); const fontSize = Math.floor(initials.length > 1 ? diameter * 0.4 : diameter * 0.5); diff --git a/ts/test/session/unit/utils/Initials_test.ts b/ts/test/session/unit/utils/Initials_test.ts new file mode 100644 index 000000000..8f1d26ab5 --- /dev/null +++ b/ts/test/session/unit/utils/Initials_test.ts @@ -0,0 +1,82 @@ +import { expect } from 'chai'; +import { getInitials } from '../../../../util/getInitials'; + +describe('getInitials', () => { + describe('empty of null string', () => { + it('initials: return undefined if string is undefined', () => { + expect(getInitials(undefined)).to.be.equal('0', 'should have return 0'); + }); + + it('initials: return undefined if string is empty', () => { + expect(getInitials('')).to.be.equal('0', 'should have return 0'); + }); + + it('initials: return undefined if string is null', () => { + expect(getInitials(null as any)).to.be.equal('0', 'should have return 0'); + }); + }); + + describe('name is a pubkey', () => { + it('initials: return the first char after 05 if it starts with 05 and has length >2 ', () => { + expect(getInitials('052')).to.be.equal('2', 'should have return 2'); + }); + + it('initials: return the first char after 05 capitalized if it starts with 05 and has length >2 ', () => { + expect(getInitials('05bcd')).to.be.equal('B', 'should have return B'); + }); + + it('initials: return the first char after 05 if it starts with 05 and has length >2 ', () => { + expect(getInitials('059052052052052052052052')).to.be.equal('9', 'should have return 9'); + }); + }); + + describe('name has a space in its content', () => { + it('initials: return the first char of each first 2 words if a space is present ', () => { + expect(getInitials('John Doe')).to.be.equal('JD', 'should have return JD'); + }); + + it('initials: return the first char capitalized of each first 2 words if a space is present ', () => { + expect(getInitials('John doe')).to.be.equal('JD', 'should have return JD capitalized'); + }); + + it('initials: return the first char capitalized of each first 2 words if a space is present, even with more than 2 words ', () => { + expect(getInitials('John Doe Alice')).to.be.equal('JD', 'should have return JD capitalized'); + }); + + it('initials: return the first char capitalized of each first 2 words if a space is present, even with more than 2 words ', () => { + expect(getInitials('John doe Alice')).to.be.equal('JD', 'should have return JD capitalized'); + }); + + describe('name is not ascii', () => { + // ß maps to SS in uppercase + it('initials: shorten to 2 char at most if the uppercase form length is > 2 ', () => { + expect(getInitials('John ß')).to.be.equal('JS', 'should have return JS capitalized'); + }); + + it('initials: shorten to 2 char at most if the uppercase form length is > 2 ', () => { + expect(getInitials('ß ß')).to.be.equal('SS', 'should have return SS capitalized'); + }); + }); + }); + + describe('name has NO spaces in its content', () => { + it('initials: return the first 2 chars of the first word if the name has no space ', () => { + expect(getInitials('JOHNY')).to.be.equal('JO', 'should have return JO'); + }); + + it('initials: return the first 2 chars capitalized of the first word if the name has no space ', () => { + expect(getInitials('Johnny')).to.be.equal('JO', 'should have return JO'); + }); + + describe('name is not ascii', () => { + // ß maps to SS in uppercase + it('initials: shorten to 2 char at most if the uppercase form length is > 2 ', () => { + expect(getInitials('ß')).to.be.equal('SS', 'should have return SS capitalized'); + }); + + it('initials: shorten to 2 char at most if the uppercase form length is > 2 ', () => { + expect(getInitials('ßß')).to.be.equal('SS', 'should have return SS capitalized'); + }); + }); + }); +}); diff --git a/ts/util/getInitials.ts b/ts/util/getInitials.ts index 8c7bb4777..c2ac667b8 100644 --- a/ts/util/getInitials.ts +++ b/ts/util/getInitials.ts @@ -1,18 +1,35 @@ -export function getInitials(name?: string): string | undefined { +export function getInitials(name?: string): string { if (!name || !name.length) { - return; + return '0'; } if (name.length > 2 && name.startsWith('05')) { - return name[2]; + // Just the third char of the pubkey when the name is a pubkey + return upperAndShorten(name[2]); } - const initials = name - .split(' ') - .slice(0, 2) - .map(n => { - return n[0]; - }); + if (name.indexOf(' ') === -1) { + // there is no space, just return the first 2 chars of the name - return initials.join(''); + if (name.length > 1) { + return upperAndShorten(name.slice(0, 2)); + } + return upperAndShorten(name[0]); + } + + // name has a space, just extract the first char of each words + return upperAndShorten( + name + .split(' ') + .slice(0, 2) + .map(n => { + return n[0]; + }) + .join('') + ); +} + +function upperAndShorten(str: string) { + // believe it or not, some chars put in uppercase can be more than one char. (ß for instance) + return str.toLocaleUpperCase().slice(0, 2); } From fd1657037adc56bfe619a6a6fe0defd4db896dd3 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 28 Mar 2022 14:09:48 +1100 Subject: [PATCH 05/10] add tests for emoji size rendering in messages --- ts/test/session/unit/utils/Emoji_test.ts | 132 ++++++++++++++++++++ ts/test/session/unit/utils/Initials_test.ts | 2 +- ts/util/emoji.ts | 7 +- 3 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 ts/test/session/unit/utils/Emoji_test.ts diff --git a/ts/test/session/unit/utils/Emoji_test.ts b/ts/test/session/unit/utils/Emoji_test.ts new file mode 100644 index 000000000..9391a08b5 --- /dev/null +++ b/ts/test/session/unit/utils/Emoji_test.ts @@ -0,0 +1,132 @@ +import { expect } from 'chai'; +import { getEmojiSizeClass } from '../../../../util/emoji'; + +describe('getEmojiSizeClass', () => { + describe('empty or null string', () => { + it('undefined as string', () => { + expect(getEmojiSizeClass(undefined as any)).to.be.equal('small', 'should have return small'); + }); + it('null as string', () => { + expect(getEmojiSizeClass(null as any)).to.be.equal('small', 'should have return small'); + }); + + it('empty string', () => { + expect(getEmojiSizeClass('')).to.be.equal('small', 'should have return small'); + }); + }); + + describe('with only characters not emojis of ascii/utf8', () => { + it('string of ascii only', () => { + expect( + getEmojiSizeClass('The ASCII compatible UTF-8 encoding of ISO 10646 and Unicode') + ).to.be.equal('small', 'should have return small'); + }); + + it('string of utf8 with weird chars but no', () => { + expect(getEmojiSizeClass('ASCII safety test: 1lI|, 0OD, 8B')).to.be.equal( + 'small', + 'should have return small' + ); + }); + + it('string of utf8 with weird chars', () => { + // taken from https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html + expect( + getEmojiSizeClass('ASCII safety test: 1lI|, 0OD, 8B, γιγνώσκειν, ὦ ἄνδρες დასასწრებად') + ).to.be.equal('small', 'should have return small'); + }); + + it('short string of utf8 with weird chars', () => { + // taken from https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html + expect(getEmojiSizeClass('დ')).to.be.equal('small', 'should have return small'); + }); + }); + + describe('with string containing utf8 emojis', () => { + describe('with string containing utf8 emojis and normal characters', () => { + it('one emoji after a normal sentence', () => { + expect( + getEmojiSizeClass('The SMILING FACE WITH HORNS character (😈) is assigned') + ).to.be.equal('small', 'should have return small'); + }); + + it('multiple emoji after a normal sentence', () => { + expect( + getEmojiSizeClass('The SMILING FACE WITH HORNS character (😈) is assigned 😈 😈') + ).to.be.equal('small', 'should have return small'); + }); + + it('multiple emoji before a normal sentence', () => { + expect( + getEmojiSizeClass('😈 😈The SMILING FACE WITH HORNS character () is assigned') + ).to.be.equal('small', 'should have return small'); + }); + + it('one emoji with just a space after', () => { + expect(getEmojiSizeClass('😈 ')).to.be.equal('jumbo', 'should have return jumbo'); + }); + + it('one emoji with just a space before', () => { + expect(getEmojiSizeClass(' 😈')).to.be.equal('jumbo', 'should have return jumbo'); + }); + + it('one emoji with just a space before & after', () => { + expect(getEmojiSizeClass(' 😈 ')).to.be.equal('jumbo', 'should have return jumbo'); + }); + }); + describe('with string containing only emojis ', () => { + it('one emoji without other characters', () => { + expect(getEmojiSizeClass('😈')).to.be.equal('jumbo', 'should have return jumbo'); + }); + + it('two emoji without other characters', () => { + expect(getEmojiSizeClass('😈😈')).to.be.equal('jumbo', 'should have return jumbo'); + }); + + it('3 emoji without other characters', () => { + expect(getEmojiSizeClass('😈😈😈')).to.be.equal('large', 'should have return large'); + }); + + it('4 emoji without other characters', () => { + expect(getEmojiSizeClass('😈😈😈😈')).to.be.equal('large', 'should have return large'); + }); + + it('5 emoji without other characters', () => { + expect(getEmojiSizeClass('😈😈😈😈😈')).to.be.equal('medium', 'should have return medium'); + }); + + it('6 emoji without other characters', () => { + expect(getEmojiSizeClass('😈😈😈😈😈😈')).to.be.equal( + 'medium', + 'should have return medium' + ); + }); + + it('7 emoji without other characters', () => { + expect(getEmojiSizeClass('😈😈😈😈😈😈😈')).to.be.equal( + 'small', + 'should have return small' + ); + }); + + it('lots of emojis without other characters', () => { + expect( + getEmojiSizeClass( + '😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈😈' + ) + ).to.be.equal('small', 'should have return small'); + }); + + it('lots of emojis without other characters except space', () => { + expect(getEmojiSizeClass('😈😈😈😈😈😈😈😈😈😈😈 😈😈 😈😈 😈😈 ')).to.be.equal( + 'small', + 'should have return small' + ); + }); + + it('3 emojis without other characters except space', () => { + expect(getEmojiSizeClass('😈 😈 😈 ')).to.be.equal('large', 'should have return small'); + }); + }); + }); +}); diff --git a/ts/test/session/unit/utils/Initials_test.ts b/ts/test/session/unit/utils/Initials_test.ts index 8f1d26ab5..e86cb4376 100644 --- a/ts/test/session/unit/utils/Initials_test.ts +++ b/ts/test/session/unit/utils/Initials_test.ts @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { getInitials } from '../../../../util/getInitials'; describe('getInitials', () => { - describe('empty of null string', () => { + describe('empty or null string', () => { it('initials: return undefined if string is undefined', () => { expect(getInitials(undefined)).to.be.equal('0', 'should have return 0'); }); diff --git a/ts/util/emoji.ts b/ts/util/emoji.ts index 1f171a621..90357d5e4 100644 --- a/ts/util/emoji.ts +++ b/ts/util/emoji.ts @@ -18,14 +18,15 @@ function hasNormalCharacters(str: string) { } export function getEmojiSizeClass(str: string): SizeClassType { + if (!str || !str.length) { + return 'small'; + } if (hasNormalCharacters(str)) { return 'small'; } const emojiCount = getCountOfAllMatches(str); - if (emojiCount > 8) { - return 'small'; - } else if (emojiCount > 6) { + if (emojiCount > 6) { return 'small'; } else if (emojiCount > 4) { return 'medium'; From 0cb4a13494aeedc076b6bd5b12d3a9fba5f704b4 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 28 Mar 2022 14:48:55 +1100 Subject: [PATCH 06/10] add tests for timerBucket icon this marks unit test number 302, previous: 237 --- .../session/unit/utils/TimerBucket_test.ts | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 ts/test/session/unit/utils/TimerBucket_test.ts diff --git a/ts/test/session/unit/utils/TimerBucket_test.ts b/ts/test/session/unit/utils/TimerBucket_test.ts new file mode 100644 index 000000000..43e81fd52 --- /dev/null +++ b/ts/test/session/unit/utils/TimerBucket_test.ts @@ -0,0 +1,172 @@ +import { expect } from 'chai'; +import { getIncrement, getTimerBucketIcon } from '../../../../util/timer'; + +describe('getIncrement', () => { + describe('negative length', () => { + it('length < 0', () => { + expect(getIncrement(-1)).to.be.equal(1000, 'should have return 1000'); + }); + + it('length < -1000', () => { + expect(getIncrement(-1000)).to.be.equal(1000, 'should have return 1000'); + }); + }); + + describe('positive length but less than a minute => should return 500', () => { + it('length = 60000', () => { + expect(getIncrement(60000)).to.be.equal(500, 'should have return 500'); + }); + + it('length = 10000', () => { + expect(getIncrement(10000)).to.be.equal(500, 'should have return 500'); + }); + + it('length = 0', () => { + expect(getIncrement(0)).to.be.equal(500, 'should have return 500'); + }); + }); + + describe('positive length > a minute => should return Math.ceil(length / 12) ', () => { + it('length = 2 minutes', () => { + expect(getIncrement(120000)).to.be.equal(10000, 'should have return 10000'); + }); + + it('length = 2 minutes not divisible by 12', () => { + // because we have Math.ceil() + expect(getIncrement(120001)).to.be.equal(10001, 'should have return 10000'); + }); + + it('length = 20 days', () => { + expect(getIncrement(1000 * 60 * 60 * 24 * 20)).to.be.equal( + 144000000, + 'should have return 144000000' + ); + }); + + it('length = 20 days not divisible by 12', () => { + // because we have Math.ceil() + expect(getIncrement(1000 * 60 * 60 * 24 * 20 + 1)).to.be.equal( + 144000001, + 'should have return 144000001' + ); + }); + }); +}); + +describe('getTimerBucketIcon', () => { + describe('absolute values', () => { + it('delta < 0', () => { + expect(getTimerBucketIcon(Date.now() - 1000, 100)).to.be.equal( + 'timer60', + 'should have return timer60' + ); + }); + + it('delta > length by a little', () => { + expect(getTimerBucketIcon(Date.now() + 101, 100)).to.be.equal( + 'timer00', + 'should have return timer00' + ); + }); + + it('delta > length by a lot', () => { + expect(getTimerBucketIcon(Date.now() + 10100000, 100)).to.be.equal( + 'timer00', + 'should have return timer00' + ); + }); + }); + + describe('calculated values for length 1000', () => { + const length = 1000; + it('delta = 0', () => { + expect(getTimerBucketIcon(Date.now(), length)).to.be.equal( + 'timer00', + 'should have return timer00' + ); + }); + it('delta = 1/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (1 / 12) * length, length)).to.be.equal( + 'timer05', + 'should have return timer05' + ); + }); + + it('delta = 2/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (2 / 12) * length, length)).to.be.equal( + 'timer10', + 'should have return timer10' + ); + }); + + it('delta = 3/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (3 / 12) * length, length)).to.be.equal( + 'timer15', + 'should have return timer15' + ); + }); + + it('delta = 4/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (4 / 12) * length, length)).to.be.equal( + 'timer20', + 'should have return timer20' + ); + }); + + it('delta = 5/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (5 / 12) * length, length)).to.be.equal( + 'timer25', + 'should have return timer25' + ); + }); + + it('delta = 6/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (6 / 12) * length, length)).to.be.equal( + 'timer30', + 'should have return timer30' + ); + }); + + it('delta = 7/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (7 / 12) * length, length)).to.be.equal( + 'timer35', + 'should have return timer35' + ); + }); + + it('delta = 8/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (8 / 12) * length, length)).to.be.equal( + 'timer40', + 'should have return timer40' + ); + }); + + it('delta = 9/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (9 / 12) * length, length)).to.be.equal( + 'timer45', + 'should have return timer45' + ); + }); + + it('delta = 10/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (10 / 12) * length, length)).to.be.equal( + 'timer50', + 'should have return timer50' + ); + }); + + it('delta = 11/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (11 / 12) * length, length)).to.be.equal( + 'timer55', + 'should have return timer55' + ); + }); + + it('delta = 12/12 of length', () => { + expect(getTimerBucketIcon(Date.now() + (12 / 12) * length, length)).to.be.equal( + 'timer60', + 'should have return timer60' + ); + }); + }); +}); From bfaeda5cdb673989318767b4e19bd0ce81afce9f Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 28 Mar 2022 16:22:20 +1100 Subject: [PATCH 07/10] make sure the conversation with ourself is marked approvedMe --- ts/components/leftpane/ActionsPanel.tsx | 4 ++++ ts/util/accountManager.ts | 3 +++ 2 files changed, 7 insertions(+) diff --git a/ts/components/leftpane/ActionsPanel.tsx b/ts/components/leftpane/ActionsPanel.tsx index 189f86103..ff36e2456 100644 --- a/ts/components/leftpane/ActionsPanel.tsx +++ b/ts/components/leftpane/ActionsPanel.tsx @@ -53,6 +53,7 @@ import { SessionToastContainer } from '../SessionToastContainer'; import { LeftPaneSectionContainer } from './LeftPaneSectionContainer'; import { getLatestDesktopReleaseFileToFsV2 } from '../../session/apis/file_server_api/FileServerApiV2'; import { ipcRenderer } from 'electron'; +import { UserUtils } from '../../session/utils'; const Section = (props: { type: SectionType }) => { const ourNumber = useSelector(getOurNumber); @@ -185,6 +186,9 @@ const setupTheme = () => { // Do this only if we created a new Session ID, or if we already received the initial configuration message const triggerSyncIfNeeded = async () => { + await getConversationController() + .get(UserUtils.getOurPubKeyStrFromCache()) + .setDidApproveMe(true, true); const didWeHandleAConfigurationMessageAlready = (await getItemById(hasSyncedInitialConfigurationItem))?.value || false; if (didWeHandleAConfigurationMessageAlready) { diff --git a/ts/util/accountManager.ts b/ts/util/accountManager.ts index e467ad46c..1fe59c38e 100644 --- a/ts/util/accountManager.ts +++ b/ts/util/accountManager.ts @@ -172,6 +172,9 @@ async function registrationDone(ourPubkey: string, displayName: string) { ); await conversation.setLokiProfile({ displayName }); await conversation.setIsApproved(true); + await conversation.setDidApproveMe(true); + + await conversation.commit(); const user = { ourNumber: getOurPubKeyStrFromCache(), ourPrimary: window.textsecure.storage.get('primaryDevicePubKey'), From 07197003710b5834c181cdcb980aeba2fb21c58d Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 29 Mar 2022 16:14:32 +1100 Subject: [PATCH 08/10] do not end call if connection fails, instead wait for new offer --- preload.js | 2 +- ts/session/utils/calling/CallManager.ts | 3 ++- ts/state/ducks/call.tsx | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/preload.js b/preload.js index e3f05df0f..e0c56b2d6 100644 --- a/preload.js +++ b/preload.js @@ -31,7 +31,7 @@ window.getNodeVersion = () => config.node_version; window.sessionFeatureFlags = { useOnionRequests: true, - useCallMessage: false, + useCallMessage: true, }; window.versionInfo = { diff --git a/ts/session/utils/calling/CallManager.ts b/ts/session/utils/calling/CallManager.ts index 1661d9a7a..cb407cd11 100644 --- a/ts/session/utils/calling/CallManager.ts +++ b/ts/session/utils/calling/CallManager.ts @@ -5,6 +5,7 @@ import { openConversationWithMessages } from '../../../state/ducks/conversations import { answerCall, callConnected, + callReconnecting, CallStatusEnum, endCall, incomingCall, @@ -601,7 +602,7 @@ function handleConnectionStateChanged(pubkey: string) { window.log.info('handleConnectionStateChanged :', peerConnection?.connectionState); if (peerConnection?.signalingState === 'closed' || peerConnection?.connectionState === 'failed') { - closeVideoCall(); + window.inboxStore?.dispatch(callReconnecting({ pubkey })); } else if (peerConnection?.connectionState === 'connected') { const firstAudioInput = audioInputsList?.[0].deviceId || undefined; if (firstAudioInput) { diff --git a/ts/state/ducks/call.tsx b/ts/state/ducks/call.tsx index 140469640..8e664ea7d 100644 --- a/ts/state/ducks/call.tsx +++ b/ts/state/ducks/call.tsx @@ -76,6 +76,22 @@ const callSlice = createSlice({ state.callIsInFullScreen = false; return state; }, + callReconnecting(state: CallStateType, action: PayloadAction<{ pubkey: string }>) { + const callerPubkey = action.payload.pubkey; + if (callerPubkey !== state.ongoingWith) { + window.log.info('cannot reconnect a call we did not start or receive first'); + return state; + } + const existingCallState = state.ongoingCallStatus; + + if (existingCallState !== 'ongoing') { + window.log.info('cannot reconnect a call we are not ongoing'); + return state; + } + + state.ongoingCallStatus = 'connecting'; + return state; + }, startingCallWith(state: CallStateType, action: PayloadAction<{ pubkey: string }>) { if (state.ongoingWith) { window.log.warn('cannot start a call with an ongoing call already: ongoingWith'); @@ -112,6 +128,7 @@ export const { endCall, answerCall, callConnected, + callReconnecting, startingCallWith, setFullScreenCall, } = actions; From fe5753179752b6e32b02a137c967c41059aee5c3 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 1 Apr 2022 15:46:27 +1100 Subject: [PATCH 09/10] Sesion 1.8.4 with calls enabled --- package.json | 2 +- preload.js | 1 - .../conversation/ConversationHeader.tsx | 8 +------ .../settings/section/CategoryPrivacy.tsx | 21 +++++++++---------- ts/receiver/contentMessage.ts | 2 +- ts/window.d.ts | 1 - 6 files changed, 13 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index b623cdb6d..d5b7c05cf 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "session-desktop", "productName": "Session", "description": "Private messaging from your desktop", - "version": "1.7.9", + "version": "1.8.4", "license": "GPL-3.0", "author": { "name": "Oxen Labs", diff --git a/preload.js b/preload.js index e3f05df0f..7f2148e5b 100644 --- a/preload.js +++ b/preload.js @@ -31,7 +31,6 @@ window.getNodeVersion = () => config.node_version; window.sessionFeatureFlags = { useOnionRequests: true, - useCallMessage: false, }; window.versionInfo = { diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index c8ae281d6..a8aa78d7a 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -210,13 +210,7 @@ const CallButton = () => { const hasOngoingCall = useSelector(getHasOngoingCall); const canCall = !(hasIncomingCall || hasOngoingCall); - if ( - !isPrivate || - isMe || - !selectedConvoKey || - isBlocked || - !window.sessionFeatureFlags.useCallMessage - ) { + if (!isPrivate || isMe || !selectedConvoKey || isBlocked) { return null; } diff --git a/ts/components/settings/section/CategoryPrivacy.tsx b/ts/components/settings/section/CategoryPrivacy.tsx index 59288bada..a91e6a4a4 100644 --- a/ts/components/settings/section/CategoryPrivacy.tsx +++ b/ts/components/settings/section/CategoryPrivacy.tsx @@ -69,17 +69,16 @@ export const SettingsCategoryPrivacy = (props: { description={window.i18n('mediaPermissionsDescription')} active={Boolean(window.getSettingValue('media-permissions'))} /> - {window.sessionFeatureFlags.useCallMessage && ( - { - await toggleCallMediaPermissions(forceUpdate); - forceUpdate(); - }} - title={window.i18n('callMediaPermissionsTitle')} - description={window.i18n('callMediaPermissionsDescription')} - active={Boolean(window.getCallMediaPermissions())} - /> - )} + { + await toggleCallMediaPermissions(forceUpdate); + forceUpdate(); + }} + title={window.i18n('callMediaPermissionsTitle')} + description={window.i18n('callMediaPermissionsDescription')} + active={Boolean(window.getCallMediaPermissions())} + /> + { const old = Boolean(window.getSettingValue(SettingsKey.settingsReadReceipt)); diff --git a/ts/receiver/contentMessage.ts b/ts/receiver/contentMessage.ts index d23fc9fb1..cb036428a 100644 --- a/ts/receiver/contentMessage.ts +++ b/ts/receiver/contentMessage.ts @@ -425,7 +425,7 @@ export async function innerHandleSwarmContentMessage( if (content.unsendMessage) { await handleUnsendMessage(envelope, content.unsendMessage as SignalService.Unsend); } - if (content.callMessage && window.sessionFeatureFlags?.useCallMessage) { + if (content.callMessage) { await handleCallMessage(envelope, content.callMessage as SignalService.CallMessage); } if (content.messageRequestResponse) { diff --git a/ts/window.d.ts b/ts/window.d.ts index bfb8aa8c7..c16fc0751 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -38,7 +38,6 @@ declare global { log: any; sessionFeatureFlags: { useOnionRequests: boolean; - useCallMessage: boolean; }; SessionSnodeAPI: SessionSnodeAPI; onLogin: any; From 332d58027ff2dbd06247cec46ac07b9b0e4431de Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 6 Apr 2022 13:41:04 +1000 Subject: [PATCH 10/10] show draggable call container when opening settings from call convo --- .../calling/DraggableCallContainer.tsx | 10 ++++- ts/session/utils/TypedEmitter.ts | 45 ------------------- ts/session/utils/index.ts | 1 - 3 files changed, 9 insertions(+), 47 deletions(-) delete mode 100644 ts/session/utils/TypedEmitter.ts diff --git a/ts/components/calling/DraggableCallContainer.tsx b/ts/components/calling/DraggableCallContainer.tsx index b4ed84637..3de4ed894 100644 --- a/ts/components/calling/DraggableCallContainer.tsx +++ b/ts/components/calling/DraggableCallContainer.tsx @@ -9,6 +9,8 @@ import { openConversationWithMessages } from '../../state/ducks/conversations'; import { Avatar, AvatarSize } from '../avatar/Avatar'; import { useVideoCallEventsListener } from '../../hooks/useVideoEventListener'; import { VideoLoadingSpinner } from './InConversationCallContainer'; +import { getSection } from '../../state/selectors/section'; +import { SectionType } from '../../state/ducks/section'; export const DraggableCallWindow = styled.div` position: absolute; @@ -57,6 +59,7 @@ export const DraggableCallContainer = () => { const ongoingCallProps = useSelector(getHasOngoingCallWith); const selectedConversationKey = useSelector(getSelectedConversationKey); const hasOngoingCall = useSelector(getHasOngoingCall); + const selectedSection = useSelector(getSection); // the draggable container has a width of 12vw, so we just set it's X to a bit more than this const [positionX, setPositionX] = useState(window.innerWidth - (window.innerWidth * 1) / 6); @@ -99,7 +102,12 @@ export const DraggableCallContainer = () => { } }; - if (!hasOngoingCall || !ongoingCallProps || ongoingCallPubkey === selectedConversationKey) { + if ( + !hasOngoingCall || + !ongoingCallProps || + (ongoingCallPubkey === selectedConversationKey && + selectedSection.focusedSection !== SectionType.Settings) + ) { return null; } diff --git a/ts/session/utils/TypedEmitter.ts b/ts/session/utils/TypedEmitter.ts deleted file mode 100644 index a834ff6c6..000000000 --- a/ts/session/utils/TypedEmitter.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Code from https://github.com/andywer/typed-emitter - -type Arguments = [T] extends [(...args: infer U) => any] ? U : [T] extends [void] ? [] : [T]; - -/** - * Type-safe event emitter. - * - * Use it like this: - * - * interface MyEvents { - * error: (error: Error) => void - * message: (from: string, content: string) => void - * } - * - * const myEmitter = new EventEmitter() as TypedEmitter - * - * myEmitter.on("message", (from, content) => { - * // ... - * }) - * - * myEmitter.emit("error", "x") // <- Will catch this type error - * - * or - * - * class MyEmitter extends EventEmitter implements TypedEventEmitter - */ -export interface TypedEventEmitter { - addListener(event: E, listener: Events[E]): this; - on(event: E, listener: Events[E]): this; - once(event: E, listener: Events[E]): this; - prependListener(event: E, listener: Events[E]): this; - prependOnceListener(event: E, listener: Events[E]): this; - - off(event: E, listener: Events[E]): this; - removeAllListeners(event?: E): this; - removeListener(event: E, listener: Events[E]): this; - - emit(event: E, ...args: Arguments): boolean; - eventNames(): Array; - listeners(event: E): Array; - listenerCount(event: E): number; - - getMaxListeners(): number; - setMaxListeners(maxListeners: number): this; -} diff --git a/ts/session/utils/index.ts b/ts/session/utils/index.ts index 9ec887fa4..c3e942bca 100644 --- a/ts/session/utils/index.ts +++ b/ts/session/utils/index.ts @@ -10,7 +10,6 @@ import * as AttachmentDownloads from './AttachmentsDownload'; import * as CallManager from './calling/CallManager'; export * from './Attachments'; -export * from './TypedEmitter'; export * from './JobQueue'; export {