From bcad497c7fbb4dba48a65b34b6051e95038f47a0 Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 7 Jul 2020 13:20:26 +1000 Subject: [PATCH 01/17] regex-for-http --- js/modules/loki_public_chat_api.js | 2 ++ ts/session/types/OpenGroup.ts | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/js/modules/loki_public_chat_api.js b/js/modules/loki_public_chat_api.js index e37c01f3c..bbe64b8f6 100644 --- a/js/modules/loki_public_chat_api.js +++ b/js/modules/loki_public_chat_api.js @@ -7,6 +7,8 @@ const nodeFetch = require('node-fetch'); const validOpenGroupServer = async serverUrl => { // test to make sure it's online (and maybe has a valid SSL cert) try { + console.log('[vince] loki_public_chat_api --> serverUrl:', serverUrl); + const url = new URL(serverUrl); if (window.lokiFeatureFlags.useFileOnionRequests) { diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index 01c148515..b823b55f3 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -7,8 +7,10 @@ interface OpenGroupParams { } export class OpenGroup { + // Matches prefixes https:// http:// plus no prefix. + // Servers without prefix default to https:// private static readonly serverRegex = new RegExp( - '^([\\w-]{2,}.){1,2}[\\w-]{2,}$' + '^(https?:\\/\\/){0,1}([\\w-]{2,}.){1,2}[\\w-]{2,}$' ); private static readonly groupIdRegex = new RegExp( '^publicChat:[0-9]*@([\\w-]{2,}.){1,2}[\\w-]{2,}$' @@ -17,10 +19,22 @@ export class OpenGroup { public readonly channel: number; public readonly groupId?: string; public readonly conversationId: string; + private readonly rawServer: string; constructor(params: OpenGroupParams) { - const strippedServer = params.server.replace('https://', ''); - this.server = strippedServer; + // https will be prepended unless explicitly http + const prefixRegex = new RegExp('https:\//\//'); + this.rawServer = params.server.replace(prefixRegex, ''); + + const isHttps = Boolean(params.server.match('^(https:\/\/){1}')); + const isHttp = Boolean(params.server.match('^(http:\/\/){1}')); + const hasNoPrefix = !(params.server.match(prefixRegex)); + + console.log('[vince] isHttps:', isHttps); + console.log('[vince] isHttp:', isHttp); + console.log('[vince] hasNoPrefix:', hasNoPrefix); + + this.server = params.server; // Validate server format const isValid = OpenGroup.serverRegex.test(this.server); @@ -28,6 +42,8 @@ export class OpenGroup { throw Error('an invalid server or groupId was provided'); } + console.log('[vince] OpenGroup --> constructor:', this.server); + this.channel = params.channel; this.conversationId = params.conversationId; this.groupId = OpenGroup.getGroupId(this.server, this.channel); From ea23df29862f3a19cec91ccd7290190cda9d1865 Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 7 Jul 2020 14:21:11 +1000 Subject: [PATCH 02/17] OpenGroup strict SSL verification --- ts/session/types/OpenGroup.ts | 51 ++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index b823b55f3..000772a75 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -19,22 +19,10 @@ export class OpenGroup { public readonly channel: number; public readonly groupId?: string; public readonly conversationId: string; - private readonly rawServer: string; constructor(params: OpenGroupParams) { // https will be prepended unless explicitly http - const prefixRegex = new RegExp('https:\//\//'); - this.rawServer = params.server.replace(prefixRegex, ''); - - const isHttps = Boolean(params.server.match('^(https:\/\/){1}')); - const isHttp = Boolean(params.server.match('^(http:\/\/){1}')); - const hasNoPrefix = !(params.server.match(prefixRegex)); - - console.log('[vince] isHttps:', isHttps); - console.log('[vince] isHttp:', isHttp); - console.log('[vince] hasNoPrefix:', hasNoPrefix); - - this.server = params.server; + this.server = OpenGroup.prefixify(params.server); // Validate server format const isValid = OpenGroup.serverRegex.test(this.server); @@ -42,8 +30,6 @@ export class OpenGroup { throw Error('an invalid server or groupId was provided'); } - console.log('[vince] OpenGroup --> constructor:', this.server); - this.channel = params.channel; this.conversationId = params.conversationId; this.groupId = OpenGroup.getGroupId(this.server, this.channel); @@ -51,12 +37,13 @@ export class OpenGroup { public static from( groupId: string, - conversationId: string + conversationId: string, + hasSSL: boolean = true ): OpenGroup | undefined { // Returns a new instance from a groupId if it's valid // eg. groupId = 'publicChat:1@chat.getsession.org' - const server = this.getServer(groupId); + const server = this.getServer(groupId, hasSSL); const channel = this.getChannel(groupId); // Was groupId successfully utilized? @@ -71,17 +58,22 @@ export class OpenGroup { conversationId, } as OpenGroupParams; - if (this.serverRegex.test(server)) { + const isValid = OpenGroup.serverRegex.test(server); + if (!isValid) { return; } return new OpenGroup(openGroupParams); } - private static getServer(groupId: string): string | undefined { + private static getServer(groupId: string, hasSSL: boolean): string | undefined { const isValid = this.groupIdRegex.test(groupId); + const strippedServer = isValid ? groupId.split('@')[1] : undefined; - return isValid ? groupId.split('@')[1] : undefined; + // We don't know for sure if the server is https or http when taken from the groupId. Preifx accordingly. + return strippedServer + ? this.prefixify(strippedServer, hasSSL) + : undefined; } private static getChannel(groupId: string): number | undefined { @@ -92,7 +84,22 @@ export class OpenGroup { } private static getGroupId(server: string, channel: number): string { - // server is already validated in constructor; no need to re-check - return `publicChat:${channel}@${server}`; + // Server is already validated in constructor; no need to re-check + + // Strip server prefix + const prefixRegex = new RegExp('https?:\\/\\/'); + const strippedServer = server.replace(prefixRegex, ''); + + return `publicChat:${channel}@${strippedServer}`; + } + + private static prefixify(server: string, hasSSL: boolean = true): string { + // Prefix server with https:// if it's not already prefixed with http or https. + const hasPrefix = server.match('^https?:\/\/'); + if (hasPrefix) { + return server; + } + + return `http${hasSSL ? 's' : ''}://${server}`; } } From 8484b9d3f6f98315f7f7ef5174544a48ab9575c2 Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 7 Jul 2020 14:30:49 +1000 Subject: [PATCH 03/17] regex-fiddling --- ts/session/types/OpenGroup.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index 000772a75..bfc79b96f 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -22,7 +22,7 @@ export class OpenGroup { constructor(params: OpenGroupParams) { // https will be prepended unless explicitly http - this.server = OpenGroup.prefixify(params.server); + this.server = OpenGroup.prefixify(params.server.toLowerCase()); // Validate server format const isValid = OpenGroup.serverRegex.test(this.server); @@ -72,7 +72,7 @@ export class OpenGroup { // We don't know for sure if the server is https or http when taken from the groupId. Preifx accordingly. return strippedServer - ? this.prefixify(strippedServer, hasSSL) + ? this.prefixify(strippedServer.toLowerCase(), hasSSL) : undefined; } From 39b41cb222e30258a17af233260f34973e3c0c8c Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 7 Jul 2020 17:46:50 +1000 Subject: [PATCH 04/17] joining-open-groups --- js/background.js | 10 ++--- .../session/LeftPaneMessageSection.tsx | 21 ++++++---- ts/session/types/OpenGroup.ts | 40 ++++++++++++++++++- ts/window.d.ts | 5 ++- 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/js/background.js b/js/background.js index 89f5d242f..d663c596c 100644 --- a/js/background.js +++ b/js/background.js @@ -1090,7 +1090,7 @@ window.setMediaPermissions(!mediaPermissions); }; - // attempts a connection to an open group server + // Attempts a connection to an open group server window.attemptConnection = async (serverURL, channelId) => { let rawserverURL = serverURL .replace(/^https?:\/\//i, '') @@ -1099,7 +1099,7 @@ const sslServerURL = `https://${rawserverURL}`; const conversationId = `publicChat:${channelId}@${rawserverURL}`; - // quickly peak to make sure we don't already have it + // Quickly peak to make sure we don't already have it const conversationExists = window.ConversationController.get( conversationId ); @@ -1110,7 +1110,7 @@ }); } - // get server + // Get server const serverAPI = await window.lokiPublicChatAPI.findOrCreateServer( sslServerURL ); @@ -1122,13 +1122,13 @@ }); } - // create conversation + // Create conversation const conversation = await window.ConversationController.getOrCreateAndWait( conversationId, 'group' ); - // convert conversation to a public one + // Convert conversation to a public one await conversation.setPublicSource(sslServerURL, channelId); // and finally activate it diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 5bf50a38f..951f0d487 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -28,6 +28,7 @@ import { SessionButtonColor, SessionButtonType, } from './SessionButton'; +import { OpenGroup } from '../../session/types'; export interface Props { searchTerm: string; @@ -438,27 +439,24 @@ export class LeftPaneMessageSection extends React.Component { } } - private handleJoinChannelButtonClick(groupUrl: string) { + private async handleJoinChannelButtonClick(server: string) { const { loading } = this.state; if (loading) { return false; } - // longest TLD is now (20/02/06) 24 characters per https://jasontucker.blog/8945/what-is-the-longest-tld-you-can-get-for-a-domain-name - const regexURL = /(http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,24}(:[0-9]{1,5})?(\/.*)?/; - - if (groupUrl.length <= 0) { + if (!OpenGroup.validate(server)) { window.pushToast({ title: window.i18n('noServerURL'), type: 'error', id: 'connectToServerFail', }); - return false; - } + await OpenGroup.join(server); + - if (!regexURL.test(groupUrl)) { + if (groupUrl.length <= 0) { window.pushToast({ title: window.i18n('noServerURL'), type: 'error', @@ -468,6 +466,13 @@ export class LeftPaneMessageSection extends React.Component { return false; } + + + return false; + } + + + MainViewController.joinChannelStateManager(this, groupUrl, () => { this.handleToggleOverlay(undefined); }); diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index bfc79b96f..11a8edf15 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -1,5 +1,9 @@ // This is the Open Group equivalent to the PubKey type. +import LokiPublicChatFactoryAPI from "../../../js/modules/loki_public_chat_api"; +import { UserUtil } from "../../util"; +import { ConversationType } from "../../receiver/common"; + interface OpenGroupParams { server: string; channel: number; @@ -18,7 +22,11 @@ export class OpenGroup { public readonly server: string; public readonly channel: number; public readonly groupId?: string; - public readonly conversationId: string; + public readonly conversationId: string; // eg. c12 + + // The following are set on join() - not required + public connected?: boolean; + public conversation?: ConversationType; constructor(params: OpenGroupParams) { // https will be prepended unless explicitly http @@ -35,6 +43,14 @@ export class OpenGroup { this.groupId = OpenGroup.getGroupId(this.server, this.channel); } + public static validate(serverUrl: string): boolean { + if (this.serverRegex.test(serverUrl)) { + return true; + } + + return false; + } + public static from( groupId: string, conversationId: string, @@ -66,6 +82,26 @@ export class OpenGroup { return new OpenGroup(openGroupParams); } + public static async join(server: string): Promise { + if (!OpenGroup.validate(server)) { + return; + } + + // Make this not hard coded + const channel = 1; + const conversation = window.attemptConnection(server, channel); + const groupId = OpenGroup.getGroupId(server, channel); + + return new OpenGroup({ + server, + groupId, + conversation.cid, + }) + + return {serverInfo, connectionPromise}; + + } + private static getServer(groupId: string, hasSSL: boolean): string | undefined { const isValid = this.groupIdRegex.test(groupId); const strippedServer = isValid ? groupId.split('@')[1] : undefined; @@ -102,4 +138,6 @@ export class OpenGroup { return `http${hasSSL ? 's' : ''}://${server}`; } + + } diff --git a/ts/window.d.ts b/ts/window.d.ts index bd50f7e5d..5ea680b4c 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -7,12 +7,15 @@ import { LokiPublicChatFactoryInterface } from '../js/modules/loki_public_chat_a import { LokiAppDotNetServerInterface } from '../js/modules/loki_app_dot_net_api'; import { LokiMessageInterface } from '../js/modules/loki_message_api'; import { SwarmPolling } from './session/snode_api/swarmPolling'; + import { LibTextsecure } from '../libtextsecure'; +import { ConversationType } from '../js/modules/data'; /* We declare window stuff here instead of global.d.ts because we are importing other declarations. If you import anything in global.d.ts, the type system won't work correctly. */ + declare global { interface Window { CONSTANTS: any; @@ -33,7 +36,7 @@ declare global { StubMessageAPI: any; WebAPI: any; Whisper: any; - attemptConnection: any; + attemptConnection: ConversationType; clearLocalData: any; clipboard: any; confirmationDialog: any; From 54209e98a82cb27e66dfe57833d16f8f65100cfe Mon Sep 17 00:00:00 2001 From: Vincent Date: Tue, 7 Jul 2020 18:19:59 +1000 Subject: [PATCH 05/17] propogate join channel erros --- ts/session/types/OpenGroup.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index 11a8edf15..a5990efad 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -89,17 +89,27 @@ export class OpenGroup { // Make this not hard coded const channel = 1; - const conversation = window.attemptConnection(server, channel); - const groupId = OpenGroup.getGroupId(server, channel); + let conversation; + try { + conversation = await window.attemptConnection(server, channel); + } catch (e) { + console.warn(e); + return; + } return new OpenGroup({ server, - groupId, - conversation.cid, - }) - - return {serverInfo, connectionPromise}; + channel, + conversationId: conversation?.cid, + }); + } + + public static async isConnected(server: string): Promise { + if (!OpenGroup.validate(server)) { + return false; + } + return Boolean(window.lokiPublicChatAPI.findOrCreateServer(server)); } private static getServer(groupId: string, hasSSL: boolean): string | undefined { From ab966e600abd68e8f18743383e5c4cf35e1d53cf Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 10:19:51 +1000 Subject: [PATCH 06/17] OpenGrop joining --- ts/session/types/OpenGroup.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index a5990efad..15d836acf 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -83,6 +83,7 @@ export class OpenGroup { } public static async join(server: string): Promise { + const prefixedServer = OpenGroup.prefixify(server); if (!OpenGroup.validate(server)) { return; } @@ -90,17 +91,20 @@ export class OpenGroup { // Make this not hard coded const channel = 1; let conversation; + let conversationId; try { - conversation = await window.attemptConnection(server, channel); + conversation = await window.attemptConnection(prefixedServer, channel); + conversationId = conversation?.cid; } catch (e) { console.warn(e); return; } + // Do we want to add conversation as a property of OpenGroup? return new OpenGroup({ server, channel, - conversationId: conversation?.cid, + conversationId, }); } From f6ae5386e91e216f679e969f3ee28e3ef9cd3362 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 10:59:20 +1000 Subject: [PATCH 07/17] getting opengroup conversation --- _locales/en/messages.json | 6 +- js/modules/loki_public_chat_api.js | 2 - .../session/LeftPaneMessageSection.tsx | 60 +++++++++++++++---- ts/session/types/OpenGroup.ts | 42 +++++++++---- 4 files changed, 82 insertions(+), 28 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 522bc76f4..3f2e59ed6 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -2180,7 +2180,7 @@ "message": "You have removed your password." }, "publicChatExists": { - "message": "You are already connected to this public channel" + "message": "You are already connected to this open group" }, "connectToServerFail": { "message": "Failed to connect to server. Check URL" @@ -2239,6 +2239,10 @@ "message": "Invalid Pubkey Format", "description": "Error string shown when user types an invalid pubkey format" }, + "attemptedConnectionTimeout": { + "message": "Connection to open group timed out", + "description": "Shown in toast when attempted connection to OpenGroup times out" + }, "lnsMappingNotFound": { "message": "There is no LNS mapping associated with this name", "description": "Shown in toast if user enters an unknown LNS name" diff --git a/js/modules/loki_public_chat_api.js b/js/modules/loki_public_chat_api.js index bbe64b8f6..e37c01f3c 100644 --- a/js/modules/loki_public_chat_api.js +++ b/js/modules/loki_public_chat_api.js @@ -7,8 +7,6 @@ const nodeFetch = require('node-fetch'); const validOpenGroupServer = async serverUrl => { // test to make sure it's online (and maybe has a valid SSL cert) try { - console.log('[vince] loki_public_chat_api --> serverUrl:', serverUrl); - const url = new URL(serverUrl); if (window.lokiFeatureFlags.useFileOnionRequests) { diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 951f0d487..4c2d690ee 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -439,45 +439,79 @@ export class LeftPaneMessageSection extends React.Component { } } - private async handleJoinChannelButtonClick(server: string) { + private async handleJoinChannelButtonClick(serverUrl: string) { const { loading } = this.state; if (loading) { - return false; + return; } - if (!OpenGroup.validate(server)) { + // Server URL entered? + if (serverUrl.length === 0) { window.pushToast({ title: window.i18n('noServerURL'), type: 'error', id: 'connectToServerFail', }); - await OpenGroup.join(server); - + return; + } - if (groupUrl.length <= 0) { + // Server URL valid? + if (!OpenGroup.validate(serverUrl)) { window.pushToast({ title: window.i18n('noServerURL'), - type: 'error', id: 'connectToServerFail', + type: 'error', }); - return false; + return; } + // Already connected? + if (Boolean(await OpenGroup.getConversation(serverUrl))) { + window.pushToast({ + title: window.i18n('publicChatExists'), + id: 'publicChatExists', + type: 'error', + }); - return false; + return; } - + const successPromise = OpenGroup.join(serverUrl); + const timeoutPromise = new Promise((_resolve, reject) => + // tslint:disable-next-line: no-unnecessary-callback-wrapper no-void-expression + setTimeout(() => reject(), window.CONSTANTS.MAX_CONNECTION_DURATION) + ); - MainViewController.joinChannelStateManager(this, groupUrl, () => { + // Connect to server with timeout. + this.setState({ loading: true }); + await Promise.race([successPromise, timeoutPromise]).then(() => { this.handleToggleOverlay(undefined); - }); + this.setState({ + loading: false, + connectSuccess: true, + }); - return true; + window.pushToast({ + title: window.i18n('connectToServerSuccess'), + id: 'connectToServerSuccess', + type: 'success', + }); + }).catch(() => { + this.setState({ + connectSuccess: false, + loading: false, + }); + + window.pushToast({ + title: window.i18n('attemptedConnectionTimeout'), + id: 'attemptedConnectionTimeout', + type: 'error', + }); + }); } private async onCreateClosedGroup( diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index 15d836acf..4b679bf18 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -1,8 +1,6 @@ // This is the Open Group equivalent to the PubKey type. -import LokiPublicChatFactoryAPI from "../../../js/modules/loki_public_chat_api"; -import { UserUtil } from "../../util"; -import { ConversationType } from "../../receiver/common"; +import { LokiPublicChatFactoryInterface } from "../../../js/modules/loki_public_chat_api"; interface OpenGroupParams { server: string; @@ -24,10 +22,6 @@ export class OpenGroup { public readonly groupId?: string; public readonly conversationId: string; // eg. c12 - // The following are set on join() - not required - public connected?: boolean; - public conversation?: ConversationType; - constructor(params: OpenGroupParams) { // https will be prepended unless explicitly http this.server = OpenGroup.prefixify(params.server.toLowerCase()); @@ -92,6 +86,20 @@ export class OpenGroup { const channel = 1; let conversation; let conversationId; + + // Return OpenGroup if we're already connected + conversation = await OpenGroup.getConversation(prefixedServer); + if (conversation) { + conversationId = conversation?.cid; + if (conversationId) { + return new OpenGroup({ + server: prefixedServer, + channel: 1, + conversationId, + }); + } + } + try { conversation = await window.attemptConnection(prefixedServer, channel); conversationId = conversation?.cid; @@ -108,12 +116,24 @@ export class OpenGroup { }); } - public static async isConnected(server: string): Promise { + public static async getConversation(server: string): Promise { if (!OpenGroup.validate(server)) { - return false; + return; + } + + const prefixedServer = this.prefixify(server); + const serverInfo = await window.lokiPublicChatAPI.findOrCreateServer(prefixedServer) as any; + + if (!serverInfo?.channels?.length) { + return; } - return Boolean(window.lokiPublicChatAPI.findOrCreateServer(server)); + return serverInfo.channels[0].conversation; + } + + public static getConversationByCID(conversationId: string): any { + const { ConversationController } = window; + return ConversationController.get(conversationId); } private static getServer(groupId: string, hasSSL: boolean): string | undefined { @@ -152,6 +172,4 @@ export class OpenGroup { return `http${hasSSL ? 's' : ''}://${server}`; } - - } From 6111cb2d17da53e8e5f28eae26ea7757702114ca Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 11:10:22 +1000 Subject: [PATCH 08/17] server-regex-tighten --- ts/components/session/LeftPaneMessageSection.tsx | 6 ------ ts/session/types/OpenGroup.ts | 10 ++-------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 4c2d690ee..4a3d3ade3 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -448,12 +448,6 @@ export class LeftPaneMessageSection extends React.Component { // Server URL entered? if (serverUrl.length === 0) { - window.pushToast({ - title: window.i18n('noServerURL'), - type: 'error', - id: 'connectToServerFail', - }); - return; } diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index 4b679bf18..45c4f31d1 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -1,7 +1,5 @@ // This is the Open Group equivalent to the PubKey type. -import { LokiPublicChatFactoryInterface } from "../../../js/modules/loki_public_chat_api"; - interface OpenGroupParams { server: string; channel: number; @@ -12,7 +10,7 @@ export class OpenGroup { // Matches prefixes https:// http:// plus no prefix. // Servers without prefix default to https:// private static readonly serverRegex = new RegExp( - '^(https?:\\/\\/){0,1}([\\w-]{2,}.){1,2}[\\w-]{2,}$' + '^((https?:\\/\\/){0,1})([\\w-]{2,}\\.){1,2}[\\w-]{2,}$' ); private static readonly groupIdRegex = new RegExp( '^publicChat:[0-9]*@([\\w-]{2,}.){1,2}[\\w-]{2,}$' @@ -38,11 +36,7 @@ export class OpenGroup { } public static validate(serverUrl: string): boolean { - if (this.serverRegex.test(serverUrl)) { - return true; - } - - return false; + return this.serverRegex.test(serverUrl); } public static from( From a005aa8a2e540e9f98428be41230840a9342c54f Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 11:41:30 +1000 Subject: [PATCH 09/17] onLoading join --- .../session/LeftPaneMessageSection.tsx | 18 +++++++----------- ts/session/types/OpenGroup.ts | 12 +++++++++--- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 4a3d3ade3..d32f819e1 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -462,7 +462,6 @@ export class LeftPaneMessageSection extends React.Component { return; } - // Already connected? if (Boolean(await OpenGroup.getConversation(serverUrl))) { window.pushToast({ @@ -474,15 +473,12 @@ export class LeftPaneMessageSection extends React.Component { return; } - const successPromise = OpenGroup.join(serverUrl); - const timeoutPromise = new Promise((_resolve, reject) => - // tslint:disable-next-line: no-unnecessary-callback-wrapper no-void-expression - setTimeout(() => reject(), window.CONSTANTS.MAX_CONNECTION_DURATION) - ); + // Connect to server + const successPromise = OpenGroup.join(serverUrl, () => { + this.setState({ loading: true }); + }); - // Connect to server with timeout. - this.setState({ loading: true }); - await Promise.race([successPromise, timeoutPromise]).then(() => { + successPromise.then(() => { this.handleToggleOverlay(undefined); this.setState({ loading: false, @@ -501,8 +497,8 @@ export class LeftPaneMessageSection extends React.Component { }); window.pushToast({ - title: window.i18n('attemptedConnectionTimeout'), - id: 'attemptedConnectionTimeout', + title: window.i18n('connectToServerFail'), + id: 'connectToServerFail', type: 'error', }); }); diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index 45c4f31d1..44a6cb661 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -70,7 +70,9 @@ export class OpenGroup { return new OpenGroup(openGroupParams); } - public static async join(server: string): Promise { + public static async join(server: string, onLoading?: any): Promise { + // onLoading called when the server begins connecting - after passing every guard + const prefixedServer = OpenGroup.prefixify(server); if (!OpenGroup.validate(server)) { return; @@ -94,12 +96,16 @@ export class OpenGroup { } } + // Try to connect to server try { + if (onLoading) { + onLoading(); + } + conversation = await window.attemptConnection(prefixedServer, channel); conversationId = conversation?.cid; } catch (e) { - console.warn(e); - return; + throw new Error(e); } // Do we want to add conversation as a property of OpenGroup? From e72970fdc7e8a04e7ecc896b713310dabac0efbf Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 12:14:46 +1000 Subject: [PATCH 10/17] load-conv-public --- _locales/en/messages.json | 5 +- .../session/LeftPaneMessageSection.tsx | 47 ++++++++++++++----- ts/session/types/OpenGroup.ts | 9 ++++ ts/state/selectors/conversations.ts | 6 +++ 4 files changed, 54 insertions(+), 13 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 36fc29438..bc1dfbbbc 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -2194,8 +2194,11 @@ "connectToServerFail": { "message": "Failed to connect to server. Check URL" }, + "connectingToServer": { + "message": "Connecting to server..." + }, "connectToServerSuccess": { - "message": "Successfully connected to new open group server" + "message": "Successfully connected to open group" }, "setPasswordFail": { "message": "Failed to set password" diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index d32f819e1..8a27576a7 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -59,7 +59,6 @@ interface State { loading: boolean; overlay: false | SessionComposeToType; valuePasted: string; - connectSuccess: boolean; } export class LeftPaneMessageSection extends React.Component { @@ -73,7 +72,6 @@ export class LeftPaneMessageSection extends React.Component { loading: false, overlay: false, valuePasted: '', - connectSuccess: false, }; const conversations = this.getCurrentConversations(); @@ -474,25 +472,50 @@ export class LeftPaneMessageSection extends React.Component { } // Connect to server - const successPromise = OpenGroup.join(serverUrl, () => { - this.setState({ loading: true }); + const successPromise = OpenGroup.join(serverUrl, async () => { + if (await OpenGroup.serverExists(serverUrl)) { + window.pushToast({ + title: window.i18n('connectingToServer'), + id: 'connectToServerSuccess', + type: 'success', + }); + + this.setState({loading: true}); + + // Call conversationRenderedResolve when the conversation is rendered. + let conversationRenderedResolve: any; + const conversationRenderedPromise = async () => + // tslint:disable-next-line: promise-must-complete + new Promise(resolve => { + conversationRenderedResolve = resolve; + }); + + // STOP IT AT THE SOURCE; SELECTORS/CONVERSATIONS.TS + const isRenderedInterval = setInterval(async () => { + // The conversation is shown in LeftPane when lastMessage is set. + // Notify the user when we're ready to render in LeftPane. + if ((await OpenGroup.getConversation(serverUrl))?.lastMessage) { + conversationRenderedResolve(); + window.pushToast({ + title: window.i18n('connectToServerFail'), + id: 'connectToServerFail', + type: 'error', + }); + } + }, 100); + + + + } }); successPromise.then(() => { this.handleToggleOverlay(undefined); this.setState({ loading: false, - connectSuccess: true, - }); - - window.pushToast({ - title: window.i18n('connectToServerSuccess'), - id: 'connectToServerSuccess', - type: 'success', }); }).catch(() => { this.setState({ - connectSuccess: false, loading: false, }); diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index 44a6cb661..f70ffb934 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -131,6 +131,15 @@ export class OpenGroup { return serverInfo.channels[0].conversation; } + public static async serverExists(server: string): Promise { + if (!OpenGroup.validate(server)) { + return false; + } + + const prefixedServer = this.prefixify(server); + return Boolean(await window.lokiPublicChatAPI.findOrCreateServer(prefixedServer)); + } + public static getConversationByCID(conversationId: string): any { const { ConversationController } = window; return ConversationController.get(conversationId); diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 5b896ad6c..9ba5559c9 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -120,6 +120,12 @@ export const _getLeftPaneLists = ( }; } + const isPublic = conversation.isPublic; + console.log('[vince] isPublic:', isPublic); + console.log('[vince] isPublic:', isPublic); + console.log('[vince] isPublic:', isPublic); + console.log('[vince] isPublic:', isPublic); + // Remove all invalid conversations and conversatons of devices associated with cancelled attempted links if (!conversation.timestamp) { continue; From 3bc7d95d4749398317260a320343661761f33a98 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 14:29:22 +1000 Subject: [PATCH 11/17] closed-group-contacts-redux --- ts/components/LeftPane.tsx | 1 + .../session/LeftPaneContactSection.tsx | 1 - .../session/LeftPaneMessageSection.tsx | 28 ++----------------- .../session/SessionClosableOverlay.tsx | 20 ++++--------- ts/state/selectors/conversations.ts | 24 ++++++++++------ 5 files changed, 25 insertions(+), 49 deletions(-) diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index d567c23ab..a4c4c600c 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -122,6 +122,7 @@ export class LeftPane extends React.Component { return ( ; contacts: Array; - searchResults?: SearchResultsProps; updateSearchTerm: (searchTerm: string) => void; diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 8a27576a7..77a26f2dc 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -7,6 +7,7 @@ import { ConversationListItem, PropsData as ConversationListItemPropsType, } from '../ConversationListItem'; +import { ConversationType } from '../../state/ducks/conversations'; import { PropsData as SearchResultsProps, SearchResults, @@ -34,6 +35,7 @@ export interface Props { searchTerm: string; isSecondaryDevice: boolean; + contacts: Array; conversations?: Array; searchResults?: SearchResultsProps; @@ -313,6 +315,7 @@ export class LeftPaneMessageSection extends React.Component { const closedGroupElement = ( { @@ -481,31 +484,6 @@ export class LeftPaneMessageSection extends React.Component { }); this.setState({loading: true}); - - // Call conversationRenderedResolve when the conversation is rendered. - let conversationRenderedResolve: any; - const conversationRenderedPromise = async () => - // tslint:disable-next-line: promise-must-complete - new Promise(resolve => { - conversationRenderedResolve = resolve; - }); - - // STOP IT AT THE SOURCE; SELECTORS/CONVERSATIONS.TS - const isRenderedInterval = setInterval(async () => { - // The conversation is shown in LeftPane when lastMessage is set. - // Notify the user when we're ready to render in LeftPane. - if ((await OpenGroup.getConversation(serverUrl))?.lastMessage) { - conversationRenderedResolve(); - window.pushToast({ - title: window.i18n('connectToServerFail'), - id: 'connectToServerFail', - type: 'error', - }); - } - }, 100); - - - } }); diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index d9ad94de4..3788380d9 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -66,20 +66,10 @@ export class SessionClosableOverlay extends React.Component { } public getContacts() { - const conversations = window.getConversations() || []; - - const conversationList = conversations.filter((conversation: any) => { - return ( - !conversation.isMe() && - conversation.isPrivate() && - !conversation.isSecondaryDevice() && - !conversation.isBlocked() - ); - }); + const contactsList = this.props.contacts ?? []; - return conversationList.map((d: any) => { - const lokiProfile = d.getLokiProfile(); - const name = lokiProfile ? lokiProfile.displayName : 'Anonymous'; + return contactsList.map((d: any) => { + const name = d.name ?? 'Anonymous'; // TODO: should take existing members into account const existingMember = false; @@ -87,10 +77,10 @@ export class SessionClosableOverlay extends React.Component { return { id: d.id, authorPhoneNumber: d.id, - authorProfileName: name, + authorProfileName: d.name, selected: false, authorName: name, - authorColor: d.getColor(), + authorColor: d.color, checkmarked: false, existingMember, }; diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 9ba5559c9..bc6d32a6d 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -120,14 +120,22 @@ export const _getLeftPaneLists = ( }; } - const isPublic = conversation.isPublic; - console.log('[vince] isPublic:', isPublic); - console.log('[vince] isPublic:', isPublic); - console.log('[vince] isPublic:', isPublic); - console.log('[vince] isPublic:', isPublic); - + // Add Open Group to list as soon as the name has been set + if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { + continue; + } + + // Show loading icon while fetching messages + if (conversation.isPublic && !conversation.timestamp) { + conversation.lastMessage = { + status: 'sending', + text: '', + isRss: false, + }; + } + // Remove all invalid conversations and conversatons of devices associated with cancelled attempted links - if (!conversation.timestamp) { + if (!conversation.isPublic && !conversation.timestamp) { continue; } @@ -139,7 +147,7 @@ export const _getLeftPaneLists = ( unreadCount += conversation.unreadCount; } - if (!conversation.activeAt) { + if (!conversation.isPublic && !conversation.activeAt) { continue; } From 3a3d41254292f19987f56a76d35c9ff0957ea8b9 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 14:37:25 +1000 Subject: [PATCH 12/17] linted --- ts/components/MainViewController.tsx | 66 ------------------- .../session/LeftPaneMessageSection.tsx | 32 ++++----- ts/session/types/OpenGroup.ts | 20 ++++-- ts/state/selectors/conversations.ts | 5 +- 4 files changed, 36 insertions(+), 87 deletions(-) diff --git a/ts/components/MainViewController.tsx b/ts/components/MainViewController.tsx index 055113ce5..fbdce046b 100644 --- a/ts/components/MainViewController.tsx +++ b/ts/components/MainViewController.tsx @@ -7,7 +7,6 @@ import { } from './session/settings/SessionSettings'; export const MainViewController = { - joinChannelStateManager, createClosedGroup, renderMessageView, renderSettingsView, @@ -40,71 +39,6 @@ export class MessageView extends React.Component { // //////////// Management ///////////// // ///////////////////////////////////// -function joinChannelStateManager( - thisRef: any, - serverURL: string, - onSuccess?: any -) { - // Any component that uses this function MUST have the keys [loading, connectSuccess] - // in their State - - // TODO: Make this not hard coded - const channelId = 1; - thisRef.setState({ loading: true }); - const connectionResult = window.attemptConnection(serverURL, channelId); - - // Give 5s maximum for promise to revole. Else, throw error. - const connectionTimeout = setTimeout(() => { - if (!thisRef.state.connectSuccess) { - thisRef.setState({ loading: false }); - window.pushToast({ - title: window.i18n('connectToServerFail'), - type: 'error', - id: 'connectToServerFail', - }); - - return; - } - }, window.CONSTANTS.MAX_CONNECTION_DURATION); - - connectionResult - .then(() => { - clearTimeout(connectionTimeout); - - if (thisRef.state.loading) { - thisRef.setState({ - connectSuccess: true, - loading: false, - }); - window.pushToast({ - title: window.i18n('connectToServerSuccess'), - id: 'connectToServerSuccess', - type: 'success', - }); - - if (onSuccess) { - onSuccess(); - } - } - }) - .catch((connectionError: string) => { - clearTimeout(connectionTimeout); - thisRef.setState({ - connectSuccess: true, - loading: false, - }); - window.pushToast({ - title: connectionError, - id: 'connectToServerFail', - type: 'error', - }); - - return false; - }); - - return true; -} - async function createClosedGroup( groupName: string, groupMembers: Array, diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 77a26f2dc..6065adab9 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -483,26 +483,28 @@ export class LeftPaneMessageSection extends React.Component { type: 'success', }); - this.setState({loading: true}); + this.setState({ loading: true }); } }); - successPromise.then(() => { - this.handleToggleOverlay(undefined); - this.setState({ - loading: false, - }); - }).catch(() => { - this.setState({ - loading: false, - }); + successPromise + .then(() => { + this.handleToggleOverlay(undefined); + this.setState({ + loading: false, + }); + }) + .catch(() => { + this.setState({ + loading: false, + }); - window.pushToast({ - title: window.i18n('connectToServerFail'), - id: 'connectToServerFail', - type: 'error', + window.pushToast({ + title: window.i18n('connectToServerFail'), + id: 'connectToServerFail', + type: 'error', + }); }); - }); } private async onCreateClosedGroup( diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index f70ffb934..121dc1a25 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -70,7 +70,10 @@ export class OpenGroup { return new OpenGroup(openGroupParams); } - public static async join(server: string, onLoading?: any): Promise { + public static async join( + server: string, + onLoading?: any + ): Promise { // onLoading called when the server begins connecting - after passing every guard const prefixedServer = OpenGroup.prefixify(server); @@ -122,7 +125,9 @@ export class OpenGroup { } const prefixedServer = this.prefixify(server); - const serverInfo = await window.lokiPublicChatAPI.findOrCreateServer(prefixedServer) as any; + const serverInfo = (await window.lokiPublicChatAPI.findOrCreateServer( + prefixedServer + )) as any; if (!serverInfo?.channels?.length) { return; @@ -137,7 +142,9 @@ export class OpenGroup { } const prefixedServer = this.prefixify(server); - return Boolean(await window.lokiPublicChatAPI.findOrCreateServer(prefixedServer)); + return Boolean( + await window.lokiPublicChatAPI.findOrCreateServer(prefixedServer) + ); } public static getConversationByCID(conversationId: string): any { @@ -145,7 +152,10 @@ export class OpenGroup { return ConversationController.get(conversationId); } - private static getServer(groupId: string, hasSSL: boolean): string | undefined { + private static getServer( + groupId: string, + hasSSL: boolean + ): string | undefined { const isValid = this.groupIdRegex.test(groupId); const strippedServer = isValid ? groupId.split('@')[1] : undefined; @@ -174,7 +184,7 @@ export class OpenGroup { private static prefixify(server: string, hasSSL: boolean = true): string { // Prefix server with https:// if it's not already prefixed with http or https. - const hasPrefix = server.match('^https?:\/\/'); + const hasPrefix = server.match('^https?://'); if (hasPrefix) { return server; } diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index bc6d32a6d..729a2f772 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -121,7 +121,10 @@ export const _getLeftPaneLists = ( } // Add Open Group to list as soon as the name has been set - if (conversation.isPublic && (!conversation.name || conversation.name === 'Unknown group')) { + if ( + conversation.isPublic && + (!conversation.name || conversation.name === 'Unknown group') + ) { continue; } From baf3050b1d56594be9d51a14e1637c96c05e3d21 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 15:03:50 +1000 Subject: [PATCH 13/17] @params --- .../session/SessionClosableOverlay.tsx | 2 +- ts/session/types/OpenGroup.ts | 70 ++++++++++++++----- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index 3788380d9..56f8538ae 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -77,7 +77,7 @@ export class SessionClosableOverlay extends React.Component { return { id: d.id, authorPhoneNumber: d.id, - authorProfileName: d.name, + authorProfileName: name, selected: false, authorName: name, authorColor: d.color, diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index 121dc1a25..23822eb55 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -1,4 +1,3 @@ -// This is the Open Group equivalent to the PubKey type. interface OpenGroupParams { server: string; @@ -7,8 +6,6 @@ interface OpenGroupParams { } export class OpenGroup { - // Matches prefixes https:// http:// plus no prefix. - // Servers without prefix default to https:// private static readonly serverRegex = new RegExp( '^((https?:\\/\\/){0,1})([\\w-]{2,}\\.){1,2}[\\w-]{2,}$' ); @@ -18,10 +15,18 @@ export class OpenGroup { public readonly server: string; public readonly channel: number; public readonly groupId?: string; - public readonly conversationId: string; // eg. c12 - + public readonly conversationId: string; + + /** + * An OpenGroup object. + * If `params.server` is not valid, this will throw an `Error`. + * + * @param params.server The server URL. `https` will be prepended if `http` or `https` is not explicitly set + * @param params.channel The server channel + * @param params.groupId The string corresponding to the server. Eg. `publicChat:1@chat.getsession.org` + * @param params.conversationId The conversation ID for the backbone model + */ constructor(params: OpenGroupParams) { - // https will be prepended unless explicitly http this.server = OpenGroup.prefixify(params.server.toLowerCase()); // Validate server format @@ -35,18 +40,28 @@ export class OpenGroup { this.groupId = OpenGroup.getGroupId(this.server, this.channel); } + /** + * Validate the URL of an open group server + * + * @param serverUrl The server URL to validate + */ public static validate(serverUrl: string): boolean { return this.serverRegex.test(serverUrl); } + /** + * Try to make a new instance of `OpenGroup`. + * This does NOT respect `ConversationController` and does not guarentee the conversation's existence. + * + * @param groupId The string corresponding to the server. Eg. `publicChat:1@chat.getsession.org` + * @param conversationId The conversation ID for the backbone model + * @returns `OpenGroup` if valid otherwise returns `undefined`. + */ public static from( groupId: string, conversationId: string, hasSSL: boolean = true ): OpenGroup | undefined { - // Returns a new instance from a groupId if it's valid - // eg. groupId = 'publicChat:1@chat.getsession.org' - const server = this.getServer(groupId, hasSSL); const channel = this.getChannel(groupId); @@ -70,12 +85,17 @@ export class OpenGroup { return new OpenGroup(openGroupParams); } + /** + * Join an open group + * + * @param server The server URL + * @param onLoading Callback function to be called once server begins connecting + * @returns `OpenGroup` if connection success or if already connected + */ public static async join( server: string, onLoading?: any ): Promise { - // onLoading called when the server begins connecting - after passing every guard - const prefixedServer = OpenGroup.prefixify(server); if (!OpenGroup.validate(server)) { return; @@ -119,6 +139,12 @@ export class OpenGroup { }); } + /** + * Get the conversation model of a server from its URL + * + * @param server The server URL + * @returns BackBone conversation model corresponding to the server if it exists, otherwise `undefined` + */ public static async getConversation(server: string): Promise { if (!OpenGroup.validate(server)) { return; @@ -136,6 +162,23 @@ export class OpenGroup { return serverInfo.channels[0].conversation; } + /** + * Get the conversation model of a server from conversation ID + * + * @param conversationId The server's conversation ID + * @returns BackBone conversation model corresponding to the server if it exists, otherwise `undefined` + */ + public static getConversationByCID(conversationId: string): any { + const { ConversationController } = window; + return ConversationController.get(conversationId); + } + + /** + * Check if the server exists. + * This does not compare against your conversations with the server. + * + * @param server The server URL + */ public static async serverExists(server: string): Promise { if (!OpenGroup.validate(server)) { return false; @@ -147,11 +190,6 @@ export class OpenGroup { ); } - public static getConversationByCID(conversationId: string): any { - const { ConversationController } = window; - return ConversationController.get(conversationId); - } - private static getServer( groupId: string, hasSSL: boolean From 4a13b1e6bbafb1f3b59b4391f0e94fb314a2e48a Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 15:25:18 +1000 Subject: [PATCH 14/17] attemptConnection-complete --- js/background.js | 22 ++++++++++++++++------ ts/session/types/OpenGroup.ts | 1 - ts/state/selectors/conversations.ts | 3 ++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/js/background.js b/js/background.js index 243715932..fb132b982 100644 --- a/js/background.js +++ b/js/background.js @@ -1092,12 +1092,22 @@ // Attempts a connection to an open group server window.attemptConnection = async (serverURL, channelId) => { - let rawserverURL = serverURL + let completeServerURL = serverURL.toLowerCase(); + const valid = window.libsession.Types.OpenGroup.validate(completeServerURL); + if (!valid) { + return new Promise((_resolve, reject) => { + reject(window.i18n('connectToServerFail')); + }); + } + + // Add http or https prefix to server + completeServerURL = window.libsession.Types.OpenGroup.prefixify(completeServerURL); + + const rawServerURL = serverURL .replace(/^https?:\/\//i, '') .replace(/[/\\]+$/i, ''); - rawserverURL = rawserverURL.toLowerCase(); - const sslServerURL = `https://${rawserverURL}`; - const conversationId = `publicChat:${channelId}@${rawserverURL}`; + + const conversationId = `publicChat:${channelId}@${rawServerURL}`; // Quickly peak to make sure we don't already have it const conversationExists = window.ConversationController.get( @@ -1112,7 +1122,7 @@ // Get server const serverAPI = await window.lokiPublicChatAPI.findOrCreateServer( - sslServerURL + completeServerURL ); // SSL certificate failure or offline if (!serverAPI) { @@ -1129,7 +1139,7 @@ ); // Convert conversation to a public one - await conversation.setPublicSource(sslServerURL, channelId); + await conversation.setPublicSource(completeServerURL, channelId); // and finally activate it conversation.getPublicSendData(); // may want "await" if you want to use the API diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index 23822eb55..52026d048 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -1,4 +1,3 @@ - interface OpenGroupParams { server: string; channel: number; diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 729a2f772..3f28c7851 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -137,7 +137,8 @@ export const _getLeftPaneLists = ( }; } - // Remove all invalid conversations and conversatons of devices associated with cancelled attempted links + // Remove all invalid conversations and conversatons of devices associated + // with cancelled attempted links if (!conversation.isPublic && !conversation.timestamp) { continue; } From 6cb05c36776d40811325c82bbfb0d8b29f50d008 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 15:36:59 +1000 Subject: [PATCH 15/17] review-fixes --- _locales/en/messages.json | 4 ++ js/background.js | 8 ++- .../session/LeftPaneMessageSection.tsx | 52 +++++++++---------- .../session/SessionClosableOverlay.tsx | 2 +- ts/session/types/OpenGroup.ts | 17 ++---- 5 files changed, 41 insertions(+), 42 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index bc1dfbbbc..7fc3b92b3 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -2377,6 +2377,10 @@ "displayName": { "message": "Display Name" }, + "anonymous": { + "message": "Anonymous", + "description": "The name of currently unidentified users" + }, "enterDisplayName": { "message": "Enter a display name" }, diff --git a/js/background.js b/js/background.js index fb132b982..ba2e9c047 100644 --- a/js/background.js +++ b/js/background.js @@ -1093,7 +1093,9 @@ // Attempts a connection to an open group server window.attemptConnection = async (serverURL, channelId) => { let completeServerURL = serverURL.toLowerCase(); - const valid = window.libsession.Types.OpenGroup.validate(completeServerURL); + const valid = window.libsession.Types.OpenGroup.validate( + completeServerURL + ); if (!valid) { return new Promise((_resolve, reject) => { reject(window.i18n('connectToServerFail')); @@ -1101,7 +1103,9 @@ } // Add http or https prefix to server - completeServerURL = window.libsession.Types.OpenGroup.prefixify(completeServerURL); + completeServerURL = window.libsession.Types.OpenGroup.prefixify( + completeServerURL + ); const rawServerURL = serverURL .replace(/^https?:\/\//i, '') diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 6065adab9..c34307d14 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -475,36 +475,34 @@ export class LeftPaneMessageSection extends React.Component { } // Connect to server - const successPromise = OpenGroup.join(serverUrl, async () => { - if (await OpenGroup.serverExists(serverUrl)) { - window.pushToast({ - title: window.i18n('connectingToServer'), - id: 'connectToServerSuccess', - type: 'success', - }); - - this.setState({ loading: true }); - } - }); + try { + await OpenGroup.join(serverUrl, async () => { + if (await OpenGroup.serverExists(serverUrl)) { + window.pushToast({ + title: window.i18n('connectingToServer'), + id: 'connectToServerSuccess', + type: 'success', + }); + + this.setState({ loading: true }); + } + }); - successPromise - .then(() => { - this.handleToggleOverlay(undefined); - this.setState({ - loading: false, - }); - }) - .catch(() => { - this.setState({ - loading: false, - }); + this.handleToggleOverlay(undefined); + this.setState({ + loading: false, + }); + } catch (e) { + this.setState({ + loading: false, + }); - window.pushToast({ - title: window.i18n('connectToServerFail'), - id: 'connectToServerFail', - type: 'error', - }); + window.pushToast({ + title: window.i18n('connectToServerFail'), + id: 'connectToServerFail', + type: 'error', }); + } } private async onCreateClosedGroup( diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index 56f8538ae..f2085948c 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -69,7 +69,7 @@ export class SessionClosableOverlay extends React.Component { const contactsList = this.props.contacts ?? []; return contactsList.map((d: any) => { - const name = d.name ?? 'Anonymous'; + const name = d.name ?? window.i18n('anonymous'); // TODO: should take existing members into account const existingMember = false; diff --git a/ts/session/types/OpenGroup.ts b/ts/session/types/OpenGroup.ts index 52026d048..462a21589 100644 --- a/ts/session/types/OpenGroup.ts +++ b/ts/session/types/OpenGroup.ts @@ -1,3 +1,5 @@ +import { ConversationModel } from '../../../js/models/conversations'; + interface OpenGroupParams { server: string; channel: number; @@ -144,7 +146,9 @@ export class OpenGroup { * @param server The server URL * @returns BackBone conversation model corresponding to the server if it exists, otherwise `undefined` */ - public static async getConversation(server: string): Promise { + public static async getConversation( + server: string + ): Promise { if (!OpenGroup.validate(server)) { return; } @@ -161,17 +165,6 @@ export class OpenGroup { return serverInfo.channels[0].conversation; } - /** - * Get the conversation model of a server from conversation ID - * - * @param conversationId The server's conversation ID - * @returns BackBone conversation model corresponding to the server if it exists, otherwise `undefined` - */ - public static getConversationByCID(conversationId: string): any { - const { ConversationController } = window; - return ConversationController.get(conversationId); - } - /** * Check if the server exists. * This does not compare against your conversations with the server. From 8b8510cf71873b208c9fffd2e19c3f00b3341e8d Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 15:59:57 +1000 Subject: [PATCH 16/17] finally-clause --- ts/components/session/LeftPaneMessageSection.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index c34307d14..03210e07f 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -487,11 +487,6 @@ export class LeftPaneMessageSection extends React.Component { this.setState({ loading: true }); } }); - - this.handleToggleOverlay(undefined); - this.setState({ - loading: false, - }); } catch (e) { this.setState({ loading: false, @@ -502,6 +497,10 @@ export class LeftPaneMessageSection extends React.Component { id: 'connectToServerFail', type: 'error', }); + } finally { + this.setState({ + loading: false, + }); } } From 4574407b41a6b5a032f56161114ae17b94d8f91c Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 8 Jul 2020 16:00:57 +1000 Subject: [PATCH 17/17] finally-clause --- ts/components/session/LeftPaneMessageSection.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 03210e07f..26f92bfd6 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -488,10 +488,6 @@ export class LeftPaneMessageSection extends React.Component { } }); } catch (e) { - this.setState({ - loading: false, - }); - window.pushToast({ title: window.i18n('connectToServerFail'), id: 'connectToServerFail',