diff --git a/lib/components/farcaster-data-handler.react.js b/lib/components/farcaster-data-handler.react.js --- a/lib/components/farcaster-data-handler.react.js +++ b/lib/components/farcaster-data-handler.react.js @@ -15,6 +15,7 @@ useCurrentUserFID, useUnlinkFID, useSetLocalFID, + useSetLocalCurrentUserSupportsDCs, } from '../utils/farcaster-utils.js'; import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; import { useSelector, useDispatch } from '../utils/redux-utils.js'; @@ -157,6 +158,8 @@ const [fidLoaded, setFIDLoaded] = React.useState(false); const setLocalFID = useSetLocalFID(); + const setLocalDCsSupport = useSetLocalCurrentUserSupportsDCs(); + const handleCurrentUserFID = React.useCallback(async () => { if ( canQueryHandleCurrentUserFID === @@ -178,6 +181,10 @@ ? false : getCachedUserIdentity(currentUserID) === undefined; + if (userIdentities[currentUserID]) { + setLocalDCsSupport(userIdentities[currentUserID].hasFarcasterDCsToken); + } + if (fid && !identityFIDRequestTimedOut && fid !== identityFID) { setLocalFID(identityFID); return; @@ -204,6 +211,7 @@ unlinkFID, setLocalFID, getCachedUserIdentity, + setLocalDCsSupport, ]); React.useEffect(() => { diff --git a/lib/tunnelbroker/use-peer-to-peer-message-handler.js b/lib/tunnelbroker/use-peer-to-peer-message-handler.js --- a/lib/tunnelbroker/use-peer-to-peer-message-handler.js +++ b/lib/tunnelbroker/use-peer-to-peer-message-handler.js @@ -50,6 +50,10 @@ import { getConfig } from '../utils/config.js'; import { getContentSigningKey } from '../utils/crypto-utils.js'; import { getMessageForException } from '../utils/errors.js'; +import { + useSetLocalCurrentUserSupportsDCs, + useSetLocalFID, +} from '../utils/farcaster-utils.js'; import { hasHigherDeviceID, OLM_ERROR_FLAG, @@ -203,6 +207,9 @@ const broadcastDeviceListUpdates = useBroadcastDeviceListUpdates(); const getAndUpdateDeviceListsForUsers = useGetAndUpdateDeviceListsForUsers(); + const setLocalFID = useSetLocalFID(); + const setLocalDCsSupport = useSetLocalCurrentUserSupportsDCs(); + const dispatch = useDispatch(); const handleOlmMessageToDevice = useHandleOlmMessageToDevice(); @@ -539,6 +546,11 @@ deviceToken: message.invalidatedToken, }, }); + } else if ( + message.type === peerToPeerMessageTypes.FARCASTER_CONNECTION_UPDATED + ) { + setLocalFID(message.farcasterID); + setLocalDCsSupport(message.hasDCsToken); } }, [ @@ -554,6 +566,8 @@ olmAPI, olmDebugLog, resendPeerToPeerMessages, + setLocalDCsSupport, + setLocalFID, sqliteAPI, ], ); diff --git a/lib/types/tunnelbroker/peer-to-peer-message-types.js b/lib/types/tunnelbroker/peer-to-peer-message-types.js --- a/lib/types/tunnelbroker/peer-to-peer-message-types.js +++ b/lib/types/tunnelbroker/peer-to-peer-message-types.js @@ -28,6 +28,7 @@ MESSAGE_PROCESSED: 'MessageProcessed', IDENTITY_DEVICE_LIST_UPDATED: 'IdentityDeviceListUpdated', BAD_DEVICE_TOKEN: 'BadDeviceToken', + FARCASTER_CONNECTION_UPDATED: 'FarcasterConnectionUpdated', }); export type OutboundSessionCreation = { @@ -120,6 +121,18 @@ invalidatedToken: t.String, }); +export type FarcasterConnectionUpdated = { + +type: 'FarcasterConnectionUpdated', + +farcasterID: ?string, + +hasDCsToken: ?boolean, +}; +export const farcasterConnectionUpdatedValidator: TInterface = + tShape({ + type: tString('FarcasterConnectionUpdated'), + farcasterID: t.maybe(t.String), + hasDCsToken: t.maybe(t.Boolean), + }); + export type PeerToPeerMessage = | OutboundSessionCreation | EncryptedMessage @@ -128,7 +141,8 @@ | DeviceListUpdated | MessageProcessed | IdentityDeviceListUpdated - | BadDeviceToken; + | BadDeviceToken + | FarcasterConnectionUpdated; export const peerToPeerMessageValidator: TUnion = t.union([ outboundSessionCreationValidator, @@ -139,4 +153,5 @@ messageProcessedValidator, identityDeviceListUpdatedValidator, badDeviceTokenValidator, + farcasterConnectionUpdatedValidator, ]); diff --git a/lib/utils/farcaster-utils.js b/lib/utils/farcaster-utils.js --- a/lib/utils/farcaster-utils.js +++ b/lib/utils/farcaster-utils.js @@ -3,10 +3,13 @@ import invariant from 'invariant'; import * as React from 'react'; +import { getContentSigningKey } from './crypto-utils.js'; import { useSelector, useDispatch } from './redux-utils.js'; import { setSyncedMetadataEntryActionType } from '../actions/synced-metadata-actions.js'; import { useUserIdentityCache } from '../components/user-identity-cache.react.js'; +import { getOwnPeerDevices } from '../selectors/user-selectors.js'; import { IdentityClientContext } from '../shared/identity-client-context.js'; +import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js'; import { syncedMetadataNames } from '../types/synced-metadata-types.js'; const DISABLE_CONNECT_FARCASTER_ALERT = false; @@ -108,13 +111,16 @@ const { linkFarcasterAccount } = identityClient; const setLocalFID = useSetLocalFID(); + const broadcastConnectionStatus = + useBroadcastUpdateFarcasterConnectionStatus(); return React.useCallback( async (fid: string) => { await linkFarcasterAccount(fid); setLocalFID(fid); + await broadcastConnectionStatus(fid, null); }, - [setLocalFID, linkFarcasterAccount], + [linkFarcasterAccount, setLocalFID, broadcastConnectionStatus], ); } @@ -127,12 +133,20 @@ const setLocalFID = useSetLocalFID(); const setLocalDCsSupport = useSetLocalCurrentUserSupportsDCs(); + const broadcastConnectionStatus = + useBroadcastUpdateFarcasterConnectionStatus(); return React.useCallback(async () => { await unlinkFarcasterAccount(); setLocalFID(null); setLocalDCsSupport(null); - }, [setLocalFID, setLocalDCsSupport, unlinkFarcasterAccount]); + await broadcastConnectionStatus(null, null); + }, [ + unlinkFarcasterAccount, + setLocalFID, + setLocalDCsSupport, + broadcastConnectionStatus, + ]); } function useLinkFarcasterDCs(): ( @@ -146,13 +160,42 @@ const { linkFarcasterDCsAccount } = identityClient; const setLocalDCsSupport = useSetLocalCurrentUserSupportsDCs(); + const broadcastConnectionStatus = + useBroadcastUpdateFarcasterConnectionStatus(); return React.useCallback( async (fid: string, farcasterDCsToken: string) => { await linkFarcasterDCsAccount(fid, farcasterDCsToken); setLocalDCsSupport(true); + await broadcastConnectionStatus(fid, true); }, - [setLocalDCsSupport, linkFarcasterDCsAccount], + [linkFarcasterDCsAccount, setLocalDCsSupport, broadcastConnectionStatus], + ); +} + +function useBroadcastUpdateFarcasterConnectionStatus() { + const { sendMessageToDevice } = useTunnelbroker(); + const userDeviceIDs = useSelector(getOwnPeerDevices).map( + peer => peer.deviceID, + ); + return React.useCallback( + async (farcasterID: ?string, hasDCsToken: ?boolean) => { + const thisDeviceID = await getContentSigningKey(); + const message = JSON.stringify({ + type: 'FarcasterConnectionUpdated', + farcasterID, + hasDCsToken, + }); + for (const deviceID of userDeviceIDs) { + if (deviceID !== thisDeviceID) { + void sendMessageToDevice({ + deviceID, + payload: message, + }); + } + } + }, + [sendMessageToDevice, userDeviceIDs], ); }