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 @@ -21,15 +21,18 @@ return; } try { - const userDeviceLists = + const peersDeviceLists = await identityContext.identityClient.getDeviceListsForUsers( relativeUserIDs, ); - const usersRawDeviceLists = - convertSignedDeviceListsToRawDeviceLists(userDeviceLists); + const usersRawDeviceLists = convertSignedDeviceListsToRawDeviceLists( + peersDeviceLists.usersSignedDeviceLists, + ); + const usersPlatformDetails = peersDeviceLists.usersDevicesPlatformDetails; + dispatch({ type: setPeerDeviceListsActionType, - payload: { deviceLists: usersRawDeviceLists }, + payload: { deviceLists: usersRawDeviceLists, usersPlatformDetails }, }); } catch (e) { console.log(`Error creating initial peer list: ${e.message}`); diff --git a/lib/types/identity-service-types.js b/lib/types/identity-service-types.js --- a/lib/types/identity-service-types.js +++ b/lib/types/identity-service-types.js @@ -180,7 +180,7 @@ ) => Promise<$ReadOnlyArray>; +getDeviceListsForUsers: ( userIDs: $ReadOnlyArray, - ) => Promise; + ) => Promise; // updating device list is possible only on Native // web cannot be a primary device, so there's no need to expose it to JS +updateDeviceList?: (newDeviceList: SignedDeviceList) => Promise; @@ -342,3 +342,24 @@ stateVersion: t.maybe(t.Number), majorDesktopVersion: t.maybe(t.Number), }); + +export type UserDevicesPlatformDetails = { + +[deviceID: string]: IdentityPlatformDetails, +}; +export const userDevicesPlatformDetailsValidator: TDict = + t.dict(t.String, identityPlatformDetailsValidator); + +export type UsersDevicesPlatformDetails = { + +[userID: string]: UserDevicesPlatformDetails, +}; +export const usersDevicesPlatformDetailsValidator: TDict = + t.dict(t.String, userDevicesPlatformDetailsValidator); + +export type PeersDeviceLists = { + +usersSignedDeviceLists: UsersSignedDeviceLists, + +usersDevicesPlatformDetails: UsersDevicesPlatformDetails, +}; +export const peersDeviceListsValidator: TInterface = tShape({ + usersSignedDeviceLists: usersSignedDeviceListsValidator, + usersDevicesPlatformDetails: usersDevicesPlatformDetailsValidator, +}); diff --git a/native/identity-service/identity-service-context-provider.react.js b/native/identity-service/identity-service-context-provider.react.js --- a/native/identity-service/identity-service-context-provider.react.js +++ b/native/identity-service/identity-service-context-provider.react.js @@ -27,8 +27,9 @@ type UserDevicesOlmInboundKeys, type UserDevicesOlmOutboundKeys, type UsersSignedDeviceLists, - usersSignedDeviceListsValidator, identitiesValidator, + type UsersDevicesPlatformDetails, + peersDeviceListsValidator, } from 'lib/types/identity-service-types.js'; import { getContentSigningKey } from 'lib/utils/crypto-utils.js'; import { assertWithValidator } from 'lib/utils/validation-utils.js'; @@ -606,20 +607,26 @@ token, userIDs, ); - const rawPayloads: { +[userID: string]: string } = JSON.parse(result); + + const rawPayloads: { + +usersDeviceLists: { +[userID: string]: string }, + +usersDevicesPlatformDetails: UsersDevicesPlatformDetails, + } = JSON.parse(result); let usersDeviceLists: UsersSignedDeviceLists = {}; - for (const userID in rawPayloads) { + for (const userID in rawPayloads.usersDeviceLists) { usersDeviceLists = { ...usersDeviceLists, - [userID]: JSON.parse(rawPayloads[userID]), + [userID]: JSON.parse(rawPayloads.usersDeviceLists[userID]), }; } - return assertWithValidator( - usersDeviceLists, - usersSignedDeviceListsValidator, - ); + const peersDeviceLists = { + usersSignedDeviceLists: usersDeviceLists, + usersDevicesPlatformDetails: rawPayloads.usersDevicesPlatformDetails, + }; + + return assertWithValidator(peersDeviceLists, peersDeviceListsValidator); }, updateDeviceList: async (newDeviceList: SignedDeviceList) => { const { diff --git a/native/native_rust_library/src/identity/device_list.rs b/native/native_rust_library/src/identity/device_list.rs --- a/native/native_rust_library/src/identity/device_list.rs +++ b/native/native_rust_library/src/identity/device_list.rs @@ -1,15 +1,17 @@ use grpc_clients::identity::get_auth_client; use grpc_clients::identity::protos::auth::{ - GetDeviceListRequest, PeersDeviceListsRequest, UpdateDeviceListRequest, + GetDeviceListRequest, PeersDeviceListsRequest, PeersDeviceListsResponse, + UpdateDeviceListRequest, }; +use std::collections::HashMap; +use super::PLATFORM_METADATA; use crate::identity::AuthInfo; use crate::utils::jsi_callbacks::{ handle_string_result_as_callback, handle_void_result_as_callback, }; use crate::{Error, IDENTITY_SOCKET_ADDR, RUNTIME}; - -use super::PLATFORM_METADATA; +use serde::Serialize; pub mod ffi { use super::*; @@ -112,12 +114,39 @@ ) .await?; - let response = identity_client + let PeersDeviceListsResponse { + users_device_lists, + users_devices_platform_details, + } = identity_client .get_device_lists_for_users(PeersDeviceListsRequest { user_ids }) .await? .into_inner(); - let payload = serde_json::to_string(&response.users_device_lists)?; + let nested_users_devices_platform_details: HashMap< + String, + HashMap, + > = users_devices_platform_details + .into_iter() + .map(|(user_id, user_devices_platform_details)| { + ( + user_id, + user_devices_platform_details + .devices_platform_details + .into_iter() + .map(|(device_id, platform_details)| { + (device_id, platform_details.into()) + }) + .collect(), + ) + }) + .collect(); + + let result = PeersDeviceLists { + users_device_lists: users_device_lists, + users_devices_platform_details: nested_users_devices_platform_details, + }; + + let payload = serde_json::to_string(&result)?; Ok(payload) } @@ -142,3 +171,35 @@ Ok(()) } + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct PlatformDetails { + device_type: i32, + code_version: u64, + state_version: Option, + major_desktop_version: Option, +} + +impl From + for PlatformDetails +{ + fn from( + value: grpc_clients::identity::protos::auth::PlatformDetails, + ) -> Self { + Self { + device_type: value.device_type, + code_version: value.code_version, + state_version: value.state_version, + major_desktop_version: value.major_desktop_version, + } + } +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct PeersDeviceLists { + users_device_lists: HashMap, + users_devices_platform_details: + HashMap>, +} diff --git a/web/grpc/identity-service-client-wrapper.js b/web/grpc/identity-service-client-wrapper.js --- a/web/grpc/identity-service-client-wrapper.js +++ b/web/grpc/identity-service-client-wrapper.js @@ -29,9 +29,11 @@ type FarcasterUser, farcasterUsersValidator, type UsersSignedDeviceLists, - usersSignedDeviceListsValidator, type Identities, identitiesValidator, + type PeersDeviceLists, + peersDeviceListsValidator, + type IdentityPlatformDetails, } from 'lib/types/identity-service-types.js'; import { getMessageForException } from 'lib/utils/errors.js'; import { assertWithValidator } from 'lib/utils/validation-utils.js'; @@ -535,7 +537,7 @@ getDeviceListsForUsers: ( userIDs: $ReadOnlyArray, - ) => Promise = async userIDs => { + ) => Promise = async userIDs => { const client = this.authClient; if (!client) { throw new Error('Identity service client is not initialized'); @@ -544,6 +546,8 @@ request.setUserIdsList([...userIDs]); const response = await client.getDeviceListsForUsers(request); const rawPayloads = response.toObject()?.usersDeviceListsMap; + const rawUsersDevicesPlatformDetails = + response.toObject()?.usersDevicesPlatformDetailsMap; let usersDeviceLists: UsersSignedDeviceLists = {}; rawPayloads.forEach(([userID, rawPayload]) => { @@ -553,10 +557,25 @@ }; }); - return assertWithValidator( - usersDeviceLists, - usersSignedDeviceListsValidator, - ); + const usersDevicesPlatformDetails: { + [userID: string]: { +[deviceID: string]: IdentityPlatformDetails }, + } = {}; + + for (const [ + userID, + rawUserDevicesPlatformDetails, + ] of rawUsersDevicesPlatformDetails) { + usersDevicesPlatformDetails[userID] = Object.fromEntries( + rawUserDevicesPlatformDetails.devicesPlatformDetailsMap, + ); + } + + const peersDeviceLists = { + usersSignedDeviceLists: usersDeviceLists, + usersDevicesPlatformDetails, + }; + + return assertWithValidator(peersDeviceLists, peersDeviceListsValidator); }; getFarcasterUsers: (