diff --git a/keyserver/src/fetchers/user-fetchers.js b/keyserver/src/fetchers/user-fetchers.js --- a/keyserver/src/fetchers/user-fetchers.js +++ b/keyserver/src/fetchers/user-fetchers.js @@ -26,7 +26,7 @@ import type { Viewer } from '../session/viewer.js'; async function fetchUserInfos( - userIDs: string[], + userIDs: $ReadOnlyArray, ): Promise<{ [id: string]: GlobalUserInfo }> { if (userIDs.length <= 0) { return {}; diff --git a/keyserver/src/responders/website-responders.js b/keyserver/src/responders/website-responders.js --- a/keyserver/src/responders/website-responders.js +++ b/keyserver/src/responders/website-responders.js @@ -44,6 +44,7 @@ import { ServerError } from 'lib/utils/errors.js'; import { promiseAll } from 'lib/utils/promises.js'; import { defaultNotifPermissionAlertInfo } from 'lib/utils/push-alerts.js'; +import { infoFromURL } from 'lib/utils/url-utils.js'; import { tBool, tNumber, tShape, tString } from 'lib/utils/validation-utils.js'; import getTitle from 'web/title/getTitle.js'; import { navInfoValidator } from 'web/types/nav-types.js'; @@ -56,6 +57,7 @@ import { fetchCurrentUserInfo, fetchKnownUserInfos, + fetchUserInfos, } from '../fetchers/user-fetchers.js'; import { getWebPushConfig } from '../push/providers.js'; import { setNewSession } from '../session/cookies.js'; @@ -258,20 +260,41 @@ baseLegalPolicies, ); - let initialNavInfo; - try { - initialNavInfo = navInfoFromURL(req.url, { - now: currentDateInTimeZone(viewer.timeZone), - }); - } catch (e) { - throw new ServerError(e.message); - } + const initialNavInfoPromise = (async () => { + try { + const urlInfo = infoFromURL(req.url); + + let backupInfo = { + now: currentDateInTimeZone(viewer.timeZone), + }; + // Some user ids in selectedUserList might not exist in the userStore + // (e.g. they were included in the results of the user search endpoint) + // Because of that we keep their userInfos inside the navInfo. + if (urlInfo.selectedUserList) { + const fetchedUserInfos = await fetchUserInfos(urlInfo.selectedUserList); + const userInfos = {}; + for (const userID in fetchedUserInfos) { + const userInfo = fetchedUserInfos[userID]; + if (userInfo.username) { + userInfos[userID] = userInfo; + } + } + backupInfo = { userInfos, ...backupInfo }; + } + return navInfoFromURL(urlInfo, backupInfo); + } catch (e) { + throw new ServerError(e.message); + } + })(); - const calendarQuery = { - startDate: initialNavInfo.startDate, - endDate: initialNavInfo.endDate, - filters: defaultCalendarFilters, - }; + const calendarQueryPromise = (async () => { + const initialNavInfo = await initialNavInfoPromise; + return { + startDate: initialNavInfo.startDate, + endDate: initialNavInfo.endDate, + filters: defaultCalendarFilters, + }; + })(); const messageSelectionCriteria = { joinedThreads: true }; const initialTime = Date.now(); @@ -282,11 +305,15 @@ messageSelectionCriteria, defaultNumberPerThread, ); - const entryInfoPromise = fetchEntryInfos(viewer, [calendarQuery]); + const entryInfoPromise = (async () => { + const calendarQuery = await calendarQueryPromise; + return await fetchEntryInfos(viewer, [calendarQuery]); + })(); const currentUserInfoPromise = fetchCurrentUserInfo(viewer); const userInfoPromise = fetchKnownUserInfos(viewer); const sessionIDPromise = (async () => { + const calendarQuery = await calendarQueryPromise; if (viewer.loggedIn) { await setNewSession(viewer, calendarQuery, initialTime); } @@ -356,14 +383,19 @@ })(); const navInfoPromise = (async () => { - const [{ threadInfos }, messageStore, currentUserInfo, userStore] = - await Promise.all([ - threadInfoPromise, - messageStorePromise, - currentUserInfoPromise, - userStorePromise, - ]); - const finalNavInfo = initialNavInfo; + const [ + { threadInfos }, + messageStore, + currentUserInfo, + userStore, + finalNavInfo, + ] = await Promise.all([ + threadInfoPromise, + messageStorePromise, + currentUserInfoPromise, + userStorePromise, + initialNavInfoPromise, + ]); const requestedActiveChatThreadID = finalNavInfo.activeChatThreadID; if ( @@ -509,10 +541,10 @@ windowDimensions: { width: 0, height: 0 }, baseHref, notifPermissionAlertInfo: defaultNotifPermissionAlertInfo, - connection: { + connection: (async () => ({ ...defaultConnectionInfo(viewer.platform ?? 'web', viewer.timeZone), - actualizedCalendarQuery: calendarQuery, - }, + actualizedCalendarQuery: await calendarQueryPromise, + }))(), watchedThreadIDs: [], lifecycleState: 'active', enabledApps: defaultWebEnabledApps, diff --git a/web/app.react.js b/web/app.react.js --- a/web/app.react.js +++ b/web/app.react.js @@ -28,6 +28,7 @@ import type { LoadingStatus } from 'lib/types/loading-types.js'; import type { Dispatch } from 'lib/types/redux-types.js'; import { registerConfig } from 'lib/utils/config.js'; +import { infoFromURL } from 'lib/utils/url-utils.js'; import { WagmiENSCacheProvider, wagmiClient } from 'lib/utils/wagmi-utils.js'; import Calendar from './calendar/calendar.react.js'; @@ -132,7 +133,8 @@ history.push(newURL); } } else if (pathname !== prevProps.location.pathname) { - const newNavInfo = navInfoFromURL(pathname, { navInfo }); + const urlInfo = infoFromURL(pathname); + const newNavInfo = navInfoFromURL(urlInfo, { navInfo }); if (!_isEqual(newNavInfo)(navInfo)) { this.props.dispatch({ type: updateNavInfoActionType, diff --git a/web/url-utils.js b/web/url-utils.js --- a/web/url-utils.js +++ b/web/url-utils.js @@ -3,11 +3,12 @@ import invariant from 'invariant'; import _keyBy from 'lodash/fp/keyBy.js'; +import type { AccountUserInfo } from 'lib/types/user-types.js'; import { startDateForYearAndMonth, endDateForYearAndMonth, } from 'lib/utils/date-utils.js'; -import { infoFromURL } from 'lib/utils/url-utils.js'; +import { infoFromURL, type URLInfo } from 'lib/utils/url-utils.js'; import { yearExtractor, monthExtractor } from './selectors/nav-selectors.js'; import type { NavInfo } from './types/nav-types.js'; @@ -77,10 +78,13 @@ // Given a URL, this function parses out a navInfo object, leaving values as // default if they are unspecified. function navInfoFromURL( - url: string, - backupInfo: { now?: Date, navInfo?: NavInfo }, + urlInfo: URLInfo, + backupInfo: { + +now?: Date, + +userInfos?: { +[id: string]: AccountUserInfo }, + +navInfo?: NavInfo, + }, ): NavInfo { - const urlInfo = infoFromURL(url); const { navInfo } = backupInfo; const now = backupInfo.now ? backupInfo.now : new Date(); @@ -129,8 +133,9 @@ if (urlInfo.selectedUserList) { const selectedUsers = _keyBy('id')(navInfo?.selectedUserList ?? []); + const userInfos = backupInfo.userInfos ?? {}; newNavInfo.selectedUserList = urlInfo.selectedUserList - ?.map(id => selectedUsers[id]) + ?.map(id => selectedUsers[id] ?? userInfos[id]) ?.filter(Boolean); }