signal-desktop/ts/state/createStore.ts

109 lines
2.8 KiB
TypeScript
Raw Normal View History

2023-01-03 19:55:46 +00:00
// Copyright 2019 Signal Messenger, LLC
2020-10-30 20:34:04 +00:00
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable no-console */
2024-11-18 19:20:23 +00:00
import type { Middleware, Store, UnknownAction } from 'redux';
import { applyMiddleware, createStore as reduxCreateStore } from 'redux';
2019-01-14 21:49:58 +00:00
import promise from 'redux-promise-middleware';
2024-11-18 19:20:23 +00:00
import { thunk } from 'redux-thunk';
2019-01-14 21:49:58 +00:00
import { createLogger } from 'redux-logger';
import * as log from '../logging/log';
import type { StateType } from './reducer';
import { reducer } from './reducer';
2021-08-18 20:08:14 +00:00
import { dispatchItemsMiddleware } from '../shims/dispatchItemsMiddleware';
import { isOlderThan } from '../util/timestamp';
import { SECOND } from '../util/durations';
2024-09-04 18:12:45 +00:00
import { getEnvironment } from '../environment';
declare global {
// We want to extend `window`'s properties, so we need an interface.
2022-06-03 21:07:51 +00:00
// eslint-disable-next-line no-restricted-syntax
interface Console {
_log: Console['log'];
}
}
2019-01-14 21:49:58 +00:00
2024-09-04 18:12:45 +00:00
const env = getEnvironment();
2019-01-14 21:49:58 +00:00
// So Redux logging doesn't go to disk, and so we can get colors/styles
const directConsole = {
log: console._log,
groupCollapsed: console.groupCollapsed,
group: console.group,
groupEnd: console.groupEnd,
warn: console.warn,
error: console.error,
};
const logger = createLogger({
logger: directConsole,
predicate: (_getState, action) => {
if (action.type === 'network/CHECK_NETWORK_STATUS') {
return false;
}
if (action.type === 'calling/GROUP_CALL_AUDIO_LEVELS_CHANGE') {
return false;
}
return true;
},
2019-01-14 21:49:58 +00:00
});
const ACTION_COUNT_THRESHOLD = 25;
type ActionStats = {
timestamp: number;
names: Array<string>;
};
const actionStats: ActionStats = {
timestamp: Date.now(),
names: [],
};
2024-11-18 19:20:23 +00:00
export const actionRateLogger: Middleware = () => next => _action => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const action = _action as any as UnknownAction;
const name = action.type;
const lastTimestamp = actionStats.timestamp;
let count = actionStats.names.length;
if (isOlderThan(lastTimestamp, SECOND)) {
if (count > 0) {
actionStats.names = [];
}
actionStats.timestamp = Date.now();
return next(action);
}
actionStats.names.push(name);
count += 1;
if (count >= ACTION_COUNT_THRESHOLD) {
log.warn(
`ActionRateLogger: got ${count} events since ${lastTimestamp}: ${actionStats.names.join(',')}`
);
actionStats.names = [];
actionStats.timestamp = Date.now();
}
return next(action);
};
const middlewareList = [
promise,
thunk,
2021-08-18 20:08:14 +00:00
dispatchItemsMiddleware,
actionRateLogger,
...(env === 'production' ? [] : [logger]),
];
2019-01-14 21:49:58 +00:00
const enhancer = applyMiddleware(...middlewareList);
2019-01-14 21:49:58 +00:00
export const createStore = (
initialState: Readonly<StateType>
2024-11-18 19:20:23 +00:00
): Store<StateType> =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
reduxCreateStore<any, any>(reducer, initialState, enhancer);