diff --git a/js/background.js b/js/background.js index d83615be7..a7fe870da 100644 --- a/js/background.js +++ b/js/background.js @@ -248,8 +248,8 @@ // singleton to interface the File server // If already exists we registered as a secondary device if (!window.lokiFileServerAPI) { - window.lokiFileServerAPI = new window.LokiFileServerAPI(ourKey); - await window.lokiFileServerAPI.establishConnection( + window.lokiFileServerAPIFactory = new window.LokiFileServerAPI(ourKey); + window.lokiFileServerAPI = await window.lokiFileServerAPIFactory.establishHomeConnection( window.getDefaultFileServer() ); } @@ -904,6 +904,12 @@ displayName: newName, avatar: newAvatarPath, }); + // inform all your registered public servers + // could put load on all the servers + // if they just keep changing their names without sending messages + // so we could disable this here + // or least it enable for the quickest response + window.lokiPublicChatAPI.setProfileName(newName); }, }); } @@ -1134,8 +1140,8 @@ if (Whisper.Registration.ongoingSecondaryDeviceRegistration()) { const ourKey = textsecure.storage.user.getNumber(); window.lokiMessageAPI = new window.LokiMessageAPI(ourKey); - window.lokiFileServerAPI = new window.LokiFileServerAPI(ourKey); - await window.lokiFileServerAPI.establishConnection( + window.lokiFileServerAPIFactory = new window.LokiFileServerAPI(ourKey); + window.lokiFileServerAPI = await window.lokiFileServerAPIFactory.establishHomeConnection( window.getDefaultFileServer() ); window.localLokiServer = null; diff --git a/js/conversation_controller.js b/js/conversation_controller.js index 3f83d6b61..3619f3e62 100644 --- a/js/conversation_controller.js +++ b/js/conversation_controller.js @@ -1,4 +1,4 @@ -/* global _, Whisper, Backbone, storage, textsecure, libsignal, lokiPublicChatAPI */ +/* global _, Whisper, Backbone, storage, textsecure, libsignal, log */ /* eslint-disable more/no-then */ @@ -160,8 +160,12 @@ return; } if (conversation.isPublic()) { - const server = conversation.getPublicSource(); - lokiPublicChatAPI.unregisterChannel(server.server, server.channelId); + const channelAPI = await conversation.getPublicSendData(); + if (channelAPI === null) { + log.warn(`Could not get API for public conversation ${id}`); + } else { + channelAPI.serverAPI.partChannel(channelAPI.channelId); + } } await conversation.destroyMessages(); const deviceIds = await textsecure.storage.protocol.getDeviceIds(id); diff --git a/js/models/conversations.js b/js/models/conversations.js index a682e6fd9..eb2e6cfc7 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -2333,7 +2333,7 @@ ); return null; } - const channelAPI = serverAPI.findOrCreateChannel( + const channelAPI = await serverAPI.findOrCreateChannel( this.get('channelId'), this.id ); diff --git a/js/modules/loki_app_dot_net_api.js b/js/modules/loki_app_dot_net_api.js index 23c9711af..dc1a15a26 100644 --- a/js/modules/loki_app_dot_net_api.js +++ b/js/modules/loki_app_dot_net_api.js @@ -1,5 +1,5 @@ /* global log, textsecure, libloki, Signal, Whisper, Headers, ConversationController, -clearTimeout, MessageController, libsignal, StringView, window, _, lokiFileServerAPI, +clearTimeout, MessageController, libsignal, StringView, window, _, dcodeIO, Buffer */ const EventEmitter = require('events'); const nodeFetch = require('node-fetch'); @@ -13,7 +13,8 @@ const PUBLICCHAT_DELETION_POLL_EVERY = 5 * 1000; // 5s const PUBLICCHAT_MOD_POLL_EVERY = 30 * 1000; // 30s const PUBLICCHAT_MIN_TIME_BETWEEN_DUPLICATE_MESSAGES = 10 * 1000; // 10s -const ATTACHMENT_TYPE = 'net.app.core.oembed'; +const HOMESERVER_USER_ANNOTATION_TYPE = 'network.loki.messenger.homeserver'; +const MESSAGE_ATTACHMENT_TYPE = 'net.app.core.oembed'; const LOKI_ATTACHMENT_TYPE = 'attachment'; const LOKI_PREVIEW_TYPE = 'preview'; @@ -26,7 +27,6 @@ class LokiAppDotNetAPI extends EventEmitter { this.myPrivateKey = false; this.allMembers = []; // Multidevice states - this.slavePrimaryMap = {}; this.primaryUserProfileName = {}; } @@ -91,7 +91,21 @@ class LokiAppDotNetAPI extends EventEmitter { this.servers.splice(i, 1); } - getListOfMembers() { + // shouldn't this be scoped per conversation? + async getListOfMembers() { + // enable in the next release + /* + let members = []; + await Promise.all(this.servers.map(async server => { + await Promise.all(server.channels.map(async channel => { + const newMembers = await channel.getSubscribers(); + members = [...members, ...newMembers]; + })); + })); + const results = members.map(member => { + return { authorPhoneNumber: member.username }; + }); + */ return this.allMembers; } @@ -100,6 +114,25 @@ class LokiAppDotNetAPI extends EventEmitter { setListOfMembers(members) { this.allMembers = members; } + + async setProfileName(profileName) { + await Promise.all( + this.servers.map(async server => { + await server.setProfileName(profileName); + }) + ); + } + + async setHomeServer(homeServer) { + await Promise.all( + this.servers.map(async server => { + // this may fail + // but we can't create a sql table to remember to retry forever + // I think we just silently fail for now + await server.setHomeServer(homeServer); + }) + ); + } } class LokiAppDotNetServerAPI { @@ -118,18 +151,30 @@ class LokiAppDotNetServerAPI { } // channel getter/factory - findOrCreateChannel(channelId, conversationId) { + async findOrCreateChannel(channelId, conversationId) { let thisChannel = this.channels.find( channel => channel.channelId === channelId ); if (!thisChannel) { - log.info(`LokiAppDotNetAPI creating channel ${conversationId}`); + log.info(`LokiAppDotNetAPI registering channel ${conversationId}`); + // make sure we're subscribed + // eventually we'll need to move to account registration/add server + await this.serverRequest(`channels/${channelId}/subscribe`, { + method: 'POST', + }); thisChannel = new LokiPublicChannelAPI(this, channelId, conversationId); this.channels.push(thisChannel); } return thisChannel; } + async partChannel(channelId) { + await this.serverRequest(`channels/${channelId}/subscribe`, { + method: 'DELETE', + }); + this.unregisterChannel(channelId); + } + // deallocate resources channel uses unregisterChannel(channelId) { let thisChannel; @@ -147,6 +192,68 @@ class LokiAppDotNetServerAPI { this.channels.splice(i, 1); } + async setProfileName(profileName) { + // when we add an annotation, may need this + /* + const privKey = await this.serverAPI.chatAPI.getPrivateKey(); + // we might need an annotation that sets the homeserver for media + // better to include this with each attachment... + const objToSign = { + name: profileName, + version: 1, + annotations: [], + }; + const sig = await libsignal.Curve.async.calculateSignature( + privKey, + JSON.stringify(objToSign) + ); + */ + + const res = await this.serverRequest('users/me', { + method: 'PATCH', + objBody: { + name: profileName, + }, + }); + // no big deal if it fails... + if (res.err || !res.response || !res.response.data) { + if (res.err) { + log.error(`Error ${res.err}`); + } + return []; + } + + // expecting a user object + return res.response.data.annotations || []; + + // if no profileName should we update the local from the server? + // no because there will be multiple public chat servers + } + + async setHomeServer(homeServer) { + const res = await this.serverRequest('users/me', { + method: 'PATCH', + objBody: { + annotations: [ + { + type: HOMESERVER_USER_ANNOTATION_TYPE, + value: homeServer, + }, + ], + }, + }); + + if (res.err || !res.response || !res.response.data) { + if (res.err) { + log.error(`Error ${res.err}`); + } + return []; + } + + // expecting a user object + return res.response.data.annotations || []; + } + // get active token for this server async getOrRefreshServerToken(forceRefresh = false) { let token; @@ -178,42 +285,14 @@ class LokiAppDotNetServerAPI { tokenRes.response.data && tokenRes.response.data.user ) { - // get our profile name and write it to the network + // get our profile name const ourNumber = textsecure.storage.user.getNumber(); const profileConvo = ConversationController.get(ourNumber); const profileName = profileConvo.getProfileName(); - - // update profile name as needed + // if doesn't match, write it to the network if (tokenRes.response.data.user.name !== profileName) { - if (profileName) { - // will need this when we add an annotation - /* - const privKey = await this.serverAPI.chatAPI.getPrivateKey(); - // we might need an annotation that sets the homeserver for media - // better to include this with each attachment... - const objToSign = { - name: profileName, - version: 1, - annotations: [], - }; - const sig = await libsignal.Curve.async.calculateSignature( - privKey, - JSON.stringify(objToSign) - ); - */ - - await this.serverRequest('users/me', { - method: 'PATCH', - objBody: { - name: profileName, - }, - }); - // no big deal if it fails... - // } else { - // should we update the local from the server? - // guessing no because there will be multiple servers - } - // update our avatar if needed + // update our profile name if it got out of sync + this.setProfileName(profileName); } } @@ -393,6 +472,71 @@ class LokiAppDotNetServerAPI { return res.response.data.annotations || []; } + async getSubscribers(channelId, wantObjects) { + if (!channelId) { + log.warn('No channelId provided to getSubscribers!'); + return []; + } + + let res = {}; + if (!Array.isArray(channelId) && wantObjects) { + res = await this.serverRequest(`channels/${channelId}/subscribers`, { + method: 'GET', + params: { + include_user_annotations: 1, + }, + }); + } else { + // not deployed on all backends yet + res.err = 'array subscribers endpoint not yet implemented'; + /* + var list = channelId; + if (!Array.isArray(list)) { + list = [channelId]; + } + const idres = await this.serverRequest(`channels/subscribers/ids`, { + method: 'GET', + params: { + ids: list.join(','), + include_user_annotations: 1, + }, + }); + if (wantObjects) { + if (idres.err || !idres.response || !idres.response.data) { + if (idres.err) { + log.error(`Error ${idres.err}`); + } + return []; + } + const userList = []; + await Promise.all(idres.response.data.map(async channelId => { + const channelUserObjs = await this.getUsers(idres.response.data[channelId]); + userList.push(...channelUserObjs); + })); + res = { + response: { + meta: { + code: 200, + }, + data: userList + } + } + } else { + res = idres; + } + */ + } + + if (res.err || !res.response || !res.response.data) { + if (res.err) { + log.error(`Error ${res.err}`); + } + return []; + } + + return res.response.data || []; + } + async getUsers(pubKeys) { if (!pubKeys) { log.warn('No pubKeys provided to getUsers!'); @@ -565,6 +709,10 @@ class LokiPublicChannelAPI { return this.serverAPI.serverRequest(endpoint, options); } + getSubscribers() { + return this.serverAPI.getSubscribers(this.channelId, true); + } + // get moderation actions async pollForModerators() { try { @@ -874,6 +1022,7 @@ class LokiPublicChannelAPI { async pollOnceForMessages() { const params = { include_annotations: 1, + include_user_annotations: 1, // to get the home server include_deleted: false, }; if (!this.conversation) { @@ -885,6 +1034,7 @@ class LokiPublicChannelAPI { params.since_id = this.lastGot; // Just grab the most recent 100 messages if you don't have a valid lastGot params.count = this.lastGot === 0 ? -100 : 20; + // log.info(`Getting ${params.count} from ${this.lastGot} on ${this.baseChannelUrl}`); const res = await this.serverRequest(`${this.baseChannelUrl}/messages`, { params, }); @@ -894,11 +1044,16 @@ class LokiPublicChannelAPI { } let receivedAt = new Date().getTime(); - const pubKeys = []; + const homeServerPubKeys = {}; let pendingMessages = []; + // get our profile name + const ourNumber = textsecure.storage.user.getNumber(); + let lastProfileName = false; + // the signature forces this to be async pendingMessages = await Promise.all( + // process these in chronological order res.response.data.reverse().map(async adnMessage => { // still update our last received if deleted, not signed or not valid this.lastGot = !this.lastGot @@ -920,13 +1075,7 @@ class LokiPublicChannelAPI { return false; } - const { - timestamp, - quote, - attachments, - preview, - avatar, - } = messengerData; + const { timestamp, quote, attachments, preview } = messengerData; if (!timestamp) { return false; // Invalid message } @@ -961,11 +1110,35 @@ class LokiPublicChannelAPI { ].splice(-5); const from = adnMessage.user.name || 'Anonymous'; // profileName - const avatarObj = avatar || null; + + // if us + if (adnMessage.user.username === ourNumber) { + // update the last name we saw from ourself + lastProfileName = from; + } // track sources for multidevice support - if (pubKeys.indexOf(`@${adnMessage.user.username}`) === -1) { - pubKeys.push(`@${adnMessage.user.username}`); + // sort it by home server + let homeServer = window.getDefaultFileServer(); + if (adnMessage.user && adnMessage.user.annotations.length) { + const homeNotes = adnMessage.user.annotations.filter( + note => note.type === HOMESERVER_USER_ANNOTATION_TYPE + ); + // FIXME: this annotation should probably be signed and verified... + homeServer = homeNotes.reduce( + (curVal, note) => (note.value ? note.value : curVal), + homeServer + ); + } + if (homeServerPubKeys[homeServer] === undefined) { + homeServerPubKeys[homeServer] = []; + } + if ( + homeServerPubKeys[homeServer].indexOf( + `@${adnMessage.user.username}` + ) === -1 + ) { + homeServerPubKeys[homeServer].push(`@${adnMessage.user.username}`); } // generate signal message object @@ -999,7 +1172,6 @@ class LokiPublicChannelAPI { preview, profile: { displayName: from, - avatar: avatarObj, }, }, }; @@ -1010,27 +1182,59 @@ class LokiPublicChannelAPI { return messageData; }) ); - this.conversation.setLastRetrievedMessage(this.lastGot); // do we really need this? if (!pendingMessages.length) { + this.conversation.setLastRetrievedMessage(this.lastGot); return; } - // get list of verified primary PKs - const verifiedPrimaryPKs = await lokiFileServerAPI.verifyPrimaryPubKeys( - pubKeys + // slave to primary map for this group of messages + let slavePrimaryMap = {}; + // pubKey to avatar + let avatarMap = {}; + + // reduce list of servers into verified maps and keys + const verifiedPrimaryPKs = await Object.keys(homeServerPubKeys).reduce( + async (curVal, serverUrl) => { + // get an API to this server + const serverAPI = await window.lokiFileServerAPIFactory.establishConnection( + serverUrl + ); + + // get list of verified primary PKs + const result = await serverAPI.verifyPrimaryPubKeys( + homeServerPubKeys[serverUrl] + ); + + // merged these device mappings into our slavePrimaryMap + // should not be any collisions, since each pubKey can only have one home server + slavePrimaryMap = { ...slavePrimaryMap, ...result.slaveMap }; + + // merge this servers avatarMap into our map + // again shouldn't be any collisions + avatarMap = { ...avatarMap, ...serverAPI.avatarMap }; + + // copy verified pub keys into result + return curVal.concat(result.verifiedPrimaryPKs); + }, + [] ); - // access slavePrimaryMap set by verifyPrimaryPubKeys - const { slavePrimaryMap } = this.serverAPI.chatAPI; // sort pending messages by if slave device or not /* eslint-disable no-param-reassign */ const slaveMessages = pendingMessages - .filter(messageData => !!messageData) + .filter(messageData => !!messageData) // filter out false messages .reduce((retval, messageData) => { - // if a known slave, queue + // if a known slave if (slavePrimaryMap[messageData.source]) { + // pop primary device avatars in + if (avatarMap[slavePrimaryMap[messageData.source]]) { + // modify messageData for user's avatar + messageData.message.profile.avatar = + avatarMap[slavePrimaryMap[messageData.source]]; + } + // delay sending the message if (retval[messageData.source] === undefined) { retval[messageData.source] = [messageData]; @@ -1038,7 +1242,15 @@ class LokiPublicChannelAPI { retval[messageData.source].push(messageData); } } else { - // no user or isPrimary means not multidevice, send event now + // not from a paired/slave/unregistered device + + // pop current device avatars in + if (avatarMap[messageData.source]) { + // modify messageData for user's avatar + messageData.message.profile.avatar = avatarMap[messageData.source]; + } + + // send event now this.serverAPI.chatAPI.emit('publicMessage', { message: messageData, }); @@ -1066,7 +1278,6 @@ class LokiPublicChannelAPI { /* eslint-enable no-param-reassign */ // process remaining messages - const ourNumber = textsecure.storage.user.getNumber(); Object.keys(slaveMessages).forEach(slaveKey => { // prevent our own device sent messages from coming back in if (slaveKey === ourNumber) { @@ -1092,6 +1303,21 @@ class LokiPublicChannelAPI { }); }); }); + + // if we received one of our own messages + if (lastProfileName !== false) { + // get current profileName + const profileConvo = ConversationController.get(ourNumber); + const profileName = profileConvo.getProfileName(); + // check to see if it out of sync + if (profileName !== lastProfileName) { + // out of sync, update this server + this.serverAPI.setProfileName(profileName); + } + } + + // finally update our position + this.conversation.setLastRetrievedMessage(this.lastGot); } static getPreviewFromAnnotation(annotation) { @@ -1119,7 +1345,7 @@ class LokiPublicChannelAPI { static getAnnotationFromPreview(preview) { const annotation = { - type: ATTACHMENT_TYPE, + type: MESSAGE_ATTACHMENT_TYPE, value: { // Mandatory ADN fields version: '1.0', @@ -1157,7 +1383,7 @@ class LokiPublicChannelAPI { type = 'other'; } const annotation = { - type: ATTACHMENT_TYPE, + type: MESSAGE_ATTACHMENT_TYPE, value: { // Mandatory ADN fields version: '1.0', @@ -1191,6 +1417,7 @@ class LokiPublicChannelAPI { type: 'network.loki.messenger.publicChat', value: { timestamp: messageTimeStamp, + // can remove after this release avatar: avatarAnnotation, }, }, diff --git a/js/modules/loki_file_server_api.js b/js/modules/loki_file_server_api.js index 8acd119cc..b9b20011d 100644 --- a/js/modules/loki_file_server_api.js +++ b/js/modules/loki_file_server_api.js @@ -1,64 +1,50 @@ -/* global window, log, libloki */ +/* global log, libloki */ /* global storage: false */ /* global Signal: false */ /* global log: false */ const LokiAppDotNetAPI = require('./loki_app_dot_net_api'); -const DEVICE_MAPPING_ANNOTATION_KEY = 'network.loki.messenger.devicemapping'; +const DEVICE_MAPPING_USER_ANNOTATION_TYPE = + 'network.loki.messenger.devicemapping'; -// can have multiple of these objects instances as each user can have a +// can have multiple of these instances as each user can have a // different home server -class LokiFileServerAPI { +class LokiFileServerInstance { constructor(ourKey) { this.ourKey = ourKey; + // why don't we extend this? this._adnApi = new LokiAppDotNetAPI(ourKey); + this.avatarMap = {}; } - async establishConnection(serverUrl) { + // FIXME: we don't always need a token... this._server = await this._adnApi.findOrCreateServer(serverUrl); // TODO: Handle this failure gracefully if (!this._server) { log.error('Failed to establish connection to file server'); } } - async getUserDeviceMapping(pubKey) { const annotations = await this._server.getUserAnnotations(pubKey); const deviceMapping = annotations.find( - annotation => annotation.type === DEVICE_MAPPING_ANNOTATION_KEY + annotation => annotation.type === DEVICE_MAPPING_USER_ANNOTATION_TYPE ); return deviceMapping ? deviceMapping.value : null; } - async updateOurDeviceMapping() { - const isPrimary = !storage.get('isSecondaryDevice'); - let authorisations; - if (isPrimary) { - authorisations = await Signal.Data.getGrantAuthorisationsForPrimaryPubKey( - this.ourKey - ); - } else { - authorisations = [ - await Signal.Data.getGrantAuthorisationForSecondaryPubKey(this.ourKey), - ]; - } - return this._setOurDeviceMapping(authorisations, isPrimary); - } - - async getDeviceMappingForUsers(pubKeys) { - const users = await this._server.getUsers(pubKeys); - return users; - } - async verifyUserObjectDeviceMap(pubKeys, isRequest, iterator) { - const users = await this.getDeviceMappingForUsers(pubKeys); + const users = await this._server.getUsers(pubKeys); // go through each user and find deviceMap annotations const notFoundUsers = []; await Promise.all( users.map(async user => { let found = false; + // if this user has an avatar set, copy it into the map + this.avatarMap[user.username] = user.avatar_image + ? user.avatar_image.url + : false; if (!user.annotations || !user.annotations.length) { log.info( `verifyUserObjectDeviceMap no annotation for ${user.username}` @@ -66,7 +52,7 @@ class LokiFileServerAPI { return; } const mappingNote = user.annotations.find( - note => note.type === DEVICE_MAPPING_ANNOTATION_KEY + note => note.type === DEVICE_MAPPING_USER_ANNOTATION_TYPE ); const { authorisations } = mappingNote.value; if (!Array.isArray(authorisations)) { @@ -105,6 +91,10 @@ class LokiFileServerAPI { // checkSig disabled for now // const checkSigs = {}; // cache for authorisation const primaryPubKeys = []; + const result = { + verifiedPrimaryPKs: [], + slaveMap: {}, + }; // go through multiDeviceResults and get primary Pubkey await this.verifyUserObjectDeviceMap(pubKeys, true, (slaveKey, auth) => { @@ -144,7 +134,8 @@ class LokiFileServerAPI { // no valid primary pubkeys to check if (!primaryPubKeys.length) { // log.warn(`no valid primary pubkeys to check ${pubKeys}`); - return []; + // do we want to update slavePrimaryMap? + return result; } const verifiedPrimaryPKs = []; @@ -193,30 +184,43 @@ class LokiFileServerAPI { }); }); - // FIXME: move to a return value since we're only scoped to pubkeys given - // make new map final - window.lokiPublicChatAPI.slavePrimaryMap = newSlavePrimaryMap; - - log.info( - `Updated device mappings ${JSON.stringify( - window.lokiPublicChatAPI.slavePrimaryMap - )}` - ); + log.info(`Updated device mappings ${JSON.stringify(newSlavePrimaryMap)}`); - return verifiedPrimaryPKs; + result.verifiedPrimaryPKs = verifiedPrimaryPKs; + result.slaveMap = newSlavePrimaryMap; + return result; } +} +// extends LokiFileServerInstance with functions we'd only perform on our own home server +// so we don't accidentally send info to the wrong file server +class LokiHomeServerInstance extends LokiFileServerInstance { _setOurDeviceMapping(authorisations, isPrimary) { const content = { isPrimary: isPrimary ? '1' : '0', authorisations, }; return this._server.setSelfAnnotation( - DEVICE_MAPPING_ANNOTATION_KEY, + DEVICE_MAPPING_USER_ANNOTATION_TYPE, content ); } + async updateOurDeviceMapping() { + const isPrimary = !storage.get('isSecondaryDevice'); + let authorisations; + if (isPrimary) { + authorisations = await Signal.Data.getGrantAuthorisationsForPrimaryPubKey( + this.ourKey + ); + } else { + authorisations = [ + await Signal.Data.getGrantAuthorisationForSecondaryPubKey(this.ourKey), + ]; + } + return this._setOurDeviceMapping(authorisations, isPrimary); + } + uploadAvatar(data) { return this._server.uploadAvatar(data); } @@ -230,4 +234,38 @@ class LokiFileServerAPI { } } -module.exports = LokiFileServerAPI; +// this will be our instance factory +class LokiFileServerFactoryAPI { + constructor(ourKey) { + this.ourKey = ourKey; + this.servers = []; + } + + async establishHomeConnection(serverUrl) { + let thisServer = this.servers.find( + server => server._server.baseServerUrl === serverUrl + ); + if (!thisServer) { + thisServer = new LokiHomeServerInstance(this.ourKey); + log.info(`Registering HomeServer ${serverUrl}`); + await thisServer.establishConnection(serverUrl); + this.servers.push(thisServer); + } + return thisServer; + } + + async establishConnection(serverUrl) { + let thisServer = this.servers.find( + server => server._server.baseServerUrl === serverUrl + ); + if (!thisServer) { + thisServer = new LokiFileServerInstance(this.ourKey); + log.info(`Registering FileServer ${serverUrl}`); + await thisServer.establishConnection(serverUrl); + this.servers.push(thisServer); + } + return thisServer; + } +} + +module.exports = LokiFileServerFactoryAPI; diff --git a/js/views/connecting_to_server_dialog_view.js b/js/views/connecting_to_server_dialog_view.js index 5c45a1ef1..65b427aee 100644 --- a/js/views/connecting_to_server_dialog_view.js +++ b/js/views/connecting_to_server_dialog_view.js @@ -48,7 +48,7 @@ conversationId, 'group' ); - serverAPI.findOrCreateChannel(channelId, conversationId); + await serverAPI.findOrCreateChannel(channelId, conversationId); await conversation.setPublicSource(sslServerUrl, channelId); await conversation.setFriendRequestStatus( friends.friendRequestStatusEnum.friends diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index 4ba95d709..96332b17c 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -2568,7 +2568,7 @@ this.view.scrollToBottomIfNeeded(); }, - maybeShowMembers(event) { + async maybeShowMembers(event) { const filterMembers = (caseSensitiveQuery, member) => { const { authorPhoneNumber, authorProfileName } = member; @@ -2613,8 +2613,11 @@ let allMembers; if (this.model.isPublic()) { - const members = window.lokiPublicChatAPI - .getListOfMembers() + // const api = await this.model.getPublicSendData(); + // not quite in the right format tho yet... + // let members = await api.getSubscribers(); + let members = await window.lokiPublicChatAPI.getListOfMembers(); + members = members .filter(d => !!d) .filter(d => d.authorProfileName !== 'Anonymous'); allMembers = _.uniq(members, true, d => d.authorPhoneNumber); diff --git a/ts/components/conversation/AddMentions.tsx b/ts/components/conversation/AddMentions.tsx index 28a1d5910..8b4161584 100644 --- a/ts/components/conversation/AddMentions.tsx +++ b/ts/components/conversation/AddMentions.tsx @@ -79,7 +79,7 @@ class Mention extends React.Component { } } - private findMember(pubkey: String) { + private async findMember(pubkey: String) { let groupMembers; const groupConvos = window.getConversations().models.filter((d: any) => { @@ -97,10 +97,8 @@ class Mention extends React.Component { } if (thisConvo.isPublic()) { - // TODO: make this work for other public chats as well - groupMembers = window.lokiPublicChatAPI - .getListOfMembers() - .filter((m: any) => !!m); + groupMembers = await window.lokiPublicChatAPI.getListOfMembers(); + groupMembers = groupMembers.filter((m: any) => !!m); } else { const privateConvos = window .getConversations()