diff --git a/lib/selectors/calendar-selectors.js b/lib/selectors/calendar-selectors.js --- a/lib/selectors/calendar-selectors.js +++ b/lib/selectors/calendar-selectors.js @@ -6,6 +6,7 @@ import SearchIndex from '../shared/search-index'; import { threadInFilterList } from '../shared/thread-utils'; import type { FilterThreadInfo } from '../types/filter-types'; +import { useResolvedThreadInfosObj } from '../utils/entity-helpers'; import { values } from '../utils/objects'; import { useSelector } from '../utils/redux-utils'; import { currentCalendarQuery } from './nav-selectors'; @@ -14,7 +15,8 @@ function useFilterThreadInfos( calendarActive: boolean, ): $ReadOnlyArray { - const threadInfos = useSelector(threadInfoSelector); + const unresolvedThreadInfos = useSelector(threadInfoSelector); + const threadInfos = useResolvedThreadInfosObj(unresolvedThreadInfos); const rawEntryInfos = useSelector(state => state.entryStore.entryInfos); const calendarQueryFunc = useSelector(currentCalendarQuery); diff --git a/lib/types/filter-types.js b/lib/types/filter-types.js --- a/lib/types/filter-types.js +++ b/lib/types/filter-types.js @@ -1,6 +1,6 @@ // @flow -import { type ThreadInfo } from './thread-types'; +import type { ResolvedThreadInfo } from './thread-types'; export const calendarThreadFilterTypes = Object.freeze({ THREAD_LIST: 'threads', @@ -29,6 +29,6 @@ }; export type FilterThreadInfo = { - +threadInfo: ThreadInfo, + +threadInfo: ResolvedThreadInfo, +numVisibleEntries: number, }; diff --git a/lib/types/thread-types.js b/lib/types/thread-types.js --- a/lib/types/thread-types.js +++ b/lib/types/thread-types.js @@ -221,6 +221,24 @@ +repliesCount: number, }; +export type ResolvedThreadInfo = { + +id: string, + +type: ThreadType, + +name: ?string, + +uiName: string, + +description: ?string, + +color: string, // hex, without "#" or "0x" + +creationTime: number, // millisecond timestamp + +parentThreadID: ?string, + +containingThreadID: ?string, + +community: ?string, + +members: $ReadOnlyArray, + +roles: { [id: string]: RoleInfo }, + +currentUser: ThreadCurrentUserInfo, + +sourceMessageID?: string, + +repliesCount: number, +}; + export type ServerMemberInfo = { +id: string, +role: ?string, diff --git a/lib/utils/entity-helpers.js b/lib/utils/entity-helpers.js new file mode 100644 --- /dev/null +++ b/lib/utils/entity-helpers.js @@ -0,0 +1,73 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; + +import type { ThreadInfo, ResolvedThreadInfo } from '../types/thread-types'; +import { values } from '../utils/objects'; +import { + ET, + useENSNamesForEntityText, + entityTextToRawString, +} from './entity-text'; + +function useResolvedThreadInfos( + threadInfos: $ReadOnlyArray, +): $ReadOnlyArray { + const entityText = React.useMemo( + () => + threadInfos.map(threadInfo => + ET.thread({ display: 'uiName', threadInfo }), + ), + [threadInfos], + ); + const withENSNames = useENSNamesForEntityText(entityText); + invariant( + withENSNames, + 'useENSNamesForEntityText only returns falsey when passed falsey', + ); + return React.useMemo( + () => + threadInfos.map((threadInfo, i) => { + if (typeof threadInfo.uiName === 'string') { + // Flow wants return { ...threadInfo, uiName: threadInfo.uiName } + // but that's wasteful and unneeded, so we any-cast here + return (threadInfo: any); + } + const resolvedThreadEntity = withENSNames[i]; + return { + ...threadInfo, + uiName: entityTextToRawString([resolvedThreadEntity]), + }; + }), + [threadInfos, withENSNames], + ); +} + +function useResolvedThreadInfosObj(threadInfosObj: { + +[id: string]: ThreadInfo, +}): { +[id: string]: ResolvedThreadInfo } { + const threadInfosArray = React.useMemo(() => values(threadInfosObj), [ + threadInfosObj, + ]); + const resolvedThreadInfosArray = useResolvedThreadInfos(threadInfosArray); + return React.useMemo(() => { + const obj = {}; + for (const resolvedThreadInfo of resolvedThreadInfosArray) { + obj[resolvedThreadInfo.id] = resolvedThreadInfo; + } + return obj; + }, [resolvedThreadInfosArray]); +} + +function useResolvedThreadInfo(threadInfo: ThreadInfo): ResolvedThreadInfo { + const resolutionInput = React.useMemo(() => [threadInfo], [threadInfo]); + const [resolvedThreadInfo] = useResolvedThreadInfos(resolutionInput); + return resolvedThreadInfo; +} + +export { + useResolvedThreadInfos, + useResolvedThreadInfosObj, + useResolvedThreadInfo, +};