diff --git a/app/sql.js b/app/sql.js index 227f59729..03a892eb6 100644 --- a/app/sql.js +++ b/app/sql.js @@ -75,6 +75,8 @@ module.exports = { createOrUpdatePairingAuthorisation, getAuthorisationForPubKey, getSecondaryDevicesFor, + getPrimaryDeviceFor, + getPairedDevicesFor, createOrUpdateItem, getItemById, @@ -1284,6 +1286,43 @@ async function getSecondaryDevicesFor(primaryDevicePubKey) { return map(rows, row => row.secondaryDevicePubKey); } +async function getPrimaryDeviceFor(secondaryDevicePubKey) { + const row = await db.get( + `SELECT primaryDevicePubKey FROM ${PAIRING_AUTHORISATIONS_TABLE} WHERE secondaryDevicePubKey = $secondaryDevicePubKey AND isGranted = 1;`, + { + $secondaryDevicePubKey: secondaryDevicePubKey, + } + ); + + if (!row) { + return null; + } + + return row.primaryDevicePubKey; +} + +// Return all the paired pubkeys for a specific pubkey (excluded), +// irrespective of their Primary or Secondary status. +async function getPairedDevicesFor(pubKey) { + let results = []; + + // get primary pubkey (only works if the pubkey is a secondary pubkey) + const primaryPubKey = await getPrimaryDeviceFor(pubKey); + if (primaryPubKey) { + results.push(primaryPubKey); + } + // get secondary pubkeys (only works if the pubkey is a primary pubkey) + const secondaryPubKeys = await getSecondaryDevicesFor( + primaryPubKey || pubKey + ); + results = results.concat(secondaryPubKeys); + + // ensure the input pubkey is not in the results + results = results.filter(x => x !== pubKey); + + return results; +} + const ITEMS_TABLE = 'items'; async function createOrUpdateItem(data) { return createOrUpdate(ITEMS_TABLE, data); diff --git a/js/modules/data.d.ts b/js/modules/data.d.ts index 1cec9dcb9..29e42df98 100644 --- a/js/modules/data.d.ts +++ b/js/modules/data.d.ts @@ -1,2 +1,3 @@ export function searchMessages(query: string): Promise>; export function searchConversations(query: string): Promise>; +export function getPrimaryDeviceFor(pubKey: string): Promise; diff --git a/js/modules/data.js b/js/modules/data.js index b5b1e72be..637fd7bb7 100644 --- a/js/modules/data.js +++ b/js/modules/data.js @@ -93,6 +93,8 @@ module.exports = { getGrantAuthorisationForPubKey, getAuthorisationForPubKey, getSecondaryDevicesFor, + getPrimaryDeviceFor, + getPairedDevicesFor, createOrUpdateItem, getItemById, @@ -627,8 +629,16 @@ async function getAuthorisationForPubKey(pubKey) { }; } -function getSecondaryDevicesFor(primareyDevicePubKey) { - return channels.getSecondaryDevicesFor(primareyDevicePubKey); +function getSecondaryDevicesFor(primaryDevicePubKey) { + return channels.getSecondaryDevicesFor(primaryDevicePubKey); +} + +function getPrimaryDeviceFor(secondaryDevicePubKey) { + return channels.getPrimaryDeviceFor(secondaryDevicePubKey); +} + +function getPairedDevicesFor(pubKey) { + return channels.getPairedDevicesFor(pubKey); } // Items diff --git a/ts/components/MainHeader.tsx b/ts/components/MainHeader.tsx index aa173e3e8..59daac48d 100644 --- a/ts/components/MainHeader.tsx +++ b/ts/components/MainHeader.tsx @@ -13,6 +13,7 @@ import { ContactName } from './conversation/ContactName'; import { cleanSearchTerm } from '../util/cleanSearchTerm'; import { LocalizerType } from '../types/Util'; +import { SearchOptions } from '../types/Search'; import { clipboard } from 'electron'; import { validateNumber } from '../types/PhoneNumber'; @@ -37,18 +38,11 @@ export interface Props { verified: boolean; profileName?: string; avatarPath?: string; - isSecondaryDevice?: boolean; + isSecondaryDevice: boolean; i18n: LocalizerType; updateSearchTerm: (searchTerm: string) => void; - search: ( - query: string, - options: { - regionCode: string; - ourNumber: string; - noteToSelf: string; - } - ) => void; + search: (query: string, options: SearchOptions) => void; clearSearch: () => void; onClick?: () => void; @@ -110,12 +104,20 @@ export class MainHeader extends React.Component { } public search() { - const { searchTerm, search, i18n, ourNumber, regionCode } = this.props; + const { + searchTerm, + search, + i18n, + ourNumber, + regionCode, + isSecondaryDevice, + } = this.props; if (search) { search(searchTerm, { noteToSelf: i18n('noteToSelf').toLowerCase(), ourNumber, regionCode, + isSecondaryDevice, }); } } diff --git a/ts/state/ducks/search.ts b/ts/state/ducks/search.ts index 99c18cb27..ffd4e85e0 100644 --- a/ts/state/ducks/search.ts +++ b/ts/state/ducks/search.ts @@ -1,10 +1,12 @@ import { omit, reject } from 'lodash'; import { normalize } from '../../types/PhoneNumber'; +import { SearchOptions } from '../../types/Search'; import { trigger } from '../../shims/events'; // import { getMessageModel } from '../../shims/Whisper'; // import { cleanSearchTerm } from '../../util/cleanSearchTerm'; import { + getPrimaryDeviceFor, searchConversations /*, searchMessages */, } from '../../../js/modules/data'; import { makeLookup } from '../../util/makeLookup'; @@ -81,7 +83,7 @@ export const actions = { function search( query: string, - options: { regionCode: string; ourNumber: string; noteToSelf: string } + options: SearchOptions ): SearchResultsKickoffActionType { return { type: 'SEARCH_RESULTS', @@ -91,16 +93,12 @@ function search( async function doSearch( query: string, - options: { - regionCode: string; - ourNumber: string; - noteToSelf: string; - } + options: SearchOptions ): Promise { - const { regionCode, ourNumber, noteToSelf } = options; + const { regionCode } = options; const [discussions /*, messages */] = await Promise.all([ - queryConversationsAndContacts(query, { ourNumber, noteToSelf }), + queryConversationsAndContacts(query, options), // queryMessages(query), ]); const { conversations, contacts } = discussions; @@ -170,23 +168,46 @@ function startNewConversation( async function queryConversationsAndContacts( providedQuery: string, - options: { ourNumber: string; noteToSelf: string } + options: SearchOptions ) { - const { ourNumber, noteToSelf } = options; + const { ourNumber, noteToSelf, isSecondaryDevice } = options; const query = providedQuery.replace(/[+-.()]*/g, ''); const searchResults: Array = await searchConversations( query ); + const ourPrimaryDevice = isSecondaryDevice + ? await getPrimaryDeviceFor(ourNumber) + : ourNumber; + + const resultPrimaryDevices: Array = await Promise.all( + searchResults.map( + async conversation => + conversation.id === ourPrimaryDevice + ? Promise.resolve(ourPrimaryDevice) + : getPrimaryDeviceFor(conversation.id) + ) + ); + // Split into two groups - active conversations and items just from address book let conversations: Array = []; let contacts: Array = []; const max = searchResults.length; for (let i = 0; i < max; i += 1) { const conversation = searchResults[i]; - - if (conversation.type === 'direct' && !Boolean(conversation.lastMessage)) { + const primaryDevice = resultPrimaryDevices[i]; + + if (primaryDevice) { + if (isSecondaryDevice && primaryDevice === ourPrimaryDevice) { + conversations.push(ourNumber); + } else { + conversations.push(primaryDevice); + } + } else if ( + conversation.type === 'direct' && + !Boolean(conversation.lastMessage) + ) { contacts.push(conversation.id); } else { conversations.push(conversation.id); diff --git a/ts/types/Search.ts b/ts/types/Search.ts new file mode 100644 index 000000000..debd559a0 --- /dev/null +++ b/ts/types/Search.ts @@ -0,0 +1,6 @@ +export type SearchOptions = { + regionCode: string; + ourNumber: string; + noteToSelf: string; + isSecondaryDevice: boolean; +};