signal-desktop/ts/state/ducks/network.ts
Jamie Kyle 27b55e472d
Refactor smart components
Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
2024-03-13 13:44:13 -07:00

175 lines
4.2 KiB
TypeScript

// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReadonlyDeep } from 'type-fest';
import { SocketStatus } from '../../types/SocketStatus';
import { trigger } from '../../shims/events';
import { assignWithNoUnnecessaryAllocation } from '../../util/assignWithNoUnnecessaryAllocation';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
// State
export type NetworkStateType = ReadonlyDeep<{
isOnline: boolean;
isOutage: boolean;
socketStatus: SocketStatus;
withinConnectingGracePeriod: boolean;
challengeStatus: 'required' | 'pending' | 'idle';
}>;
// Actions
const CHECK_NETWORK_STATUS = 'network/CHECK_NETWORK_STATUS';
const CLOSE_CONNECTING_GRACE_PERIOD = 'network/CLOSE_CONNECTING_GRACE_PERIOD';
const RELINK_DEVICE = 'network/RELINK_DEVICE';
const SET_CHALLENGE_STATUS = 'network/SET_CHALLENGE_STATUS';
const SET_OUTAGE = 'network/SET_OUTAGE';
export type CheckNetworkStatusPayloadType = ReadonlyDeep<{
isOnline: boolean;
socketStatus: SocketStatus;
}>;
type CheckNetworkStatusAction = ReadonlyDeep<{
type: 'network/CHECK_NETWORK_STATUS';
payload: CheckNetworkStatusPayloadType;
}>;
type CloseConnectingGracePeriodActionType = ReadonlyDeep<{
type: 'network/CLOSE_CONNECTING_GRACE_PERIOD';
}>;
type RelinkDeviceActionType = ReadonlyDeep<{
type: 'network/RELINK_DEVICE';
}>;
type SetChallengeStatusActionType = ReadonlyDeep<{
type: 'network/SET_CHALLENGE_STATUS';
payload: {
challengeStatus: NetworkStateType['challengeStatus'];
};
}>;
type SetOutageActionType = ReadonlyDeep<{
type: 'network/SET_OUTAGE';
payload: {
isOutage: boolean;
};
}>;
export type NetworkActionType = ReadonlyDeep<
| CheckNetworkStatusAction
| CloseConnectingGracePeriodActionType
| RelinkDeviceActionType
| SetChallengeStatusActionType
| SetOutageActionType
>;
// Action Creators
function checkNetworkStatus(
payload: CheckNetworkStatusPayloadType
): CheckNetworkStatusAction {
return {
type: CHECK_NETWORK_STATUS,
payload,
};
}
function closeConnectingGracePeriod(): CloseConnectingGracePeriodActionType {
return {
type: CLOSE_CONNECTING_GRACE_PERIOD,
};
}
function relinkDevice(): RelinkDeviceActionType {
trigger('setupAsNewDevice');
return {
type: RELINK_DEVICE,
};
}
function setChallengeStatus(
challengeStatus: NetworkStateType['challengeStatus']
): SetChallengeStatusActionType {
return {
type: SET_CHALLENGE_STATUS,
payload: { challengeStatus },
};
}
function setOutage(isOutage: boolean): SetOutageActionType {
return {
type: SET_OUTAGE,
payload: { isOutage },
};
}
export const actions = {
checkNetworkStatus,
closeConnectingGracePeriod,
relinkDevice,
setChallengeStatus,
setOutage,
};
export const useNetworkActions = (): BoundActionCreatorsMapObject<
typeof actions
> => useBoundActions(actions);
// Reducer
export function getEmptyState(): NetworkStateType {
return {
isOnline: navigator.onLine,
isOutage: false,
socketStatus: SocketStatus.OPEN,
withinConnectingGracePeriod: true,
challengeStatus: 'idle',
};
}
export function reducer(
state: Readonly<NetworkStateType> = getEmptyState(),
action: Readonly<NetworkActionType>
): NetworkStateType {
if (action.type === CHECK_NETWORK_STATUS) {
const { isOnline, socketStatus } = action.payload;
// This action is dispatched frequently. We avoid allocating a new object if nothing
// has changed to avoid an unnecessary re-render.
return assignWithNoUnnecessaryAllocation(state, {
isOnline,
socketStatus,
});
}
if (action.type === CLOSE_CONNECTING_GRACE_PERIOD) {
return {
...state,
withinConnectingGracePeriod: false,
};
}
if (action.type === SET_CHALLENGE_STATUS) {
return {
...state,
challengeStatus: action.payload.challengeStatus,
};
}
if (action.type === SET_OUTAGE) {
const { isOutage } = action.payload;
// This action is dispatched frequently when offline.
// We avoid allocating a new object if nothing has changed to
// avoid an unnecessary re-render.
return assignWithNoUnnecessaryAllocation(state, {
isOutage,
});
}
return state;
}