From 6c7ba4c3166c8fa24391b50266e63b0da2a3d268 Mon Sep 17 00:00:00 2001 From: edolix Date: Mon, 3 Apr 2023 19:58:12 +0200 Subject: [PATCH] attempt removing redux --- packages/core/src/BaseSession.ts | 4 +- packages/core/src/index.ts | 2 - packages/core/src/redux/actions.ts | 21 +- packages/core/src/redux/connect.test.ts | 49 --- .../features/component/componentSaga.test.ts | 53 ---- .../redux/features/component/componentSaga.ts | 23 -- .../component/componentSelectors.test.ts | 54 ++-- .../features/component/componentSelectors.ts | 15 +- .../features/component/componentSlice.test.ts | 100 ------- .../features/component/componentSlice.ts | 63 ---- packages/core/src/redux/features/index.ts | 2 - .../src/redux/features/session/sessionSaga.ts | 1 + .../features/session/sessionSelectors.ts | 4 - .../features/session/sessionSlice.test.ts | 65 ---- .../redux/features/session/sessionSlice.ts | 78 ----- packages/core/src/redux/index.ts | 1 - packages/core/src/redux/interfaces.ts | 3 +- packages/core/src/redux/rootReducer.ts | 7 - packages/core/src/redux/rootSaga.test.ts | 4 +- packages/core/src/redux/rootSaga.ts | 10 +- packages/core/src/redux/swStore.ts | 70 ++++- .../core/src/redux/toolkit/configureStore.ts | 192 ------------ .../core/src/redux/toolkit/createAction.ts | 1 - .../core/src/redux/toolkit/createReducer.ts | 121 -------- .../core/src/redux/toolkit/createSlice.ts | 279 ------------------ .../src/redux/toolkit/devtoolsExtension.ts | 204 ------------- .../src/redux/toolkit/getDefaultMiddleware.ts | 22 -- packages/core/src/redux/toolkit/index.ts | 1 - .../core/src/redux/toolkit/isPlainObject.ts | 23 -- .../core/src/redux/toolkit/mapBuilders.ts | 117 -------- packages/core/src/redux/toolkit/utils.ts | 42 --- .../utils/createDestroyableSlice.test.ts | 41 --- .../src/redux/utils/createDestroyableSlice.ts | 37 --- packages/js/src/BaseRoomSession.test.ts | 14 +- .../src/video/childMemberJoinedWorker.test.ts | 4 +- .../js/src/video/childMemberJoinedWorker.ts | 4 +- .../webrtc/src/workers/promoteDemoteWorker.ts | 4 +- .../src/workers/roomSubscribedWorker.ts | 4 +- 38 files changed, 132 insertions(+), 1607 deletions(-) delete mode 100644 packages/core/src/redux/connect.test.ts delete mode 100644 packages/core/src/redux/features/component/componentSaga.test.ts delete mode 100644 packages/core/src/redux/features/component/componentSaga.ts delete mode 100644 packages/core/src/redux/features/component/componentSlice.test.ts delete mode 100644 packages/core/src/redux/features/component/componentSlice.ts delete mode 100644 packages/core/src/redux/features/index.ts delete mode 100644 packages/core/src/redux/features/session/sessionSlice.test.ts delete mode 100644 packages/core/src/redux/features/session/sessionSlice.ts delete mode 100644 packages/core/src/redux/rootReducer.ts delete mode 100644 packages/core/src/redux/toolkit/configureStore.ts delete mode 100644 packages/core/src/redux/toolkit/createReducer.ts delete mode 100644 packages/core/src/redux/toolkit/createSlice.ts delete mode 100644 packages/core/src/redux/toolkit/devtoolsExtension.ts delete mode 100644 packages/core/src/redux/toolkit/getDefaultMiddleware.ts delete mode 100644 packages/core/src/redux/toolkit/isPlainObject.ts delete mode 100644 packages/core/src/redux/toolkit/mapBuilders.ts delete mode 100644 packages/core/src/redux/toolkit/utils.ts delete mode 100644 packages/core/src/redux/utils/createDestroyableSlice.test.ts delete mode 100644 packages/core/src/redux/utils/createDestroyableSlice.ts diff --git a/packages/core/src/BaseSession.ts b/packages/core/src/BaseSession.ts index 419fb6c594..be354e5f8a 100644 --- a/packages/core/src/BaseSession.ts +++ b/packages/core/src/BaseSession.ts @@ -34,8 +34,8 @@ import { socketMessageAction, sessionDisconnectedAction, sessionReconnectingAction, + sessionAuthStatusAction, } from './redux/actions' -import { sessionActions } from './redux/features/session/sessionSlice' import { SwAuthorizationState } from '.' import { SessionChannel, SessionChannelAction } from './redux/interfaces' @@ -584,7 +584,7 @@ export class BaseSession { this.logger.debug('Close Connection:', status) this._status = status this.dispatch( - sessionActions.authStatus( + sessionAuthStatusAction( status === 'disconnected' ? 'unauthorized' : 'unknown' ) ) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c5a1ff5094..f3e6e7a059 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -68,8 +68,6 @@ export { isSATAuth, } -export * from './redux/features/component/componentSlice' -export * from './redux/features/session/sessionSlice' export * as componentSelectors from './redux/features/component/componentSelectors' export * from './RPCMessages' export * from './utils/interfaces' diff --git a/packages/core/src/redux/actions.ts b/packages/core/src/redux/actions.ts index 92c42b45bc..c10093c46e 100644 --- a/packages/core/src/redux/actions.ts +++ b/packages/core/src/redux/actions.ts @@ -6,7 +6,13 @@ import type { SessionActions, CompoundEvents, } from '../utils/interfaces' -import { EventEmitter } from '..' +import { + Authorization, + EventEmitter, + RPCConnectResult, + SessionAuthStatus, +} from '..' +import { UpdateComponent } from './interfaces' export const initAction = createAction('swSdk/init') export const destroyAction = createAction('swSdk/destroy') @@ -44,6 +50,15 @@ export const sessionExpiringAction = createAction( export const sessionForceCloseAction = createAction( 'session.forceClose' ) +export const sessionAuthStateAction = createAction( + 'session/auth_state' +) +export const sessionAuthStatusAction = createAction( + 'session/auth_status' +) +export const sessionAuthorizedAction = createAction( + 'session/authorized' +) const formatCustomSagaAction = (id: string, action: Action) => { return `${action.type}/${id}` } @@ -68,4 +83,8 @@ export const compoundEventAttachAction = createAction< CompoundEvents >('compound_event:attach') +export const componentUpsertAction = createAction( + 'components/upsert' +) + export { createAction } diff --git a/packages/core/src/redux/connect.test.ts b/packages/core/src/redux/connect.test.ts deleted file mode 100644 index 17c0e64ec8..0000000000 --- a/packages/core/src/redux/connect.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { configureJestStore } from '../testUtils' -import { BaseComponent } from '../BaseComponent' -import { connect } from './connect' -import { componentActions } from './features' -import { EventEmitter } from '../utils/EventEmitter' -import { SDKStore } from './' - -describe('Connect', () => { - let store: SDKStore - let instance: any - let updateStateAction: any - let updateRemoteSDPAction: any - - beforeEach(() => { - store = configureJestStore() - instance = connect({ - store, - Component: BaseComponent, - })({ - emitter: new EventEmitter(), - }) - instance.emit = jest.fn() - - updateStateAction = componentActions.upsert({ - id: instance.__uuid, - state: 'active', - }) - - updateRemoteSDPAction = componentActions.upsert({ - id: instance.__uuid, - remoteSDP: '', - }) - }) - - it('should unsubscribe after destroy', () => { - store.dispatch(updateStateAction) - - instance.destroy() - instance.emit.mockClear() - - store.dispatch(updateStateAction) - expect(instance.emit).not.toHaveBeenCalled() - }) - - it('should not invoke the instance method if something else in redux changed', () => { - store.dispatch(updateRemoteSDPAction) - expect(instance.emit).not.toHaveBeenCalled() - }) -}) diff --git a/packages/core/src/redux/features/component/componentSaga.test.ts b/packages/core/src/redux/features/component/componentSaga.test.ts deleted file mode 100644 index 20a48ce29e..0000000000 --- a/packages/core/src/redux/features/component/componentSaga.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { expectSaga } from 'redux-saga-test-plan' -import { configureJestStore } from '../../../testUtils' -import { componentCleanupSagaWorker } from './componentSaga' -import { rootReducer } from '../../rootReducer' -import { SDKStore } from '../..' - -describe('componentCleanupSaga', () => { - let store: SDKStore - - beforeEach(() => { - store = configureJestStore({ - preloadedState: { - components: { - byId: { - '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9': { - id: '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9', - }, - 'faa63915-3a64-4c39-acbb-06dac0758f8a': { - id: 'faa63915-3a64-4c39-acbb-06dac0758f8a', - responses: {}, - }, - 'zfaa63915-3a64-4c39-acbb-06dac0758f8a': { - id: 'zfaa63915-3a64-4c39-acbb-06dac0758f8a', - errors: {}, - }, - }, - }, - }, - }) - }) - it('should cleanup unused components from the store', () => { - return expectSaga(componentCleanupSagaWorker) - .withReducer(rootReducer, store.getState()) - .hasFinalState({ - components: { - byId: { - '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9': { - id: '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9', - }, - }, - }, - session: { - protocol: '', - iceServers: [], - authStatus: 'unknown', - authState: undefined, - authError: undefined, - authCount: 0, - }, - }) - .run() - }) -}) diff --git a/packages/core/src/redux/features/component/componentSaga.ts b/packages/core/src/redux/features/component/componentSaga.ts deleted file mode 100644 index c7d1842c19..0000000000 --- a/packages/core/src/redux/features/component/componentSaga.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { SagaIterator } from '@redux-saga/core' -import { delay, fork, put, select } from '@redux-saga/core/effects' -import { componentActions } from '..' -import { getComponentsToCleanup } from './componentSelectors' - -export function* componentCleanupSagaWorker(): SagaIterator { - const toCleanup = yield select(getComponentsToCleanup) - - if (toCleanup.length) { - yield put( - componentActions.cleanup({ - ids: toCleanup, - }) - ) - } -} - -export function* componentCleanupSaga(): SagaIterator { - while (true) { - yield delay(3600_000) // 1 hour - yield fork(componentCleanupSagaWorker) - } -} diff --git a/packages/core/src/redux/features/component/componentSelectors.test.ts b/packages/core/src/redux/features/component/componentSelectors.test.ts index 4ff1809bf0..9dbb8f6168 100644 --- a/packages/core/src/redux/features/component/componentSelectors.test.ts +++ b/packages/core/src/redux/features/component/componentSelectors.test.ts @@ -1,38 +1,44 @@ import { configureJestStore } from '../../../testUtils' -import { getComponentsToCleanup } from './componentSelectors' +import { getComponent, getComponentsById } from './componentSelectors' import { SDKStore } from '../..' describe('componentSelectors', () => { + const preloadedState = { + components: { + byId: { + '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9': { + id: '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9', + foo: 'bar', + }, + 'faa63915-3a64-4c39-acbb-06dac0758f8a': { + id: 'faa63915-3a64-4c39-acbb-06dac0758f8a', + responses: {}, + }, + }, + }, + } let store: SDKStore beforeEach(() => { store = configureJestStore({ - preloadedState: { - components: { - byId: { - '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9': { - id: '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9', - }, - 'faa63915-3a64-4c39-acbb-06dac0758f8a': { - id: 'faa63915-3a64-4c39-acbb-06dac0758f8a', - responses: {}, - }, - 'zfaa63915-3a64-4c39-acbb-06dac0758f8a': { - id: 'zfaa63915-3a64-4c39-acbb-06dac0758f8a', - errors: {}, - }, - }, - }, - }, + preloadedState, + }) + }) + + describe('getComponent', () => { + it('should return a component', () => { + const id = '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9' + expect(getComponent(store.getState(), id)).toStrictEqual( + preloadedState.components.byId[id] + ) }) }) - describe('getComponentsToCleanup', () => { - it('should return the list of components to cleanup', () => { - expect(getComponentsToCleanup(store.getState())).toEqual([ - 'faa63915-3a64-4c39-acbb-06dac0758f8a', - 'zfaa63915-3a64-4c39-acbb-06dac0758f8a', - ]) + describe('getComponentsById', () => { + it('should return a component', () => { + expect(getComponentsById(store.getState())).toStrictEqual( + preloadedState.components.byId + ) }) }) }) diff --git a/packages/core/src/redux/features/component/componentSelectors.ts b/packages/core/src/redux/features/component/componentSelectors.ts index b692c628c0..3a6948e8d2 100644 --- a/packages/core/src/redux/features/component/componentSelectors.ts +++ b/packages/core/src/redux/features/component/componentSelectors.ts @@ -1,4 +1,4 @@ -import { ReduxComponent, SDKState } from '../../interfaces' +import { SDKState } from '../../interfaces' export const getComponent = ({ components }: SDKState, id: string) => { return components.byId?.[id] @@ -7,16 +7,3 @@ export const getComponent = ({ components }: SDKState, id: string) => { export const getComponentsById = ({ components }: SDKState) => { return components.byId } - -export const getComponentsToCleanup = (state: SDKState) => { - const components = getComponentsById(state) - - let toCleanup: Array = [] - Object.keys(components).forEach((id) => { - if (components[id].responses || components[id].errors) { - toCleanup.push(id) - } - }) - - return toCleanup -} diff --git a/packages/core/src/redux/features/component/componentSlice.test.ts b/packages/core/src/redux/features/component/componentSlice.test.ts deleted file mode 100644 index 353a24caff..0000000000 --- a/packages/core/src/redux/features/component/componentSlice.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { configureJestStore } from '../../../testUtils' -import { componentActions, initialComponentState } from './componentSlice' -import { getComponent, getComponentsById } from './componentSelectors' -import { destroyAction } from '../../actions' -import { ReduxComponent } from '../../interfaces' -import { SDKStore } from '../..' - -describe('ComponentState Tests', () => { - let store: SDKStore - const componentId = '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9' - const component: ReduxComponent = { - id: componentId, - } - - beforeEach(() => { - store = configureJestStore() - }) - - describe('update action', () => { - it('should create a new entry if the id is not in the state', () => { - expect(getComponent(store.getState(), componentId)).toBeUndefined() - store.dispatch(componentActions.upsert(component)) - - expect(getComponent(store.getState(), componentId)).toStrictEqual({ - id: '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9', - }) - }) - - it('should update the state properly', () => { - store.dispatch(componentActions.upsert(component)) - - expect(getComponent(store.getState(), componentId)).toStrictEqual({ - id: '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9', - }) - - store.dispatch( - componentActions.upsert({ - ...component, - state: 'active', - }) - ) - expect(getComponent(store.getState(), componentId)).toStrictEqual({ - id: '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9', - state: 'active', - }) - }) - }) - - describe('cleanup action', () => { - const s = configureJestStore({ - preloadedState: { - components: { - byId: { - '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9': { - id: '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9', - }, - 'faa63915-3a64-4c39-acbb-06dac0758f8a': { - id: 'faa63915-3a64-4c39-acbb-06dac0758f8a', - responses: {}, - }, - 'zfaa63915-3a64-4c39-acbb-06dac0758f8a': { - id: 'zfaa63915-3a64-4c39-acbb-06dac0758f8a', - errors: {}, - }, - }, - }, - }, - }) - - s.dispatch( - componentActions.cleanup({ - ...component, - ids: [ - '268b4cf8-a3c5-4003-8666-3b7a4f0a5af9', - 'faa63915-3a64-4c39-acbb-06dac0758f8a', - ], - }) - ) - - expect(getComponentsById(s.getState())).toStrictEqual({ - 'zfaa63915-3a64-4c39-acbb-06dac0758f8a': { - id: 'zfaa63915-3a64-4c39-acbb-06dac0758f8a', - errors: {}, - }, - }) - }) - - it('should reset to initial on destroyAction', () => { - // Create some components first - store.dispatch(componentActions.upsert(component)) - store.dispatch( - componentActions.upsert({ - id: 'random', - }) - ) - - store.dispatch(destroyAction()) - expect(store.getState().components).toStrictEqual(initialComponentState) - }) -}) diff --git a/packages/core/src/redux/features/component/componentSlice.ts b/packages/core/src/redux/features/component/componentSlice.ts deleted file mode 100644 index c72f6753b3..0000000000 --- a/packages/core/src/redux/features/component/componentSlice.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { createDestroyableSlice } from '../../utils/createDestroyableSlice' -import type { PayloadAction } from '../../toolkit' -import type { ComponentState, ReduxComponent } from '../../interfaces' -import type { DeepReadonly } from '../../../types' - -export const initialComponentState: DeepReadonly = { - byId: {}, -} - -type UpdateComponent = Partial & Pick -type CleanupComponentParams = { - ids: Array -} - -const componentSlice = createDestroyableSlice({ - name: 'components', - initialState: initialComponentState, - reducers: { - upsert: (state, { payload }: PayloadAction) => { - if (payload.id in state.byId) { - return { - ...state, - byId: { - ...state.byId, - [payload.id]: { - ...state.byId[payload.id], - ...payload, - }, - }, - } - } else { - return { - ...state, - byId: { - ...state.byId, - [payload.id]: payload, - }, - } - } - }, - cleanup: (state, { payload }: PayloadAction) => { - return { - ...state, - byId: Object.entries(state.byId).reduce( - (reducer, [componentId, value]) => { - if (!payload.ids.includes(componentId)) { - reducer[componentId] = value - } - - return reducer - }, - {} as ComponentState['byId'] - ), - } - }, - }, -}) - -// prettier-ignore -export const { - actions: componentActions, - reducer: componentReducer -} = componentSlice diff --git a/packages/core/src/redux/features/index.ts b/packages/core/src/redux/features/index.ts deleted file mode 100644 index 09e20c3c8f..0000000000 --- a/packages/core/src/redux/features/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './component/componentSlice' -export * from './session/sessionSlice' diff --git a/packages/core/src/redux/features/session/sessionSaga.ts b/packages/core/src/redux/features/session/sessionSaga.ts index 39dc6b74f1..402fba3951 100644 --- a/packages/core/src/redux/features/session/sessionSaga.ts +++ b/packages/core/src/redux/features/session/sessionSaga.ts @@ -120,6 +120,7 @@ export function* sessionChannelWatcher({ * since we'll move that logic on a separate package. */ yield put(rootChannel, { + // FIXME: type: broadcastParams.event_type, payload: broadcastParams, }) diff --git a/packages/core/src/redux/features/session/sessionSelectors.ts b/packages/core/src/redux/features/session/sessionSelectors.ts index f249cdf66b..5bc597212f 100644 --- a/packages/core/src/redux/features/session/sessionSelectors.ts +++ b/packages/core/src/redux/features/session/sessionSelectors.ts @@ -12,10 +12,6 @@ export const getAuthStatus = ({ session }: SDKState) => { return session.authStatus } -export const getAuthError = ({ session }: SDKState) => { - return session.authError -} - export const getAuthState = ({ session }: SDKState) => { return session.authState } diff --git a/packages/core/src/redux/features/session/sessionSlice.test.ts b/packages/core/src/redux/features/session/sessionSlice.test.ts deleted file mode 100644 index c826dd82a7..0000000000 --- a/packages/core/src/redux/features/session/sessionSlice.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { configureJestStore, rpcConnectResultVRT } from '../../../testUtils' -import { sessionActions, initialSessionState } from './sessionSlice' -import { getAuthStatus } from './sessionSelectors' -import { destroyAction, initAction, reauthAction } from '../../actions' -import { SDKStore } from '../..' - -describe('SessionState Tests', () => { - let store: SDKStore - - beforeEach(() => { - store = configureJestStore() - }) - - it('should seed SessionState on sessionActions.connected action', () => { - store.dispatch(sessionActions.connected(rpcConnectResultVRT)) - - expect(store.getState().session).toStrictEqual({ - protocol: rpcConnectResultVRT.protocol, - iceServers: rpcConnectResultVRT.ice_servers, - authStatus: 'authorized', - authState: { - media_allowed: 'all', - audio_allowed: 'both', - join_as: 'member', - project: '8f0a119a-cda7-4497-a47d-c81493b824d4', - project_id: '8f0a119a-cda7-4497-a47d-c81493b824d4', - resource: '9c80f1e8-9430-4070-a043-937eb3a96b38', - room: { - name: 'lobby', - display_name: 'Lobby', - scopes: ['room.self.audio_mute', 'room.self.audio_unmute'], - meta: {}, - }, - scope_id: '26675883-8499-4ee9-85eb-691c4aa209f8', - scopes: ['video'], - signature: - 'SGZtkRD9fvuBAOUp1UF56zESxdEvGT6qSGZtkRD9fvuBAOUp1UF56zESxdEvGT6q', - type: 'video', - user_name: 'Joe', - video_allowed: 'both', - meta: {}, - }, - authError: undefined, - authCount: 1, - }) - }) - - it('should reset to initial on destroyAction', () => { - store.dispatch(sessionActions.connected(rpcConnectResultVRT)) - - store.dispatch(destroyAction()) - expect(store.getState().session).toStrictEqual(initialSessionState) - }) - - it('should set authStatus to authorizing on initAction and reauthAction', () => { - store.dispatch(initAction()) - expect(getAuthStatus(store.getState())).toEqual('authorizing') - - store.dispatch(sessionActions.connected(rpcConnectResultVRT)) - expect(getAuthStatus(store.getState())).toEqual('authorized') - - store.dispatch(reauthAction({ token: 'foo' })) - expect(getAuthStatus(store.getState())).toEqual('authorizing') - }) -}) diff --git a/packages/core/src/redux/features/session/sessionSlice.ts b/packages/core/src/redux/features/session/sessionSlice.ts deleted file mode 100644 index b08782f8d3..0000000000 --- a/packages/core/src/redux/features/session/sessionSlice.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { PayloadAction, AnyAction } from '../../toolkit' -import type { SessionState } from '../../interfaces' -import type { - Authorization, - RPCConnectResult, - SessionAuthError, - SessionAuthStatus, -} from '../../../utils/interfaces' -import type { DeepReadonly } from '../../../types' -import { createDestroyableSlice } from '../../utils/createDestroyableSlice' -import { authErrorAction, initAction, reauthAction } from '../../actions' - -export const initialSessionState: DeepReadonly = { - protocol: '', - iceServers: [], - authStatus: 'unknown', - authState: undefined, - authError: undefined, - authCount: 0, -} - -type AuthorizingAction = typeof initAction | typeof reauthAction -function authorizingAction(action: AnyAction): action is AuthorizingAction { - return [initAction.type, reauthAction.type].includes(action.type) -} - -const sessionSlice = createDestroyableSlice({ - name: 'session', - initialState: initialSessionState, - reducers: { - connected: (state, { payload }: PayloadAction) => { - return { - ...state, - authStatus: 'authorized', - authState: payload?.authorization, - authCount: state.authCount + 1, - protocol: payload?.protocol ?? '', - iceServers: payload?.ice_servers ?? [], - } - }, - authStatus: (state, { payload }: PayloadAction) => { - return { - ...state, - authStatus: payload, - } - }, - updateAuthState: (state, { payload }: PayloadAction) => { - return { - ...state, - authState: payload, - } - }, - }, - extraReducers: (builder) => { - builder.addCase( - authErrorAction.type, - (state, { payload }: PayloadAction<{ error: SessionAuthError }>) => { - return { - ...state, - authStatus: 'unauthorized', - authError: payload.error, - } - } - ) - builder.addMatcher(authorizingAction, (state) => { - return { - ...state, - authStatus: 'authorizing', - } - }) - }, -}) - -// prettier-ignore -export const { - actions: sessionActions, - reducer: sessionReducer -} = sessionSlice diff --git a/packages/core/src/redux/index.ts b/packages/core/src/redux/index.ts index 35cc586470..8d03aa7305 100644 --- a/packages/core/src/redux/index.ts +++ b/packages/core/src/redux/index.ts @@ -1,5 +1,4 @@ import { channel, multicastChannel, Saga, Task } from '@redux-saga/core' -// import { configureStore as rtConfigureStore } from './toolkit' import rootSaga from './rootSaga' import { PubSubChannel, diff --git a/packages/core/src/redux/interfaces.ts b/packages/core/src/redux/interfaces.ts index c1bbe12df7..2a4dcf5bb4 100644 --- a/packages/core/src/redux/interfaces.ts +++ b/packages/core/src/redux/interfaces.ts @@ -52,7 +52,8 @@ export interface Message extends SWComponent { } export type ReduxComponent = WebRTCCall | Message - +export type UpdateComponent = Partial & + Pick export interface ComponentState { byId: { [key: string]: ReduxComponent diff --git a/packages/core/src/redux/rootReducer.ts b/packages/core/src/redux/rootReducer.ts deleted file mode 100644 index afbf4026e5..0000000000 --- a/packages/core/src/redux/rootReducer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { combineReducers } from './toolkit' -import { componentReducer, sessionReducer } from './features' - -export const rootReducer = combineReducers({ - components: componentReducer, - session: sessionReducer, -}) diff --git a/packages/core/src/redux/rootSaga.test.ts b/packages/core/src/redux/rootSaga.test.ts index 3cf99f1d30..c9477a237e 100644 --- a/packages/core/src/redux/rootSaga.test.ts +++ b/packages/core/src/redux/rootSaga.test.ts @@ -8,7 +8,6 @@ import rootSaga, { } from './rootSaga' import { sessionChannelWatcher } from './features/session/sessionSaga' import { pubSubSaga } from './features/pubSub/pubSubSaga' -import { sessionActions } from './features' import { sessionConnectedAction, sessionDisconnectedAction, @@ -22,6 +21,7 @@ import { destroyAction, initAction, reauthAction, + sessionAuthorizedAction, } from './actions' import { AuthError } from '../CustomErrors' import { @@ -68,7 +68,7 @@ describe('sessionStatusWatcher', () => { saga.next().take(actions) saga .next(authSuccessAction()) - .put(sessionActions.connected(session.rpcConnectResult)) + .put(sessionAuthorizedAction(session.rpcConnectResult)) saga.next().put(pubSubChannel, sessionConnectedAction()) // Saga waits again for actions due to the while loop diff --git a/packages/core/src/redux/rootSaga.ts b/packages/core/src/redux/rootSaga.ts index 7e19927206..b6945521e5 100644 --- a/packages/core/src/redux/rootSaga.ts +++ b/packages/core/src/redux/rootSaga.ts @@ -15,8 +15,8 @@ import { sessionExpiringAction, reauthAction, sessionForceCloseAction, + sessionAuthorizedAction, } from './actions' -import { sessionActions } from './features' import { authErrorAction, authSuccessAction, @@ -26,7 +26,6 @@ import { AuthError } from '../CustomErrors' import { PubSubChannel, RootChannel, SessionChannel } from './interfaces' import { createRestartableSaga } from './utils/sagaHelpers' import { Action } from 'redux' -// import { componentCleanupSaga } from './features/component/componentSaga' interface StartSagaOptions { session: BaseSession @@ -107,8 +106,6 @@ export function* initSessionSaga({ userOptions, }) - // const compCleanupTask = yield fork(componentCleanupSaga) - session.connect() yield take(channels.rootChannel, destroyAction.type) @@ -123,7 +120,6 @@ export function* initSessionSaga({ * being automatically cleaned up when the session is * destroyed, most likely because it's using a timer. */ - // compCleanupTask?.cancel() pubSubTask.cancel() sessionStatusTask.cancel() customTasks.forEach((task) => task.cancel()) @@ -150,7 +146,7 @@ export function* reauthenticateWorker({ session.token = token yield call(session.reauthenticate) // Update the store with the new "connect result" - yield put(sessionActions.connected(session.rpcConnectResult)) + yield put(sessionAuthorizedAction(session.rpcConnectResult)) yield put(pubSubChannel, sessionConnectedAction()) } } catch (error) { @@ -180,7 +176,7 @@ export function* sessionStatusWatcher(options: StartSagaOptions): SagaIterator { switch (action.type) { case authSuccessAction.type: { const { session, pubSubChannel } = options - yield put(sessionActions.connected(session.rpcConnectResult)) + yield put(sessionAuthorizedAction(session.rpcConnectResult)) yield put(pubSubChannel, sessionConnectedAction()) break } diff --git a/packages/core/src/redux/swStore.ts b/packages/core/src/redux/swStore.ts index b858e27d02..1312b9b150 100644 --- a/packages/core/src/redux/swStore.ts +++ b/packages/core/src/redux/swStore.ts @@ -4,11 +4,34 @@ import { getLogger } from '../utils' import { SDKState } from './interfaces' import { InternalChannels } from '../utils/interfaces' import { AnyAction } from './toolkit' +import { + authErrorAction, + componentUpsertAction, + destroyAction, + initAction, + reauthAction, + sessionAuthStateAction, + sessionAuthStatusAction, + sessionAuthorizedAction, +} from './actions' interface CreateSwStoreParams { channels: InternalChannels preloadedState: Partial } +const initialState: SDKState = { + components: { + byId: {}, + }, + session: { + protocol: '', + iceServers: [], + authStatus: 'unknown', + authState: undefined, + authError: undefined, + authCount: 0, + }, +} export const createSWStore = ({ channels, @@ -16,18 +39,8 @@ export const createSWStore = ({ }: CreateSwStoreParams) => { const logger = getLogger() let rootTask: Task - const state: SDKState = { - components: { - byId: {}, - }, - session: { - protocol: '', - iceServers: [], - authStatus: 'unknown', - authState: undefined, - authError: undefined, - authCount: 0, - }, + let state: SDKState = { + ...initialState, ...preloadedState, } @@ -45,7 +58,24 @@ export const createSWStore = ({ // Process the action within reducers switch (action.type) { - case 'session/connected': + case initAction.type: + case reauthAction.type: + state.session = { + ...state.session, + authStatus: 'authorizing', + } + break + case destroyAction.type: + state = { ...initialState } + break + case authErrorAction.type: + state.session = { + ...state.session, + authStatus: 'unauthorized', + authError: action.payload.error, + } + break + case sessionAuthorizedAction.type: state.session = { ...state.session, authStatus: 'authorized', @@ -55,7 +85,19 @@ export const createSWStore = ({ iceServers: action.payload?.ice_servers ?? [], } break - case 'components/upsert': + case sessionAuthStatusAction.type: + state.session = { + ...state.session, + authStatus: action.payload, + } + break + case sessionAuthStateAction.type: + state.session = { + ...state.session, + authState: action.payload, + } + break + case componentUpsertAction.type: if (action.payload.id in state.components.byId) { state.components = { ...state, diff --git a/packages/core/src/redux/toolkit/configureStore.ts b/packages/core/src/redux/toolkit/configureStore.ts deleted file mode 100644 index fe5ccd45c7..0000000000 --- a/packages/core/src/redux/toolkit/configureStore.ts +++ /dev/null @@ -1,192 +0,0 @@ -import type { - Reducer, - ReducersMapObject, - Middleware, - Action, - AnyAction, - StoreEnhancer, - Store, - Dispatch, - PreloadedState, - CombinedState, -} from 'redux' -import { createStore, compose, applyMiddleware, combineReducers } from 'redux' -import type { EnhancerOptions as DevToolsOptions } from './devtoolsExtension' -import { composeWithDevTools } from './devtoolsExtension' - -import isPlainObject from './isPlainObject' -import { - CurriedGetDefaultMiddleware, - curryGetDefaultMiddleware, -} from './getDefaultMiddleware' -import type { DispatchForMiddlewares, NoInfer } from './tsHelpers' - -const IS_PRODUCTION = process.env.NODE_ENV === 'production' - -/** - * Callback function type, to be used in `ConfigureStoreOptions.enhancers` - * - * @public - */ -export type ConfigureEnhancersCallback = ( - defaultEnhancers: readonly StoreEnhancer[] -) => StoreEnhancer[] - -/** - * Options for `configureStore()`. - * - * @public - */ -export interface ConfigureStoreOptions< - S = any, - A extends Action = AnyAction, - M extends Middlewares = Middlewares -> { - /** - * A single reducer function that will be used as the root reducer, or an - * object of slice reducers that will be passed to `combineReducers()`. - */ - reducer: Reducer | ReducersMapObject - - /** - * An array of Redux middleware to install. If not supplied, defaults to - * the set of middleware returned by `getDefaultMiddleware()`. - */ - middleware?: ((getDefaultMiddleware: CurriedGetDefaultMiddleware) => M) | M - - /** - * Whether to enable Redux DevTools integration. Defaults to `true`. - * - * Additional configuration can be done by passing Redux DevTools options - */ - devTools?: boolean | DevToolsOptions - - /** - * The initial state, same as Redux's createStore. - * You may optionally specify it to hydrate the state - * from the server in universal apps, or to restore a previously serialized - * user session. If you use `combineReducers()` to produce the root reducer - * function (either directly or indirectly by passing an object as `reducer`), - * this must be an object with the same shape as the reducer map keys. - */ - /* - Not 100% correct but the best approximation we can get: - - if S is a `CombinedState` applying a second `CombinedState` on it does not change anything. - - if it is not, there could be two cases: - - `ReducersMapObject` is being passed in. In this case, we will call `combineReducers` on it and `CombinedState` is correct - - `Reducer` is being passed in. In this case, actually `CombinedState` is wrong and `S` would be correct. - As we cannot distinguish between those two cases without adding another generic paramter, - we just make the pragmatic assumption that the latter almost never happens. - */ - preloadedState?: PreloadedState>> - - /** - * The store enhancers to apply. See Redux's `createStore()`. - * All enhancers will be included before the DevTools Extension enhancer. - * If you need to customize the order of enhancers, supply a callback - * function that will receive the original array (ie, `[applyMiddleware]`), - * and should return a new array (such as `[applyMiddleware, offline]`). - * If you only need to add middleware, you can use the `middleware` parameter instead. - */ - enhancers?: StoreEnhancer[] | ConfigureEnhancersCallback -} - -type Middlewares = ReadonlyArray> - -/** - * A Redux store returned by `configureStore()`. Supports dispatching - * side-effectful _thunks_ in addition to plain actions. - * - * @public - */ -export interface EnhancedStore< - S = any, - A extends Action = AnyAction, - M extends Middlewares = Middlewares -> extends Store { - /** - * The `dispatch` method of your store, enhanced by all its middlewares. - * - * @inheritdoc - */ - dispatch: Dispatch & DispatchForMiddlewares -} - -/** - * A friendly abstraction over the standard Redux `createStore()` function. - * - * @param config The store configuration. - * @returns A configured Redux store. - * - * @public - */ -export function configureStore< - S = any, - A extends Action = AnyAction, - M extends Middlewares = [] ->(options: ConfigureStoreOptions): EnhancedStore { - const curriedGetDefaultMiddleware = curryGetDefaultMiddleware() - - const { - reducer = undefined, - middleware = curriedGetDefaultMiddleware(), - devTools = true, - preloadedState = undefined, - enhancers = undefined, - } = options || {} - - let rootReducer: Reducer - - if (typeof reducer === 'function') { - rootReducer = reducer - } else if (isPlainObject(reducer)) { - rootReducer = combineReducers(reducer) - } else { - throw new Error( - '"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers' - ) - } - - let finalMiddleware = middleware - if (typeof finalMiddleware === 'function') { - finalMiddleware = finalMiddleware(curriedGetDefaultMiddleware) - - if (!IS_PRODUCTION && !Array.isArray(finalMiddleware)) { - throw new Error( - 'when using a middleware builder function, an array of middleware must be returned' - ) - } - } - if ( - !IS_PRODUCTION && - finalMiddleware.some((item) => typeof item !== 'function') - ) { - throw new Error( - 'each middleware provided to configureStore must be a function' - ) - } - - const middlewareEnhancer = applyMiddleware(...finalMiddleware) - - let finalCompose = compose - - if (devTools) { - finalCompose = composeWithDevTools({ - // Enable capture of stack traces for dispatched Redux actions - trace: !IS_PRODUCTION, - ...(typeof devTools === 'object' && devTools), - }) - } - - let storeEnhancers: StoreEnhancer[] = [middlewareEnhancer] - - if (Array.isArray(enhancers)) { - storeEnhancers = [middlewareEnhancer, ...enhancers] - } else if (typeof enhancers === 'function') { - storeEnhancers = enhancers(storeEnhancers) - } - - const composedEnhancer = finalCompose(...storeEnhancers) as any - - return createStore(rootReducer, preloadedState, composedEnhancer) -} diff --git a/packages/core/src/redux/toolkit/createAction.ts b/packages/core/src/redux/toolkit/createAction.ts index b4365c1878..3ffcd11cb0 100644 --- a/packages/core/src/redux/toolkit/createAction.ts +++ b/packages/core/src/redux/toolkit/createAction.ts @@ -6,7 +6,6 @@ import type { IsAny, } from './tsHelpers' - /** * An action with a string type and an associated payload. This is the * type of action returned by `createAction()` action creators. diff --git a/packages/core/src/redux/toolkit/createReducer.ts b/packages/core/src/redux/toolkit/createReducer.ts deleted file mode 100644 index 3677ca76c4..0000000000 --- a/packages/core/src/redux/toolkit/createReducer.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { Action, AnyAction, Reducer } from 'redux' -import { DeepReadonly } from '../../types' -import { ActionReducerMapBuilder, executeReducerBuilderCallback } from './mapBuilders' -import { NoInfer } from './tsHelpers' - -export type CaseReducer = ( - state: S, - action: A -) => S | void - -export interface ActionMatcher { - (action: AnyAction): action is A -} - -/** - * Defines a mapping from action types to corresponding action object shapes. - * - * @deprecated This should not be used manually - it is only used for internal - * inference purposes and should not have any further value. - * It might be removed in the future. - * @public - */ -export type Actions = Record - -/** - * A mapping from action types to case reducers for `createReducer()`. - * - * @deprecated This should not be used manually - it is only used - * for internal inference purposes and using it manually - * would lead to type erasure. - * It might be removed in the future. - * @public - */ -export type CaseReducers = { - [T in keyof AS]: AS[T] extends Action ? CaseReducer : void -} - -export type ActionMatcherDescription = { - matcher: ActionMatcher - reducer: CaseReducer> -} - -export type ActionMatcherDescriptionCollection = Array< - ActionMatcherDescription -> - - -export type NotFunction = T extends Function ? never : T - -export type ReducerWithInitialState> = Reducer> & { - getInitialState: () => DeepReadonly -} - -export type ReadonlyActionMatcherDescriptionCollection = ReadonlyArray< - ActionMatcherDescription -> - -function isStateFunction(x: unknown): x is () => S { - return typeof x === 'function' -} - -export function createReducer>( - initialState: S | (() => S), - builderCallback: (builder: ActionReducerMapBuilder) => void -): ReducerWithInitialState - -export function createReducer< - S extends NotFunction, - CR extends CaseReducers = CaseReducers ->( - initialState: S | (() => S), - actionsMap: CR, - actionMatchers?: ActionMatcherDescriptionCollection, - defaultCaseReducer?: CaseReducer -): ReducerWithInitialState - -export function createReducer>( - initialState: S | (() => S), - mapOrBuilderCallback: - | CaseReducers - | ((builder: ActionReducerMapBuilder) => void), - actionMatchers: ReadonlyActionMatcherDescriptionCollection = [], - defaultCaseReducer?: CaseReducer -): ReducerWithInitialState { - let [actionsMap, finalActionMatchers, finalDefaultCaseReducer] = - typeof mapOrBuilderCallback === 'function' - ? executeReducerBuilderCallback(mapOrBuilderCallback) - : [mapOrBuilderCallback, actionMatchers, defaultCaseReducer] - - // Ensure the initial state gets frozen either way - let getInitialState: () => S - if (isStateFunction(initialState)) { - getInitialState = () => initialState() - } else { - getInitialState = () => initialState - } - - function reducer(state = getInitialState(), action: any): S { - let caseReducers = [ - actionsMap[action.type], - ...finalActionMatchers - .filter(({ matcher }) => matcher(action)) - .map(({ reducer }) => reducer), - ] - if (caseReducers.filter((cr) => !!cr).length === 0) { - caseReducers = [finalDefaultCaseReducer] - } - - return caseReducers.reduce((previousState, caseReducer): S => { - if (caseReducer) { - return caseReducer(previousState, action) as S - } - - return previousState - }, state) - } - - reducer.getInitialState = getInitialState - - return reducer as ReducerWithInitialState -} diff --git a/packages/core/src/redux/toolkit/createSlice.ts b/packages/core/src/redux/toolkit/createSlice.ts deleted file mode 100644 index 361606f4f3..0000000000 --- a/packages/core/src/redux/toolkit/createSlice.ts +++ /dev/null @@ -1,279 +0,0 @@ -import type { Reducer } from 'redux' -import { - ActionCreatorWithoutPayload, - createAction, - PayloadAction, - PrepareAction, - _ActionCreatorWithPreparedPayload, -} from '.' -import { DeepReadonly } from '../../types' -import { PayloadActionCreator } from './createAction' -import { - CaseReducer, - CaseReducers, - createReducer, - ReducerWithInitialState, -} from './createReducer' -import { - ActionReducerMapBuilder, - executeReducerBuilderCallback, -} from './mapBuilders' -import { NoInfer } from './tsHelpers' - -/** - * An action creator attached to a slice. - * - * @deprecated please use PayloadActionCreator directly - * - * @public - */ -export type SliceActionCreator

= PayloadActionCreator

- -/** - * The return value of `createSlice` - * - * @public - */ -export interface Slice< - State = any, - CaseReducers extends SliceCaseReducers = SliceCaseReducers, - Name extends string = string -> { - /** - * The slice name. - */ - name: Name - - /** - * The slice's reducer. - */ - reducer: Reducer> - - /** - * Action creators for the types of actions that are handled by the slice - * reducer. - */ - actions: CaseReducerActions - - /** - * The individual case reducer functions that were passed in the `reducers` parameter. - * This enables reuse and testing if they were defined inline when calling `createSlice`. - */ - caseReducers: SliceDefinedCaseReducers - - /** - * Provides access to the initial state value given to the slice. - * If a lazy state initializer was provided, it will be called and a fresh value returned. - */ - getInitialState: () => DeepReadonly -} - -/** - * Options for `createSlice()`. - * - * @public - */ -export interface CreateSliceOptions< - State = any, - CR extends SliceCaseReducers = SliceCaseReducers, - Name extends string = string -> { - name: Name - initialState: State | (() => State) - reducers: ValidateSliceCaseReducers - extraReducers?: - | CaseReducers, any> - | ((builder: ActionReducerMapBuilder>) => void) -} - -/** - * A CaseReducer with a `prepare` method. - * - * @public - */ -export type CaseReducerWithPrepare = { - reducer: CaseReducer - prepare: PrepareAction -} - -/** - * The type describing a slice's `reducers` option. - * - * @public - */ -export type SliceCaseReducers = { - [K: string]: - | CaseReducer, PayloadAction> - | CaseReducerWithPrepare, PayloadAction> -} - -/** - * Derives the slice's `actions` property from the `reducers` options - * - * @public - */ -export type CaseReducerActions> = { - [Type in keyof CaseReducers]: CaseReducers[Type] extends { prepare: any } - ? ActionCreatorForCaseReducerWithPrepare - : ActionCreatorForCaseReducer -} - -/** - * Get a `PayloadActionCreator` type for a passed `CaseReducerWithPrepare` - * - * @internal - */ -type ActionCreatorForCaseReducerWithPrepare = - _ActionCreatorWithPreparedPayload - -/** - * Get a `PayloadActionCreator` type for a passed `CaseReducer` - * - * @internal - */ -type ActionCreatorForCaseReducer = CR extends ( - state: any, - action: infer Action -) => any - ? Action extends { payload: infer P } - ? PayloadActionCreator

- : ActionCreatorWithoutPayload - : ActionCreatorWithoutPayload - -/** - * Extracts the CaseReducers out of a `reducers` object, even if they are - * tested into a `CaseReducerWithPrepare`. - * - * @internal - */ -type SliceDefinedCaseReducers> = { - [Type in keyof CaseReducers]: CaseReducers[Type] extends { - reducer: infer Reducer - } - ? Reducer - : CaseReducers[Type] -} - -/** - * Used on a SliceCaseReducers object. - * Ensures that if a CaseReducer is a `CaseReducerWithPrepare`, that - * the `reducer` and the `prepare` function use the same type of `payload`. - * - * Might do additional such checks in the future. - * - * This type is only ever useful if you want to write your own wrapper around - * `createSlice`. Please don't use it otherwise! - * - * @public - */ -export type ValidateSliceCaseReducers< - S, - ACR extends SliceCaseReducers -> = ACR & { - [T in keyof ACR]: ACR[T] extends { - reducer(s: S, action?: infer A): any - } - ? { - prepare(...a: never[]): Omit - } - : {} -} - -function getType(slice: string, actionKey: string): string { - return `${slice}/${actionKey}` -} - -/** - * A function that accepts an initial state, an object full of reducer - * functions, and a "slice name", and automatically generates - * action creators and action types that correspond to the - * reducers and state. - * - * The `reducer` argument is passed to `createReducer()`. - * - * @public - */ -export function createSlice< - State, - CaseReducers extends SliceCaseReducers, - Name extends string = string ->( - options: CreateSliceOptions -): Slice { - const { name } = options - if (!name) { - throw new Error('`name` is a required option for createSlice') - } - - // const initialState = - // typeof options.initialState == 'function' - // ? options.initialState - // : createNextState(options.initialState, () => {}) - const initialState = options.initialState - - const reducers = options.reducers || {} - - const reducerNames = Object.keys(reducers) - - const sliceCaseReducersByName: Record = {} - const sliceCaseReducersByType: Record = {} - const actionCreators: Record = {} - - reducerNames.forEach((reducerName) => { - const maybeReducerWithPrepare = reducers[reducerName] - const type = getType(name, reducerName) - - let caseReducer: CaseReducer, any> - let prepareCallback: PrepareAction | undefined - - if ('reducer' in maybeReducerWithPrepare) { - caseReducer = maybeReducerWithPrepare.reducer - prepareCallback = maybeReducerWithPrepare.prepare - } else { - caseReducer = maybeReducerWithPrepare - } - - sliceCaseReducersByName[reducerName] = caseReducer - sliceCaseReducersByType[type] = caseReducer - actionCreators[reducerName] = prepareCallback - ? createAction(type, prepareCallback) - : createAction(type) - }) - - function buildReducer() { - const [ - extraReducers = {}, - actionMatchers = [], - defaultCaseReducer = undefined, - ] = - typeof options.extraReducers === 'function' - ? executeReducerBuilderCallback(options.extraReducers) - : [options.extraReducers] - - const finalCaseReducers = { ...extraReducers, ...sliceCaseReducersByType } - return createReducer( - initialState, - finalCaseReducers as any, - actionMatchers, - defaultCaseReducer - ) - } - - let _reducer: ReducerWithInitialState - - return { - name, - reducer(state, action) { - if (!_reducer) _reducer = buildReducer() - - return _reducer(state, action) - }, - actions: actionCreators as any, - caseReducers: sliceCaseReducersByName as any, - getInitialState() { - if (!_reducer) _reducer = buildReducer() - - return _reducer.getInitialState() - }, - } -} diff --git a/packages/core/src/redux/toolkit/devtoolsExtension.ts b/packages/core/src/redux/toolkit/devtoolsExtension.ts deleted file mode 100644 index fb54a3c0fc..0000000000 --- a/packages/core/src/redux/toolkit/devtoolsExtension.ts +++ /dev/null @@ -1,204 +0,0 @@ -import type { Action, ActionCreator, StoreEnhancer } from 'redux' -import { compose } from 'redux' - -/** - * @public - */ -export interface EnhancerOptions { - /** - * the instance name to be showed on the monitor page. Default value is `document.title`. - * If not specified and there's no document title, it will consist of `tabId` and `instanceId`. - */ - name?: string - /** - * action creators functions to be available in the Dispatcher. - */ - actionCreators?: ActionCreator[] | { [key: string]: ActionCreator } - /** - * if more than one action is dispatched in the indicated interval, all new actions will be collected and sent at once. - * It is the joint between performance and speed. When set to `0`, all actions will be sent instantly. - * Set it to a higher value when experiencing perf issues (also `maxAge` to a lower value). - * - * @default 500 ms. - */ - latency?: number - /** - * (> 1) - maximum allowed actions to be stored in the history tree. The oldest actions are removed once maxAge is reached. It's critical for performance. - * - * @default 50 - */ - maxAge?: number - /** - * See detailed documentation at http://extension.remotedev.io/docs/API/Arguments.html#serialize - */ - serialize?: - | boolean - | { - options?: - | boolean - | { - date?: boolean - regex?: boolean - undefined?: boolean - error?: boolean - symbol?: boolean - map?: boolean - set?: boolean - function?: boolean | Function - } - replacer?: (key: string, value: unknown) => unknown - reviver?: (key: string, value: unknown) => unknown - immutable?: unknown - refs?: unknown[] - } - /** - * function which takes `action` object and id number as arguments, and should return `action` object back. - */ - actionSanitizer?: (action: A, id: number) => A - /** - * function which takes `state` object and index as arguments, and should return `state` object back. - */ - stateSanitizer?: (state: S, index: number) => S - /** - * *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers). - * If `actionsWhitelist` specified, `actionsBlacklist` is ignored. - */ - actionsBlacklist?: string | string[] - /** - * *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers). - * If `actionsWhitelist` specified, `actionsBlacklist` is ignored. - */ - actionsWhitelist?: string | string[] - /** - * called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor. - * Use it as a more advanced version of `actionsBlacklist`/`actionsWhitelist` parameters. - */ - predicate?: (state: S, action: A) => boolean - /** - * if specified as `false`, it will not record the changes till clicking on `Start recording` button. - * Available only for Redux enhancer, for others use `autoPause`. - * - * @default true - */ - shouldRecordChanges?: boolean - /** - * if specified, whenever clicking on `Pause recording` button and there are actions in the history log, will add this action type. - * If not specified, will commit when paused. Available only for Redux enhancer. - * - * @default "@@PAUSED"" - */ - pauseActionType?: string - /** - * auto pauses when the extension’s window is not opened, and so has zero impact on your app when not in use. - * Not available for Redux enhancer (as it already does it but storing the data to be sent). - * - * @default false - */ - autoPause?: boolean - /** - * if specified as `true`, it will not allow any non-monitor actions to be dispatched till clicking on `Unlock changes` button. - * Available only for Redux enhancer. - * - * @default false - */ - shouldStartLocked?: boolean - /** - * if set to `false`, will not recompute the states on hot reloading (or on replacing the reducers). Available only for Redux enhancer. - * - * @default true - */ - shouldHotReload?: boolean - /** - * if specified as `true`, whenever there's an exception in reducers, the monitors will show the error message, and next actions will not be dispatched. - * - * @default false - */ - shouldCatchErrors?: boolean - /** - * If you want to restrict the extension, specify the features you allow. - * If not specified, all of the features are enabled. When set as an object, only those included as `true` will be allowed. - * Note that except `true`/`false`, `import` and `export` can be set as `custom` (which is by default for Redux enhancer), meaning that the importing/exporting occurs on the client side. - * Otherwise, you'll get/set the data right from the monitor part. - */ - features?: { - /** - * start/pause recording of dispatched actions - */ - pause?: boolean - /** - * lock/unlock dispatching actions and side effects - */ - lock?: boolean - /** - * persist states on page reloading - */ - persist?: boolean - /** - * export history of actions in a file - */ - export?: boolean | 'custom' - /** - * import history of actions from a file - */ - import?: boolean | 'custom' - /** - * jump back and forth (time travelling) - */ - jump?: boolean - /** - * skip (cancel) actions - */ - skip?: boolean - /** - * drag and drop actions in the history list - */ - reorder?: boolean - /** - * dispatch custom actions or action creators - */ - dispatch?: boolean - /** - * generate tests for the selected actions - */ - test?: boolean - } - /** - * Set to true or a stacktrace-returning function to record call stack traces for dispatched actions. - * Defaults to false. - */ - trace?: boolean | ((action: A) => string) - /** - * The maximum number of stack trace entries to record per action. Defaults to 10. - */ - traceLimit?: number -} - -/** - * @public - */ -export const composeWithDevTools: { - (options: EnhancerOptions): typeof compose - (...funcs: Array>): StoreEnhancer -} = - typeof window !== 'undefined' && - (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ - ? (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ - : function () { - if (arguments.length === 0) return undefined - if (typeof arguments[0] === 'object') return compose - return compose.apply(null, arguments as any as Function[]) - } - -/** - * @public - */ -export const devToolsEnhancer: { - (options: EnhancerOptions): StoreEnhancer -} = - typeof window !== 'undefined' && (window as any).__REDUX_DEVTOOLS_EXTENSION__ - ? (window as any).__REDUX_DEVTOOLS_EXTENSION__ - : function () { - return function (noop) { - return noop - } - } diff --git a/packages/core/src/redux/toolkit/getDefaultMiddleware.ts b/packages/core/src/redux/toolkit/getDefaultMiddleware.ts deleted file mode 100644 index b9b0bff55e..0000000000 --- a/packages/core/src/redux/toolkit/getDefaultMiddleware.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Middleware } from 'redux' -import { MiddlewareArray } from './utils' - -interface GetDefaultMiddlewareOptions {} - -export type CurriedGetDefaultMiddleware = < - O extends Partial = { - thunk: true - immutableCheck: true - serializableCheck: true - } ->( - options?: O -) => MiddlewareArray> - -export function curryGetDefaultMiddleware< - S = any ->(): CurriedGetDefaultMiddleware { - return function curriedGetDefaultMiddleware() { - return [] as unknown as MiddlewareArray> - } -} diff --git a/packages/core/src/redux/toolkit/index.ts b/packages/core/src/redux/toolkit/index.ts index 78bc6222c1..7fe93c04d7 100644 --- a/packages/core/src/redux/toolkit/index.ts +++ b/packages/core/src/redux/toolkit/index.ts @@ -9,4 +9,3 @@ */ export * from 'redux' export * from './createAction' -export * from './configureStore' diff --git a/packages/core/src/redux/toolkit/isPlainObject.ts b/packages/core/src/redux/toolkit/isPlainObject.ts deleted file mode 100644 index 06fb31aa04..0000000000 --- a/packages/core/src/redux/toolkit/isPlainObject.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Returns true if the passed value is "plain" object, i.e. an object whose - * prototype is the root `Object.prototype`. This includes objects created - * using object literals, but not for instance for class instances. - * - * @param {any} value The value to inspect. - * @returns {boolean} True if the argument appears to be a plain object. - * - * @public - */ -export default function isPlainObject(value: unknown): value is object { - if (typeof value !== 'object' || value === null) return false - - let proto = Object.getPrototypeOf(value) - if (proto === null) return true - - let baseProto = proto - while (Object.getPrototypeOf(baseProto) !== null) { - baseProto = Object.getPrototypeOf(baseProto) - } - - return proto === baseProto -} diff --git a/packages/core/src/redux/toolkit/mapBuilders.ts b/packages/core/src/redux/toolkit/mapBuilders.ts deleted file mode 100644 index 1318cd2108..0000000000 --- a/packages/core/src/redux/toolkit/mapBuilders.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { Action, AnyAction } from "redux" -import { CaseReducer } from "./createReducer" -import { ActionMatcher, ActionMatcherDescriptionCollection, CaseReducers } from "./createReducer" - -export interface TypedActionCreator { - (...args: any[]): Action - type: Type -} - -/** - * A builder for an action <-> reducer map. - * - * @public - */ - export interface ActionReducerMapBuilder { - /** - * Adds a case reducer to handle a single exact action type. - * @remarks - * All calls to `builder.addCase` must come before any calls to `builder.addMatcher` or `builder.addDefaultCase`. - * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type. - * @param reducer - The actual case reducer function. - */ - addCase>( - actionCreator: ActionCreator, - reducer: CaseReducer> - ): ActionReducerMapBuilder - /** - * Adds a case reducer to handle a single exact action type. - * @remarks - * All calls to `builder.addCase` must come before any calls to `builder.addMatcher` or `builder.addDefaultCase`. - * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type. - * @param reducer - The actual case reducer function. - */ - addCase>( - type: Type, - reducer: CaseReducer - ): ActionReducerMapBuilder - - addMatcher( - matcher: ActionMatcher | ((action: AnyAction) => boolean), - reducer: CaseReducer - ): Omit, 'addCase'> - - addDefaultCase(reducer: CaseReducer): {} -} - -export function executeReducerBuilderCallback( - builderCallback: (builder: ActionReducerMapBuilder) => void -): [ - CaseReducers, - ActionMatcherDescriptionCollection, - CaseReducer | undefined -] { - const actionsMap: CaseReducers = {} - const actionMatchers: ActionMatcherDescriptionCollection = [] - let defaultCaseReducer: CaseReducer | undefined - const builder = { - addCase( - typeOrActionCreator: string | TypedActionCreator, - reducer: CaseReducer - ) { - if (process.env.NODE_ENV !== 'production') { - /* - to keep the definition by the user in line with actual behavior, - we enforce `addCase` to always be called before calling `addMatcher` - as matching cases take precedence over matchers - */ - if (actionMatchers.length > 0) { - throw new Error( - '`builder.addCase` should only be called before calling `builder.addMatcher`' - ) - } - if (defaultCaseReducer) { - throw new Error( - '`builder.addCase` should only be called before calling `builder.addDefaultCase`' - ) - } - } - const type = - typeof typeOrActionCreator === 'string' - ? typeOrActionCreator - : typeOrActionCreator.type - if (type in actionsMap) { - throw new Error( - 'addCase cannot be called with two reducers for the same action type' - ) - } - actionsMap[type] = reducer - return builder - }, - addMatcher( - matcher: ActionMatcher, - reducer: CaseReducer - ) { - if (process.env.NODE_ENV !== 'production') { - if (defaultCaseReducer) { - throw new Error( - '`builder.addMatcher` should only be called before calling `builder.addDefaultCase`' - ) - } - } - actionMatchers.push({ matcher, reducer }) - return builder - }, - addDefaultCase(reducer: CaseReducer) { - if (process.env.NODE_ENV !== 'production') { - if (defaultCaseReducer) { - throw new Error('`builder.addDefaultCase` can only be called once') - } - } - defaultCaseReducer = reducer - return builder - }, - } - builderCallback(builder) - return [actionsMap, actionMatchers, defaultCaseReducer] -} diff --git a/packages/core/src/redux/toolkit/utils.ts b/packages/core/src/redux/toolkit/utils.ts deleted file mode 100644 index b577099d97..0000000000 --- a/packages/core/src/redux/toolkit/utils.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { Middleware } from 'redux' - -export class MiddlewareArray< - Middlewares extends Middleware -> extends Array { - constructor(arrayLength?: number) - constructor(...items: Middlewares[]) - constructor(...args: any[]) { - super(...args) - Object.setPrototypeOf(this, MiddlewareArray.prototype) - } - - static get [Symbol.species]() { - return MiddlewareArray as any - } - - concat>>( - items: AdditionalMiddlewares - ): MiddlewareArray - - concat>>( - ...items: AdditionalMiddlewares - ): MiddlewareArray - concat(...arr: any[]) { - return super.concat.apply(this, arr) - } - - prepend>>( - items: AdditionalMiddlewares - ): MiddlewareArray - - prepend>>( - ...items: AdditionalMiddlewares - ): MiddlewareArray - - prepend(...arr: any[]) { - if (arr.length === 1 && Array.isArray(arr[0])) { - return new MiddlewareArray(...arr[0].concat(this)) - } - return new MiddlewareArray(...arr.concat(this)) - } -} diff --git a/packages/core/src/redux/utils/createDestroyableSlice.test.ts b/packages/core/src/redux/utils/createDestroyableSlice.test.ts deleted file mode 100644 index 8d55ac9fb7..0000000000 --- a/packages/core/src/redux/utils/createDestroyableSlice.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createDestroyableSlice } from './createDestroyableSlice' -import { destroyAction } from '../actions' - -describe('createDestroyableSlice', () => { - it('should set the state back to its `initialState` when the `detroyAction` is dispatched', () => { - const initialState = { status: true, id: '1234-6789-1011' } - const slice = createDestroyableSlice({ - name: 'destroyable-slice', - initialState, - reducers: {}, - extraReducers: () => {}, - }) - - expect( - slice.reducer({ status: false, id: '2345-6789-1122' }, destroyAction()) - ).toStrictEqual(initialState) - }) - - it('should allow to pass extra reducers', () => { - const initialState = { status: true, id: '1234-6789-1011' } - const caseReducerState1 = { status: false, id: '2345-6789-1122' } - const caseReducerState2 = { status: true, id: '2345-6789-1122' } - - const slice = createDestroyableSlice({ - name: 'destroyable-slice', - initialState, - reducers: {}, - extraReducers: (builder) => { - builder.addCase('a-new-event-type-1', () => caseReducerState1) - builder.addCase('a-new-event-type-2', () => caseReducerState2) - }, - }) - - expect( - slice.reducer(initialState, { type: 'a-new-event-type-1' }) - ).toStrictEqual(caseReducerState1) - expect( - slice.reducer(initialState, { type: 'a-new-event-type-2' }) - ).toStrictEqual(caseReducerState2) - }) -}) diff --git a/packages/core/src/redux/utils/createDestroyableSlice.ts b/packages/core/src/redux/utils/createDestroyableSlice.ts deleted file mode 100644 index 459cbf29aa..0000000000 --- a/packages/core/src/redux/utils/createDestroyableSlice.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - SliceCaseReducers, - ValidateSliceCaseReducers, - createSlice, -} from '../toolkit/createSlice' -import { ActionReducerMapBuilder } from '../toolkit/mapBuilders' -import { destroyAction } from '../actions' - -export const createDestroyableSlice = < - T, - Reducers extends SliceCaseReducers ->({ - name = '', - initialState, - reducers, - extraReducers, -}: { - name: string - initialState: T - reducers: ValidateSliceCaseReducers - extraReducers?: (builder: ActionReducerMapBuilder) => void -}) => { - return createSlice({ - name, - initialState, - reducers, - extraReducers: (builder) => { - builder.addCase(destroyAction.type, () => { - return initialState - }) - - if (typeof extraReducers === 'function') { - extraReducers(builder) - } - }, - }) -} diff --git a/packages/js/src/BaseRoomSession.test.ts b/packages/js/src/BaseRoomSession.test.ts index f90214fc2c..dc0c67ca64 100644 --- a/packages/js/src/BaseRoomSession.test.ts +++ b/packages/js/src/BaseRoomSession.test.ts @@ -1,4 +1,4 @@ -import { EventEmitter, actions, componentActions } from '@signalwire/core' +import { EventEmitter, actions } from '@signalwire/core' import { BaseRoomSession, createBaseRoomSessionObject } from './BaseRoomSession' import type { RoomSession } from './RoomSession' import { configureFullStack, dispatchMockedRoomSubscribed } from './testUtils' @@ -29,7 +29,7 @@ describe('Room Object', () => { emitter: new EventEmitter(), }) store.dispatch( - componentActions.upsert({ + actions.componentUpsertAction({ id: callId, nodeId: 'node-id', roomId: 'room-id', @@ -92,7 +92,7 @@ describe('Room Object', () => { }) store.dispatch( - componentActions.upsert({ + actions.componentUpsertAction({ id: callId, nodeId: 'node-id', roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', @@ -227,7 +227,7 @@ describe('Room Object', () => { }) store.dispatch( - componentActions.upsert({ + actions.componentUpsertAction({ id: callId, nodeId: 'node-id', roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', @@ -391,7 +391,7 @@ describe('Room Object', () => { // @ts-expect-error room.execute = jest.fn() store.dispatch( - componentActions.upsert({ + actions.componentUpsertAction({ id: callId, nodeId: 'node-id', roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', @@ -581,7 +581,7 @@ describe('Room Object', () => { emitter, }) store.dispatch( - componentActions.upsert({ + actions.componentUpsertAction({ id: callId, nodeId: 'node-id', roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', @@ -629,7 +629,7 @@ describe('Room Object', () => { emitter, }) store.dispatch( - componentActions.upsert({ + actions.componentUpsertAction({ id: callId, nodeId: 'node-id', roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', diff --git a/packages/js/src/video/childMemberJoinedWorker.test.ts b/packages/js/src/video/childMemberJoinedWorker.test.ts index 6b74b814f0..06bf22fbea 100644 --- a/packages/js/src/video/childMemberJoinedWorker.test.ts +++ b/packages/js/src/video/childMemberJoinedWorker.test.ts @@ -1,4 +1,4 @@ -import { testUtils, componentActions, sagaHelpers } from '@signalwire/core' +import { testUtils, actions, sagaHelpers } from '@signalwire/core' import { expectSaga } from 'redux-saga-test-plan' import { childMemberJoinedWorker } from './childMemberJoinedWorker' @@ -79,7 +79,7 @@ describe('childMemberJoinedWorker', () => { }, ]) .put( - componentActions.upsert({ + actions.componentUpsertAction({ id: 'callId', roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', roomSessionId: '313bedbe-edc9-4653-b332-34fbf43e8289', diff --git a/packages/js/src/video/childMemberJoinedWorker.ts b/packages/js/src/video/childMemberJoinedWorker.ts index cb7336ac52..639d9019b8 100644 --- a/packages/js/src/video/childMemberJoinedWorker.ts +++ b/packages/js/src/video/childMemberJoinedWorker.ts @@ -8,7 +8,7 @@ import { SDKWorkerHooks, VideoMemberJoinedEvent, componentSelectors, - componentActions, + actions, type ReduxComponent, } from '@signalwire/core' @@ -67,7 +67,7 @@ export const childMemberJoinedWorker: SDKWorker< instance.applyEmitterTransforms() yield sagaEffects.put( - componentActions.upsert({ + actions.componentUpsertAction({ id: instance.callId, roomId: action.payload.room_id, roomSessionId: action.payload.room_session_id, diff --git a/packages/webrtc/src/workers/promoteDemoteWorker.ts b/packages/webrtc/src/workers/promoteDemoteWorker.ts index 5a5858d9e5..26eef5885c 100644 --- a/packages/webrtc/src/workers/promoteDemoteWorker.ts +++ b/packages/webrtc/src/workers/promoteDemoteWorker.ts @@ -8,7 +8,7 @@ import { SDKWorkerHooks, VideoMemberPromotedEvent, VideoMemberDemotedEvent, - sessionActions, + actions, selectors, VideoAuthorization, } from '@signalwire/core' @@ -50,7 +50,7 @@ export const promoteDemoteWorker: SDKWorker< getLogger().debug('promoteDemoteWorker:', action.type, action.payload) yield sagaEffects.put( - sessionActions.updateAuthState(action.payload.authorization) + actions.sessionAuthStateAction(action.payload.authorization) ) const authState: VideoAuthorization = yield sagaEffects.select( selectors.getAuthState diff --git a/packages/webrtc/src/workers/roomSubscribedWorker.ts b/packages/webrtc/src/workers/roomSubscribedWorker.ts index 97c9654e6e..b29a2e5462 100644 --- a/packages/webrtc/src/workers/roomSubscribedWorker.ts +++ b/packages/webrtc/src/workers/roomSubscribedWorker.ts @@ -7,7 +7,7 @@ import { MapToPubSubShape, SDKWorkerHooks, VideoRoomSubscribedEvent, - componentActions, + actions, } from '@signalwire/core' import { BaseConnection } from '../BaseConnection' @@ -55,7 +55,7 @@ export const roomSubscribedWorker: SDKWorker< * TODO: Replace the redux action/component with properties on RTCPeer instance? */ yield sagaEffects.put( - componentActions.upsert({ + actions.componentUpsertAction({ id: action.payload.call_id, roomId: action.payload.room_session.room_id, roomSessionId: action.payload.room_session.id,