diff --git a/lib/handlers/user-infos-handler.react.js b/lib/handlers/user-infos-handler.react.js index c42f9d009..776880b0d 100644 --- a/lib/handlers/user-infos-handler.react.js +++ b/lib/handlers/user-infos-handler.react.js @@ -1,105 +1,95 @@ // @flow 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 { useGetAndUpdateDeviceListsForUsers } 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 { useDispatch, useSelector } from '../utils/redux-utils.js'; +import { useSelector } from '../utils/redux-utils.js'; import { relyingOnAuthoritativeKeyserver, usingCommServicesAccessToken, } from '../utils/services-utils.js'; function UserInfosHandler(): React.Node { const userInfos = useSelector(state => state.userStore.userInfos); const userInfosWithMissingUsernames = React.useMemo(() => { const entriesWithoutUsernames = Object.entries(userInfos).filter( ([, value]) => !value.username, ); return Object.fromEntries(entriesWithoutUsernames); }, [userInfos]); const dispatchActionPromise = useDispatchActionPromise(); const findUserIdentities = useFindUserIdentities(); const requestedIDsRef = React.useRef(new Set()); React.useEffect(() => { const newUserIDs = Object.keys(userInfosWithMissingUsernames).filter( id => !requestedIDsRef.current.has(id), ); if (!usingCommServicesAccessToken || newUserIDs.length === 0) { return; } const promise = (async () => { newUserIDs.forEach(id => requestedIDsRef.current.add(id)); // 1. Fetch usernames from identity const identities = await findUserIdentities(newUserIDs); // 2. Fetch avatars and settings from auth keyserver if (relyingOnAuthoritativeKeyserver) { // TODO } newUserIDs.forEach(id => requestedIDsRef.current.delete(id)); const newUserInfos = []; for (const id in identities) { newUserInfos.push({ id, username: identities[id].username, }); } return { userInfos: newUserInfos }; })(); void dispatchActionPromise(findUserIdentitiesActionTypes, promise); }, [ dispatchActionPromise, findUserIdentities, userInfos, userInfosWithMissingUsernames, ]); const usersWithMissingDeviceList = useSelector( usersWithMissingDeviceListSelector, ); - const getDeviceListsForUsers = useGetDeviceListsForUsers(); - const dispatch = useDispatch(); + const getAndUpdateDeviceListsForUsers = useGetAndUpdateDeviceListsForUsers(); 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 }, - }); + await getAndUpdateDeviceListsForUsers(usersWithMissingDeviceList); } catch (e) { console.log( - `Error setting peer device list: ${ + `Error getting and setting peer device list: ${ getMessageForException(e) ?? 'unknown' }`, ); } })(); - }, [dispatch, getDeviceListsForUsers, usersWithMissingDeviceList]); + }, [getAndUpdateDeviceListsForUsers, usersWithMissingDeviceList]); } export { UserInfosHandler }; diff --git a/lib/hooks/peer-list-hooks.js b/lib/hooks/peer-list-hooks.js index dc970115f..178bc2936 100644 --- a/lib/hooks/peer-list-hooks.js +++ b/lib/hooks/peer-list-hooks.js @@ -1,119 +1,141 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { setPeerDeviceListsActionType } from '../actions/aux-user-actions.js'; import { getRelativeUserIDs } from '../selectors/user-selectors.js'; import { IdentityClientContext } from '../shared/identity-client-context.js'; import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js'; import type { UsersRawDeviceLists, UsersDevicesPlatformDetails, SignedDeviceList, } from '../types/identity-service-types.js'; import { type DeviceListUpdated, peerToPeerMessageTypes, } from '../types/tunnelbroker/peer-to-peer-message-types.js'; import { convertSignedDeviceListsToRawDeviceLists } from '../utils/device-list-utils.js'; import { useDispatch, useSelector } from '../utils/redux-utils.js'; function useCreateInitialPeerList(): () => Promise { const dispatch = useDispatch(); const relativeUserIDs = useSelector(getRelativeUserIDs); const identityContext = React.useContext(IdentityClientContext); invariant(identityContext, 'Identity context should be set'); return React.useCallback(async () => { if (!identityContext) { return; } try { const peersDeviceLists = await identityContext.identityClient.getDeviceListsForUsers( relativeUserIDs, ); const usersRawDeviceLists = convertSignedDeviceListsToRawDeviceLists( peersDeviceLists.usersSignedDeviceLists, ); const usersPlatformDetails = peersDeviceLists.usersDevicesPlatformDetails; dispatch({ type: setPeerDeviceListsActionType, payload: { deviceLists: usersRawDeviceLists, usersPlatformDetails }, }); } catch (e) { console.log(`Error creating initial peer list: ${e.message}`); } }, [dispatch, identityContext, relativeUserIDs]); } 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], ); } +function useGetAndUpdateDeviceListsForUsers(): ( + userIDs: $ReadOnlyArray, +) => Promise { + const getDeviceListsForUsers = useGetDeviceListsForUsers(); + const dispatch = useDispatch(); + return React.useCallback( + async (userIDs: $ReadOnlyArray) => { + const { deviceLists, usersPlatformDetails } = + await getDeviceListsForUsers(userIDs); + if (Object.keys(deviceLists).length === 0) { + return; + } + dispatch({ + type: setPeerDeviceListsActionType, + payload: { deviceLists, usersPlatformDetails }, + }); + }, + [dispatch, getDeviceListsForUsers], + ); +} + function useBroadcastDeviceListUpdates(): ( deviceIDs: $ReadOnlyArray, signedDeviceList?: SignedDeviceList, ) => Promise { const { sendMessage } = useTunnelbroker(); const identityContext = React.useContext(IdentityClientContext); invariant(identityContext, 'identity context not set'); return React.useCallback( async ( deviceIDs: $ReadOnlyArray, signedDeviceList?: SignedDeviceList, ) => { const { getAuthMetadata } = identityContext; const { userID } = await getAuthMetadata(); if (!userID) { throw new Error('missing auth metadata'); } const messageToPeer: DeviceListUpdated = { type: peerToPeerMessageTypes.DEVICE_LIST_UPDATED, userID, signedDeviceList, }; const payload = JSON.stringify(messageToPeer); const promises = deviceIDs.map((deviceID: string) => sendMessage({ deviceID, payload, }), ); await Promise.all(promises); }, [identityContext, sendMessage], ); } export { useCreateInitialPeerList, useGetDeviceListsForUsers, useBroadcastDeviceListUpdates, + useGetAndUpdateDeviceListsForUsers, };