diff --git a/lib/actions/device-actions.js b/lib/actions/device-actions.js --- a/lib/actions/device-actions.js +++ b/lib/actions/device-actions.js @@ -3,6 +3,15 @@ import type { VersionResponse } from '../types/device-types.js'; import type { CallServerEndpoint } from '../utils/call-server-endpoint.js'; import { getConfig } from '../utils/config.js'; +import type { CallKeyserverEndpoint } from '../utils/keyserver-call.js'; +import { useKeyserverCall } from '../utils/keyserver-call.js'; + +export type DeviceTokens = { + +[keyserverID: string]: ?string, +}; +export type SetDeviceTokenActionPayload = { + +deviceTokens: DeviceTokens, +}; const setDeviceTokenActionTypes = Object.freeze({ started: 'SET_DEVICE_TOKEN_STARTED', @@ -11,16 +20,52 @@ }); const setDeviceToken = ( - callServerEndpoint: CallServerEndpoint, - ): ((deviceToken: ?string) => Promise) => - async deviceToken => { - await callServerEndpoint('update_device_token', { - deviceToken, - platformDetails: getConfig().platformDetails, - }); - return deviceToken; + callKeyserverEndpoint: CallKeyserverEndpoint, + ): ((input: DeviceTokens) => Promise) => + async input => { + const requests = {}; + for (const keyserverID in input) { + requests[keyserverID] = { + deviceToken: input[keyserverID], + platformDetails: getConfig().platformDetails, + }; + } + await callKeyserverEndpoint('update_device_token', requests); + return { deviceTokens: input }; }; +function useSetDeviceToken(): ( + input: DeviceTokens, +) => Promise { + return useKeyserverCall(setDeviceToken); +} + +const setDeviceTokenFanout = + ( + callKeyserverEndpoint: CallKeyserverEndpoint, + allKeyserverIDs: $ReadOnlyArray, + ): ((input: ?string) => Promise) => + async input => { + const requests = {}; + const deviceTokens = {}; + for (const keyserverID of allKeyserverIDs) { + requests[keyserverID] = { + deviceToken: input, + platformDetails: getConfig().platformDetails, + }; + deviceTokens[keyserverID] = input; + } + + await callKeyserverEndpoint('update_device_token', requests); + return { deviceTokens }; + }; + +function useSetDeviceTokenFanout(): ( + input: ?string, +) => Promise { + return useKeyserverCall(setDeviceTokenFanout); +} + const getVersionActionTypes = Object.freeze({ started: 'GET_VERSION_STARTED', success: 'GET_VERSION_SUCCESS', @@ -40,7 +85,8 @@ export { setDeviceTokenActionTypes, - setDeviceToken, + useSetDeviceToken, + useSetDeviceTokenFanout, getVersionActionTypes, getVersion, updateLastCommunicatedPlatformDetailsActionType, diff --git a/lib/reducers/device-token-reducer.js b/lib/reducers/device-token-reducer.js --- a/lib/reducers/device-token-reducer.js +++ b/lib/reducers/device-token-reducer.js @@ -4,10 +4,11 @@ import type { BaseAction } from '../types/redux-types'; import { incrementalStateSyncActionType } from '../types/socket-types.js'; import { updateTypes } from '../types/update-types-enum.js'; +import { ashoatKeyserverID } from '../utils/validation-utils.js'; function reduceDeviceToken(state: ?string, action: BaseAction): ?string { if (action.type === setDeviceTokenActionTypes.success) { - return action.payload; + return action.payload.deviceTokens[ashoatKeyserverID]; } if (action.type === incrementalStateSyncActionType) { for (const update of action.payload.updatesResult.newUpdates) { diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js --- a/lib/types/redux-types.js +++ b/lib/types/redux-types.js @@ -114,6 +114,7 @@ } from './thread-types.js'; import type { ClientUpdatesResultWithUserInfos } from './update-types.js'; import type { CurrentUserInfo, UserStore } from './user-types.js'; +import type { SetDeviceTokenActionPayload } from '../actions/device-actions.js'; import type { Shape } from '../types/core.js'; import type { NotifPermissionAlertInfo } from '../utils/push-alerts.js'; @@ -682,7 +683,7 @@ } | { +type: 'SET_DEVICE_TOKEN_SUCCESS', - +payload: ?string, + +payload: SetDeviceTokenActionPayload, +loadingInfo: LoadingInfo, } | { diff --git a/lib/utils/sanitization.js b/lib/utils/sanitization.js --- a/lib/utils/sanitization.js +++ b/lib/utils/sanitization.js @@ -273,9 +273,15 @@ loadingInfo: action.loadingInfo, }: any); } else if (action.type === setDeviceTokenActionTypes.success) { + const deviceTokens: { [keyserverID: string]: ?string } = {}; + for (const keyserverID in action.payload.deviceTokens) { + deviceTokens[keyserverID] = 'FAKE'; + } return { type: 'SET_DEVICE_TOKEN_SUCCESS', - payload: 'FAKE', + payload: { + deviceTokens, + }, loadingInfo: action.loadingInfo, }; } diff --git a/native/push/push-handler.react.js b/native/push/push-handler.react.js --- a/native/push/push-handler.react.js +++ b/native/push/push-handler.react.js @@ -7,9 +7,14 @@ import { Notification as InAppNotification } from 'react-native-in-app-message'; import { useDispatch } from 'react-redux'; +import type { + DeviceTokens, + SetDeviceTokenActionPayload, +} from 'lib/actions/device-actions.js'; import { setDeviceTokenActionTypes, - setDeviceToken, + useSetDeviceToken, + useSetDeviceTokenFanout, } from 'lib/actions/device-actions.js'; import { saveMessagesActionType } from 'lib/actions/message-actions.js'; import { @@ -29,7 +34,6 @@ import type { GlobalTheme } from 'lib/types/theme-types.js'; import { type ThreadInfo } from 'lib/types/thread-types.js'; import { - useServerCall, useDispatchActionPromise, type DispatchActionPromise, } from 'lib/utils/action-utils.js'; @@ -110,7 +114,12 @@ +dispatch: Dispatch, +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs - +setDeviceToken: (deviceToken: ?string) => Promise, + +setDeviceToken: ( + input: DeviceTokens, + ) => Promise, + +setDeviceTokenFanout: ( + deviceToken: ?string, + ) => Promise, // withRootContext +rootContext: ?RootContextType, }; @@ -240,12 +249,14 @@ } else { // We do this in case there was a crash, so we can clear deviceToken from // any other cookies it might be set for + const deviceTokensMap = {}; for (const keyserverID in this.props.deviceTokens) { const deviceToken = this.props.deviceTokens[keyserverID]; if (deviceToken) { - this.setDeviceToken(deviceToken); + deviceTokensMap[keyserverID] = deviceToken; } } + this.setDeviceToken(deviceTokensMap); } } @@ -429,6 +440,7 @@ } registerPushPermissions = (deviceToken: ?string) => { + console.log('registerPushPermissions'); const deviceType = Platform.OS; if (deviceType !== 'android' && deviceType !== 'ios') { return; @@ -436,23 +448,32 @@ if (deviceType === 'ios') { iosPushPermissionResponseReceived(); } + const deviceTokensMap = {}; for (const keyserverID in this.props.deviceTokens) { const keyserverDeviceToken = this.props.deviceTokens[keyserverID]; if (deviceToken !== keyserverDeviceToken) { - this.setDeviceToken(deviceToken); + deviceTokensMap[keyserverID] = deviceToken; } } + this.setDeviceToken(deviceTokensMap); }; - setDeviceToken(deviceToken: ?string) { + setDeviceToken(deviceTokens: DeviceTokens) { this.props.dispatchActionPromise( setDeviceTokenActionTypes, - this.props.setDeviceToken(deviceToken), + this.props.setDeviceToken(deviceTokens), ); } + setAllDeviceTokensNull = () => { + this.props.dispatchActionPromise( + setDeviceTokenActionTypes, + this.props.setDeviceTokenFanout(null), + ); + }; + failedToRegisterPushPermissionsIOS = () => { - this.setDeviceToken(null); + this.setAllDeviceTokensNull(); if (!this.props.loggedIn) { return; } @@ -462,7 +483,7 @@ failedToRegisterPushPermissionsAndroid = ( shouldShowAlertOnAndroid: boolean, ) => { - this.setDeviceToken(null); + this.setAllDeviceTokensNull(); if (!this.props.loggedIn) { return; } @@ -679,7 +700,8 @@ const navigateToThread = useNavigateToThread(); const dispatch = useDispatch(); const dispatchActionPromise = useDispatchActionPromise(); - const boundSetDeviceToken = useServerCall(setDeviceToken); + const callSetDeviceToken = useSetDeviceToken(); + const callSetDeviceTokenFanout = useSetDeviceTokenFanout(); const rootContext = React.useContext(RootContext); return ( ); diff --git a/web/chat/reaction-message-utils.js b/web/chat/reaction-message-utils.js --- a/web/chat/reaction-message-utils.js +++ b/web/chat/reaction-message-utils.js @@ -12,6 +12,7 @@ import { messageTypes } from 'lib/types/message-types-enum.js'; import type { RawReactionMessageInfo } from 'lib/types/messages/reaction.js'; import { useDispatchActionPromise } from 'lib/utils/action-utils.js'; +import type { CallServerEndpointResultInfoInterface } from 'lib/utils/call-server-endpoint.js'; import { cloneError } from 'lib/utils/errors.js'; import Alert from '../modals/alert.react.js'; @@ -59,12 +60,16 @@ reaction, action, }); + const serverID: string = result.id; + const time: number = result.time; + const interfaceInfo: CallServerEndpointResultInfoInterface = + result.interface; return { localID, - serverID: result.id, + serverID, threadID, - time: result.time, - interface: result.interface, + time, + interface: interfaceInfo, }; } catch (e) { pushModal( diff --git a/web/push-notif/push-notifs-handler.js b/web/push-notif/push-notifs-handler.js --- a/web/push-notif/push-notifs-handler.js +++ b/web/push-notif/push-notifs-handler.js @@ -4,15 +4,12 @@ import { useDispatch } from 'react-redux'; import { - setDeviceToken, + useSetDeviceTokenFanout, setDeviceTokenActionTypes, } from 'lib/actions/device-actions.js'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import { isLoggedIn } from 'lib/selectors/user-selectors.js'; -import { - useDispatchActionPromise, - useServerCall, -} from 'lib/utils/action-utils.js'; +import { useDispatchActionPromise } from 'lib/utils/action-utils.js'; import { convertNonPendingIDToNewSchema } from 'lib/utils/migration-utils.js'; import { shouldSkipPushPermissionAlert, @@ -27,7 +24,7 @@ function useCreateDesktopPushSubscription() { const dispatchActionPromise = useDispatchActionPromise(); - const callSetDeviceToken = useServerCall(setDeviceToken); + const callSetDeviceToken = useSetDeviceTokenFanout(); React.useEffect( () => @@ -66,7 +63,7 @@ const publicKey = useSelector(state => state.pushApiPublicKey); const dispatchActionPromise = useDispatchActionPromise(); - const callSetDeviceToken = useServerCall(setDeviceToken); + const callSetDeviceToken = useSetDeviceTokenFanout(); return React.useCallback(async () => { if (!publicKey) {