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,7 +3,11 @@ 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'; +export type DeviceTokens = { + +[keyserverID: string]: ?string, +}; const setDeviceTokenActionTypes = Object.freeze({ started: 'SET_DEVICE_TOKEN_STARTED', success: 'SET_DEVICE_TOKEN_SUCCESS', @@ -11,14 +15,38 @@ }); 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 input; + }; + +const setDeviceTokenFanout = + ( + callKeyserverEndpoint: CallKeyserverEndpoint, + allKeyserverIDs: $ReadOnlyArray, + ): ((input: ?string) => Promise) => + async input => { + const requests = {}; + const result = {}; + for (const keyserverID of allKeyserverIDs) { + requests[keyserverID] = { + deviceToken: input, + platformDetails: getConfig().platformDetails, + }; + result[keyserverID] = input; + } + + await callKeyserverEndpoint('update_device_token', requests); + return result; }; const getVersionActionTypes = Object.freeze({ @@ -41,6 +69,7 @@ export { setDeviceTokenActionTypes, setDeviceToken, + setDeviceTokenFanout, 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[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 { DeviceTokens } 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: DeviceTokens, +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,13 @@ loadingInfo: action.loadingInfo, }: any); } else if (action.type === setDeviceTokenActionTypes.success) { + const payload: { [keyserverID: string]: ?string } = {}; + for (const keyserverID in action.payload) { + payload[keyserverID] = 'FAKE'; + } return { type: 'SET_DEVICE_TOKEN_SUCCESS', - payload: 'FAKE', + payload: payload, 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,11 @@ import { Notification as InAppNotification } from 'react-native-in-app-message'; import { useDispatch } from 'react-redux'; +import type { DeviceTokens } from 'lib/actions/device-actions.js'; import { setDeviceTokenActionTypes, setDeviceToken, + setDeviceTokenFanout, } from 'lib/actions/device-actions.js'; import { saveMessagesActionType } from 'lib/actions/message-actions.js'; import { @@ -29,10 +31,10 @@ 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'; +import { useKeyserverCall } from 'lib/utils/keyserver-call.js'; import { convertNotificationMessageInfoToNewIDSchema, convertNonPendingIDToNewSchema, @@ -110,7 +112,8 @@ +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 +243,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); } } @@ -436,23 +441,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 +476,7 @@ failedToRegisterPushPermissionsAndroid = ( shouldShowAlertOnAndroid: boolean, ) => { - this.setDeviceToken(null); + this.setAllDeviceTokensNull(); if (!this.props.loggedIn) { return; } @@ -679,7 +693,8 @@ const navigateToThread = useNavigateToThread(); const dispatch = useDispatch(); const dispatchActionPromise = useDispatchActionPromise(); - const boundSetDeviceToken = useServerCall(setDeviceToken); + const callSetDeviceToken = useKeyserverCall(setDeviceToken); + const callSetDeviceTokenFanout = useKeyserverCall(setDeviceTokenFanout); 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/modals/history/history-modal.react.js b/web/modals/history/history-modal.react.js --- a/web/modals/history/history-modal.react.js +++ b/web/modals/history/history-modal.react.js @@ -218,10 +218,12 @@ const revisions = _unionBy('id')(result)(prevState.revisions); return { ...prevState, revisions }; }); + const text: string = result[0].text; + const deleted: boolean = result[0].deleted; return { entryID, - text: result[0].text, - deleted: result[0].deleted, + text, + deleted, }; } 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,13 @@ import { useDispatch } from 'react-redux'; import { - setDeviceToken, + setDeviceTokenFanout, 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 { useKeyserverCall } from 'lib/utils/keyserver-call.js'; import { convertNonPendingIDToNewSchema } from 'lib/utils/migration-utils.js'; import { shouldSkipPushPermissionAlert, @@ -27,7 +25,7 @@ function useCreateDesktopPushSubscription() { const dispatchActionPromise = useDispatchActionPromise(); - const callSetDeviceToken = useServerCall(setDeviceToken); + const callSetDeviceToken = useKeyserverCall(setDeviceTokenFanout); React.useEffect( () => @@ -66,7 +64,7 @@ const publicKey = useSelector(state => state.pushApiPublicKey); const dispatchActionPromise = useDispatchActionPromise(); - const callSetDeviceToken = useServerCall(setDeviceToken); + const callSetDeviceToken = useKeyserverCall(setDeviceTokenFanout); return React.useCallback(async () => { if (!publicKey) {