Finish in-redux conversation lookups, getPropsForSearchResult moved

This commit is contained in:
Scott Nonnenberg 2021-01-06 07:41:43 -08:00
parent 7fe40dbf83
commit cbc6c29479
18 changed files with 901 additions and 146 deletions

View file

@ -1,4 +1,4 @@
// Copyright 2019-2020 Signal Messenger, LLC
// Copyright 2019-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import memoizee from 'memoizee';
@ -15,6 +15,7 @@ import {
MessagesByConversationType,
MessageType,
} from '../ducks/conversations';
import { getOwn } from '../../util/getOwn';
import type { CallsByConversationType } from '../ducks/calling';
import { getCallsByConversation } from './calling';
import { getBubbleProps } from '../../shims/Whisper';
@ -30,6 +31,20 @@ import {
} from './user';
import { getPinnedConversationIds } from './items';
let placeholderContact: ConversationType;
export const getPlaceholderContact = (): ConversationType => {
if (placeholderContact) {
return placeholderContact;
}
placeholderContact = {
id: 'placeholder-contact',
type: 'direct',
title: window.i18n('unknownContact'),
};
return placeholderContact;
};
export const getConversations = (state: StateType): ConversationsStateType =>
state.conversations;
@ -40,6 +55,27 @@ export const getConversationLookup = createSelector(
}
);
export const getConversationsByUuid = createSelector(
getConversations,
(state: ConversationsStateType): ConversationLookupType => {
return state.conversationsByUuid;
}
);
export const getConversationsByE164 = createSelector(
getConversations,
(state: ConversationsStateType): ConversationLookupType => {
return state.conversationsByE164;
}
);
export const getConversationsByGroupId = createSelector(
getConversations,
(state: ConversationsStateType): ConversationLookupType => {
return state.conversationsByGroupId;
}
);
export const getSelectedConversation = createSelector(
getConversations,
(state: ConversationsStateType): string | undefined => {
@ -201,17 +237,21 @@ export const getMe = createSelector(
// Backbone-based prop-generation functions expect to get Conversation information
// directly via ConversationController
export function _conversationSelector(
conversation: ConversationType
conversation?: ConversationType
// regionCode: string,
// userNumber: string
): ConversationType {
return conversation;
if (conversation) {
return conversation;
}
return getPlaceholderContact();
}
// A little optimization to reset our selector cache when high-level application data
// changes: regionCode and userNumber.
type CachedConversationSelectorType = (
conversation: ConversationType
conversation?: ConversationType
) => ConversationType;
export const getCachedSelectorForConversation = createSelector(
getRegionCode,
@ -223,23 +263,51 @@ export const getCachedSelectorForConversation = createSelector(
}
);
export type GetConversationByIdType = (
id: string
) => ConversationType | undefined;
export type GetConversationByIdType = (id?: string) => ConversationType;
export const getConversationSelector = createSelector(
getCachedSelectorForConversation,
getConversationLookup,
getConversationsByUuid,
getConversationsByE164,
getConversationsByGroupId,
(
selector: CachedConversationSelectorType,
lookup: ConversationLookupType
byId: ConversationLookupType,
byUuid: ConversationLookupType,
byE164: ConversationLookupType,
byGroupId: ConversationLookupType
): GetConversationByIdType => {
return (id: string) => {
const conversation = lookup[id];
if (!conversation) {
return undefined;
return (id?: string) => {
if (!id) {
window.log.warn(
`getConversationSelector: Called with a falsey id ${id}`
);
// This will return a placeholder contact
return selector(undefined);
}
return selector(conversation);
const onE164 = getOwn(byE164, id);
if (onE164) {
return selector(onE164);
}
const onUuid = getOwn(byUuid, id);
if (onUuid) {
return selector(onUuid);
}
const onGroupId = getOwn(byGroupId, id);
if (onGroupId) {
return selector(onGroupId);
}
const onId = getOwn(byId, id);
if (onId) {
return selector(onId);
}
window.log.warn(
`getConversationSelector: No conversation found for id ${id}`
);
// This will return a placeholder contact
return selector(undefined);
};
}
);

View file

@ -3,7 +3,6 @@
import memoizee from 'memoizee';
import { createSelector } from 'reselect';
import { getSearchResultsProps } from '../../shims/Whisper';
import { instance } from '../../util/libphonenumberInstance';
import { StateType } from '../reducer';
@ -24,7 +23,7 @@ import {
} from '../../components/SearchResults';
import { PropsDataType as MessageSearchResultPropsDataType } from '../../components/MessageSearchResult';
import { getRegionCode, getUserNumber } from './user';
import { getRegionCode, getUserConversationId } from './user';
import { getUserAgent } from './items';
import {
GetConversationByIdType,
@ -221,18 +220,21 @@ export const getSearchResults = createSelector(
export function _messageSearchResultSelector(
message: MessageSearchResultType,
_ourNumber: string,
_regionCode: string,
_sender?: ConversationType,
_recipient?: ConversationType,
from: ConversationType,
to: ConversationType,
searchConversationId?: string,
selectedMessageId?: string
): MessageSearchResultPropsDataType {
// Note: We don't use all of those parameters here, but the shim we call does.
// We want to call this function again if any of those parameters change.
return {
...getSearchResultsProps(message),
isSelected: message.id === selectedMessageId,
from,
to,
id: message.id,
conversationId: message.conversationId,
sentAt: message.sent_at,
snippet: message.snippet,
isSelected: Boolean(selectedMessageId && message.id === selectedMessageId),
isSearchingInConversation: Boolean(searchConversationId),
};
}
@ -241,16 +243,13 @@ export function _messageSearchResultSelector(
// changes: regionCode and userNumber.
type CachedMessageSearchResultSelectorType = (
message: MessageSearchResultType,
ourNumber: string,
regionCode: string,
sender?: ConversationType,
recipient?: ConversationType,
from: ConversationType,
to: ConversationType,
searchConversationId?: string,
selectedMessageId?: string
) => MessageSearchResultPropsDataType;
export const getCachedSelectorForMessageSearchResult = createSelector(
getRegionCode,
getUserNumber,
getUserConversationId,
(): CachedMessageSearchResultSelectorType => {
// Note: memoizee will check all parameters provided, and only run our selector
// if any of them have changed.
@ -267,43 +266,47 @@ export const getMessageSearchResultSelector = createSelector(
getSelectedMessage,
getConversationSelector,
getSearchConversationId,
getRegionCode,
getUserNumber,
getUserConversationId,
(
messageSearchResultSelector: CachedMessageSearchResultSelectorType,
messageSearchResultLookup: MessageSearchResultLookupType,
selectedMessage: string | undefined,
selectedMessageId: string | undefined,
conversationSelector: GetConversationByIdType,
searchConversationId: string | undefined,
regionCode: string,
ourNumber: string
ourConversationId: string
): GetMessageSearchResultByIdType => {
return (id: string) => {
const message = messageSearchResultLookup[id];
if (!message) {
window.log.warn(
`getMessageSearchResultSelector: messageSearchResultLookup was missing id ${id}`
);
return undefined;
}
const { conversationId, source, type } = message;
let sender: ConversationType | undefined;
let recipient: ConversationType | undefined;
const { conversationId, source, sourceUuid, type } = message;
let from: ConversationType;
let to: ConversationType;
if (type === 'incoming') {
sender = conversationSelector(source);
recipient = conversationSelector(ourNumber);
from = conversationSelector(sourceUuid || source);
to = conversationSelector(conversationId);
} else if (type === 'outgoing') {
sender = conversationSelector(ourNumber);
recipient = conversationSelector(conversationId);
from = conversationSelector(ourConversationId);
to = conversationSelector(conversationId);
} else {
window.log.warn(
`getMessageSearchResultSelector: Got unexpected type ${type}`
);
return undefined;
}
return messageSearchResultSelector(
message,
ourNumber,
regionCode,
sender,
recipient,
from,
to,
searchConversationId,
selectedMessage
selectedMessageId
);
};
}