diff --git a/lib/handlers/user-infos-handler.react.js b/lib/handlers/user-infos-handler.react.js --- a/lib/handlers/user-infos-handler.react.js +++ b/lib/handlers/user-infos-handler.react.js @@ -2,12 +2,16 @@ import * as React from 'react'; +import { setPeerDeviceListsActionType } from '../actions/aux-user-actions.js'; import { useFindUserIdentities, findUserIdentitiesActionTypes, } from '../actions/user-actions.js'; +import { useGetDeviceListsForUsers } from '../hooks/peer-list-hooks.js'; +import { usersWithMissingDeviceListSelector } from '../selectors/user-selectors.js'; +import { getMessageForException } from '../utils/errors.js'; import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; -import { useSelector } from '../utils/redux-utils.js'; +import { useDispatch, useSelector } from '../utils/redux-utils.js'; import { relyingOnAuthoritativeKeyserver, usingCommServicesAccessToken, @@ -63,6 +67,39 @@ userInfos, userInfosWithMissingUsernames, ]); + + const usersWithMissingDeviceList = useSelector( + usersWithMissingDeviceListSelector, + ); + const getDeviceListsForUsers = useGetDeviceListsForUsers(); + const dispatch = useDispatch(); + React.useEffect(() => { + if ( + !usingCommServicesAccessToken || + usersWithMissingDeviceList.length === 0 + ) { + return; + } + void (async () => { + try { + const { deviceLists, usersPlatformDetails } = + await getDeviceListsForUsers(usersWithMissingDeviceList); + if (Object.keys(deviceLists).length === 0) { + return; + } + dispatch({ + type: setPeerDeviceListsActionType, + payload: { deviceLists, usersPlatformDetails }, + }); + } catch (e) { + console.log( + `Error setting peer device list: ${ + getMessageForException(e) ?? 'unknown' + }`, + ); + } + })(); + }, [dispatch, getDeviceListsForUsers, usersWithMissingDeviceList]); } export { UserInfosHandler }; diff --git a/lib/hooks/peer-list-hooks.js b/lib/hooks/peer-list-hooks.js --- a/lib/hooks/peer-list-hooks.js +++ b/lib/hooks/peer-list-hooks.js @@ -6,6 +6,10 @@ import { setPeerDeviceListsActionType } from '../actions/aux-user-actions.js'; import { getRelativeUserIDs } from '../selectors/user-selectors.js'; import { IdentityClientContext } from '../shared/identity-client-context.js'; +import type { + UsersRawDeviceLists, + UsersDevicesPlatformDetails, +} from '../types/identity-service-types.js'; import { convertSignedDeviceListsToRawDeviceLists } from '../utils/device-list-utils.js'; import { useDispatch, useSelector } from '../utils/redux-utils.js'; @@ -40,4 +44,28 @@ }, [dispatch, identityContext, relativeUserIDs]); } -export { useCreateInitialPeerList }; +function useGetDeviceListsForUsers(): ( + userIDs: $ReadOnlyArray, +) => Promise<{ + +deviceLists: UsersRawDeviceLists, + +usersPlatformDetails: UsersDevicesPlatformDetails, +}> { + const client = React.useContext(IdentityClientContext); + const identityClient = client?.identityClient; + invariant(identityClient, 'Identity client should be set'); + return React.useCallback( + async (userIDs: $ReadOnlyArray) => { + const peersDeviceLists = + await identityClient.getDeviceListsForUsers(userIDs); + return { + deviceLists: convertSignedDeviceListsToRawDeviceLists( + peersDeviceLists.usersSignedDeviceLists, + ), + usersPlatformDetails: peersDeviceLists.usersDevicesPlatformDetails, + }; + }, + [identityClient], + ); +} + +export { useCreateInitialPeerList, useGetDeviceListsForUsers }; diff --git a/lib/selectors/user-selectors.js b/lib/selectors/user-selectors.js --- a/lib/selectors/user-selectors.js +++ b/lib/selectors/user-selectors.js @@ -3,11 +3,13 @@ import _memoize from 'lodash/memoize.js'; import { createSelector } from 'reselect'; +import bots from '../facts/bots.js'; import { getAvatarForUser, getRandomDefaultEmojiAvatar, } from '../shared/avatar-utils.js'; import { getSingleOtherUser } from '../shared/thread-utils.js'; +import { type AuxUserInfos } from '../types/aux-user-types.js'; import type { ClientEmojiAvatar } from '../types/avatar-types'; import type { RelativeMemberInfo, @@ -194,8 +196,27 @@ }, ); -const getRelativeUserIDs = (state: BaseAppState<>): $ReadOnlyArray => - Object.keys(state.userStore.userInfos); +const getRelativeUserIDs: (state: BaseAppState<>) => $ReadOnlyArray = + createSelector( + (state: BaseAppState<>) => state.userStore.userInfos, + (userInfos: UserInfos): $ReadOnlyArray => Object.keys(userInfos), + ); + +const usersWithMissingDeviceListSelector: ( + state: BaseAppState<>, +) => $ReadOnlyArray = createSelector( + getRelativeUserIDs, + (state: BaseAppState<>) => state.auxUserStore.auxUserInfos, + ( + userIDs: $ReadOnlyArray, + auxUserInfos: AuxUserInfos, + ): $ReadOnlyArray => + userIDs.filter( + userID => + (!auxUserInfos[userID] || !auxUserInfos[userID].deviceList) && + userID !== bots.commbot.userID, + ), +); export { userIDsToRelativeUserInfos, @@ -207,4 +228,5 @@ usersWithPersonalThreadSelector, savedEmojiAvatarSelectorForCurrentUser, getRelativeUserIDs, + usersWithMissingDeviceListSelector, };