maintenance
This commit is contained in:
164
src/store/action/channels-publishing.ts
Normal file
164
src/store/action/channels-publishing.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import {createAsyncThunk, createSelector, createSlice, PayloadAction} from '@reduxjs/toolkit';
|
||||
import {Channel} from '@phenixrts/sdk';
|
||||
import channelService from '../../services/channel.service';
|
||||
import {RootState} from '../index';
|
||||
|
||||
// Publishing state interface
|
||||
export interface ChannelPublishingState {
|
||||
channelId: string;
|
||||
isOnline: boolean;
|
||||
publisherCount: number;
|
||||
streamCount: number;
|
||||
lastUpdated: string;
|
||||
}
|
||||
|
||||
export interface ChannelsPublishingState {
|
||||
publishingState: ChannelPublishingState[];
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
lastFetched: string | null;
|
||||
}
|
||||
|
||||
const initialState: ChannelsPublishingState = {
|
||||
publishingState: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
lastFetched: null
|
||||
};
|
||||
|
||||
// Selectors
|
||||
export const channelsPublishingSelector = (state: RootState): ChannelsPublishingState =>
|
||||
state.channelsPublishing;
|
||||
|
||||
export const selectChannelsPublishingState = createSelector(
|
||||
[channelsPublishingSelector],
|
||||
(channelsPublishing: ChannelsPublishingState) => channelsPublishing.publishingState
|
||||
);
|
||||
|
||||
export const selectChannelsPublishingLoading = createSelector(
|
||||
[channelsPublishingSelector],
|
||||
(channelsPublishing: ChannelsPublishingState) => channelsPublishing.isLoading
|
||||
);
|
||||
|
||||
export const selectChannelPublishingState = createSelector(
|
||||
[selectChannelsPublishingState, (_: RootState, channelId: string) => channelId],
|
||||
(publishingStates: ChannelPublishingState[], channelId: string) =>
|
||||
publishingStates.find(state => state.channelId === channelId)
|
||||
);
|
||||
|
||||
// Async thunks
|
||||
export const fetchChannelsPublishingState = createAsyncThunk(
|
||||
'channelsPublishing/fetchChannelsPublishingState',
|
||||
async (channels: Channel[], {rejectWithValue}) => {
|
||||
try {
|
||||
const publishingStates = await Promise.all(
|
||||
channels.map(async (channel): Promise<ChannelPublishingState> => {
|
||||
try {
|
||||
const publisherCount = await channelService.getPublisherCount(channel.channelId);
|
||||
return {
|
||||
channelId: channel.channelId,
|
||||
isOnline: publisherCount > 0,
|
||||
publisherCount,
|
||||
streamCount: publisherCount, // Assuming 1:1 for now
|
||||
lastUpdated: new Date().toISOString()
|
||||
};
|
||||
} catch (error) {
|
||||
// If we can't get publisher count, assume offline
|
||||
return {
|
||||
channelId: channel.channelId,
|
||||
isOnline: false,
|
||||
publisherCount: 0,
|
||||
streamCount: 0,
|
||||
lastUpdated: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return publishingStates;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error instanceof Error ? error.message : 'Failed to fetch publishing state');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const updateChannelPublishingState = createAsyncThunk(
|
||||
'channelsPublishing/updateChannelPublishingState',
|
||||
async (channelId: string, {rejectWithValue}) => {
|
||||
try {
|
||||
const publisherCount = await channelService.getPublisherCount(channelId);
|
||||
return {
|
||||
channelId,
|
||||
isOnline: publisherCount > 0,
|
||||
publisherCount,
|
||||
streamCount: publisherCount,
|
||||
lastUpdated: new Date().toISOString()
|
||||
} as ChannelPublishingState;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error instanceof Error ? error.message : 'Failed to update publishing state');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Slice
|
||||
const channelsPublishingSlice = createSlice({
|
||||
name: 'channelsPublishing',
|
||||
initialState,
|
||||
reducers: {
|
||||
clearPublishingState: (state) => {
|
||||
state.publishingState = [];
|
||||
state.error = null;
|
||||
},
|
||||
setPublishingError: (state, action: PayloadAction<string | null>) => {
|
||||
state.error = action.payload;
|
||||
},
|
||||
updateChannelState: (state, action: PayloadAction<ChannelPublishingState>) => {
|
||||
const index = state.publishingState.findIndex(
|
||||
item => item.channelId === action.payload.channelId
|
||||
);
|
||||
|
||||
if (index >= 0) {
|
||||
state.publishingState[index] = action.payload;
|
||||
} else {
|
||||
state.publishingState.push(action.payload);
|
||||
}
|
||||
}
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(fetchChannelsPublishingState.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(fetchChannelsPublishingState.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.publishingState = action.payload;
|
||||
state.lastFetched = new Date().toISOString();
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(fetchChannelsPublishingState.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload as string;
|
||||
})
|
||||
.addCase(updateChannelPublishingState.fulfilled, (state, action) => {
|
||||
const index = state.publishingState.findIndex(
|
||||
item => item.channelId === action.payload.channelId
|
||||
);
|
||||
|
||||
if (index >= 0) {
|
||||
state.publishingState[index] = action.payload;
|
||||
} else {
|
||||
state.publishingState.push(action.payload);
|
||||
}
|
||||
})
|
||||
.addCase(updateChannelPublishingState.rejected, (state, action) => {
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const {clearPublishingState, setPublishingError, updateChannelState} = channelsPublishingSlice.actions;
|
||||
export default channelsPublishingSlice.reducer;
|
||||
126
src/store/action/channels.ts
Normal file
126
src/store/action/channels.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import {createAsyncThunk, createSelector} from '@reduxjs/toolkit';
|
||||
import channelService, {CreateChannelParams, DeleteChannelParams, ForkChannelParams, KillChannelParams} from '../../services/channel.service';
|
||||
import {RootState} from '../index';
|
||||
import {IChannelsState} from '../slices/Channels.slice';
|
||||
|
||||
// Selectors
|
||||
export const channelsSelector = (state: RootState): IChannelsState => state.channels;
|
||||
|
||||
export const selectChannelList = createSelector(
|
||||
[channelsSelector],
|
||||
(channels: IChannelsState) => channels.channels
|
||||
);
|
||||
|
||||
export const selectChannelsLoading = createSelector(
|
||||
[channelsSelector],
|
||||
(channels: IChannelsState) => channels.isLoading
|
||||
);
|
||||
|
||||
export const selectChannelsError = createSelector(
|
||||
[channelsSelector],
|
||||
(channels: IChannelsState) => channels.error
|
||||
);
|
||||
|
||||
export const selectSelectedChannel = createSelector(
|
||||
[channelsSelector],
|
||||
(channels: IChannelsState) => channels.selectedChannel
|
||||
);
|
||||
|
||||
// Async thunks for channel operations
|
||||
export const listChannels = createAsyncThunk(
|
||||
'channels/listChannels',
|
||||
async (_, {rejectWithValue}) => {
|
||||
try {
|
||||
const channels = await channelService.listChannels();
|
||||
return channels;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error instanceof Error ? error.message : 'Failed to fetch channels');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const createChannelThunk = createAsyncThunk(
|
||||
'channels/createChannel',
|
||||
async (params: CreateChannelParams, {rejectWithValue, dispatch}) => {
|
||||
try {
|
||||
const newChannel = await channelService.createChannel(params);
|
||||
// Refresh the channel list after creation
|
||||
dispatch(listChannels());
|
||||
return newChannel;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error instanceof Error ? error.message : 'Failed to create channel');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteChannelThunk = createAsyncThunk(
|
||||
'channels/deleteChannel',
|
||||
async (params: DeleteChannelParams, {rejectWithValue, dispatch}) => {
|
||||
try {
|
||||
await channelService.deleteChannel(params);
|
||||
// Refresh the channel list after deletion
|
||||
dispatch(listChannels());
|
||||
return params.channelId;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error instanceof Error ? error.message : 'Failed to delete channel');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const forkChannelThunk = createAsyncThunk(
|
||||
'channels/forkChannel',
|
||||
async (params: ForkChannelParams, {rejectWithValue, dispatch}) => {
|
||||
try {
|
||||
await channelService.forkChannel(params);
|
||||
// Refresh the channel list after forking
|
||||
dispatch(listChannels());
|
||||
return params;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error instanceof Error ? error.message : 'Failed to fork channel');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const killChannelThunk = createAsyncThunk(
|
||||
'channels/killChannel',
|
||||
async (params: KillChannelParams, {rejectWithValue, dispatch}) => {
|
||||
try {
|
||||
await channelService.killChannel(params);
|
||||
// Refresh the channel list after killing
|
||||
dispatch(listChannels());
|
||||
return params.channelId;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error instanceof Error ? error.message : 'Failed to kill channel');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const getChannelThunk = createAsyncThunk(
|
||||
'channels/getChannel',
|
||||
async (channelId: string, {rejectWithValue}) => {
|
||||
try {
|
||||
const channel = await channelService.getChannel(channelId);
|
||||
return channel;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error instanceof Error ? error.message : 'Failed to get channel');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const getPublisherCountThunk = createAsyncThunk(
|
||||
'channels/getPublisherCount',
|
||||
async (channelId: string, {rejectWithValue}) => {
|
||||
try {
|
||||
const count = await channelService.getPublisherCount(channelId);
|
||||
return {channelId, count};
|
||||
} catch (error) {
|
||||
return rejectWithValue(error instanceof Error ? error.message : 'Failed to get publisher count');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Export all actions and selectors
|
||||
export * from '../slices/Channels.slice';
|
||||
125
src/store/action/screens.ts
Normal file
125
src/store/action/screens.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import {createSelector, createSlice, PayloadAction} from '@reduxjs/toolkit';
|
||||
import {RootState} from '../index';
|
||||
|
||||
// Screen types
|
||||
export enum StoreScreensType {
|
||||
ChannelList = 'channelList',
|
||||
ChannelDetail = 'channelDetail',
|
||||
Settings = 'settings',
|
||||
Login = 'login',
|
||||
Channels = "Channels"
|
||||
}
|
||||
|
||||
// Screen state interface
|
||||
export interface ScreenProps {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ScreenState {
|
||||
currentScreen: StoreScreensType;
|
||||
screenProps: ScreenProps;
|
||||
previousScreen: StoreScreensType | null;
|
||||
navigationHistory: StoreScreensType[];
|
||||
}
|
||||
|
||||
const initialState: ScreenState = {
|
||||
currentScreen: StoreScreensType.Login,
|
||||
screenProps: {},
|
||||
previousScreen: null,
|
||||
navigationHistory: []
|
||||
};
|
||||
|
||||
// Selectors
|
||||
export const screensSelector = (state: RootState): ScreenState => state.screens;
|
||||
|
||||
export const selectCurrentScreen = createSelector(
|
||||
[screensSelector],
|
||||
(screens: ScreenState) => screens.currentScreen
|
||||
);
|
||||
|
||||
export const selectScreenProps = createSelector(
|
||||
[screensSelector],
|
||||
(screens: ScreenState) => screens.screenProps
|
||||
);
|
||||
|
||||
export const selectPreviousScreen = createSelector(
|
||||
[screensSelector],
|
||||
(screens: ScreenState) => screens.previousScreen
|
||||
);
|
||||
|
||||
export const selectNavigationHistory = createSelector(
|
||||
[screensSelector],
|
||||
(screens: ScreenState) => screens.navigationHistory
|
||||
);
|
||||
|
||||
// Slice
|
||||
const screensSlice = createSlice({
|
||||
name: 'screens',
|
||||
initialState,
|
||||
reducers: {
|
||||
setCurrentScreen: (state, action: PayloadAction<StoreScreensType>) => {
|
||||
state.previousScreen = state.currentScreen;
|
||||
state.currentScreen = action.payload;
|
||||
|
||||
// Add to navigation history (keep last 10)
|
||||
state.navigationHistory.push(action.payload);
|
||||
if (state.navigationHistory.length > 10) {
|
||||
state.navigationHistory.shift();
|
||||
}
|
||||
},
|
||||
setScreenProps: (state, action: PayloadAction<ScreenProps>) => {
|
||||
state.screenProps = action.payload;
|
||||
},
|
||||
updateScreenProps: (state, action: PayloadAction<Partial<ScreenProps>>) => {
|
||||
state.screenProps = {
|
||||
...state.screenProps,
|
||||
...action.payload
|
||||
};
|
||||
},
|
||||
navigateToScreen: (state, action: PayloadAction<{screen: StoreScreensType; props?: ScreenProps}>) => {
|
||||
state.previousScreen = state.currentScreen;
|
||||
state.currentScreen = action.payload.screen;
|
||||
|
||||
if (action.payload.props) {
|
||||
state.screenProps = action.payload.props;
|
||||
}
|
||||
|
||||
// Add to navigation history
|
||||
state.navigationHistory.push(action.payload.screen);
|
||||
if (state.navigationHistory.length > 10) {
|
||||
state.navigationHistory.shift();
|
||||
}
|
||||
},
|
||||
navigateBack: (state) => {
|
||||
if (state.previousScreen) {
|
||||
const temp = state.currentScreen;
|
||||
state.currentScreen = state.previousScreen;
|
||||
state.previousScreen = temp;
|
||||
}
|
||||
},
|
||||
clearScreenProps: (state) => {
|
||||
state.screenProps = {};
|
||||
},
|
||||
resetNavigation: (state) => {
|
||||
state.currentScreen = StoreScreensType.Login;
|
||||
state.previousScreen = null;
|
||||
state.screenProps = {};
|
||||
state.navigationHistory = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const {
|
||||
setCurrentScreen,
|
||||
setScreenProps,
|
||||
updateScreenProps,
|
||||
navigateToScreen,
|
||||
navigateBack,
|
||||
clearScreenProps,
|
||||
resetNavigation
|
||||
} = screensSlice.actions;
|
||||
|
||||
export default screensSlice.reducer;
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
authenticateCredentialsThunk,
|
||||
setError,
|
||||
selectIsAuthenticated,
|
||||
selectIsLoading,
|
||||
selectApplicationId,
|
||||
@@ -17,7 +16,7 @@ export const authenticateRequestMiddleware: Middleware = store => next => async
|
||||
const secret = selectSecret(state);
|
||||
|
||||
console.log(
|
||||
'[authenticateRequest] action [%o] isAuthenticated [%o] isLoading [%o] applicationId [%o] secret [%o]',
|
||||
'[authenticateRequestMiddleware] action [%o] isAuthenticated [%o] isLoading [%o] applicationId [%o] secret [%o]',
|
||||
action,
|
||||
isAuthenticated,
|
||||
isLoading,
|
||||
@@ -31,8 +30,8 @@ export const authenticateRequestMiddleware: Middleware = store => next => async
|
||||
typeof action === 'object' &&
|
||||
action !== null &&
|
||||
'type' in action &&
|
||||
typeof (action as any).type === 'string' &&
|
||||
(action as any).type.startsWith('authentication/')
|
||||
typeof (action as {type: string}).type === 'string' &&
|
||||
(action as {type: string}).type.startsWith('authentication/')
|
||||
) {
|
||||
return next(action);
|
||||
}
|
||||
@@ -57,13 +56,13 @@ export const authenticateRequestMiddleware: Middleware = store => next => async
|
||||
try {
|
||||
console.log('[authenticateRequest] Attempting auto-authentication');
|
||||
// Use the Redux thunk to properly update the state
|
||||
const authResult = await store.dispatch(authenticateCredentialsThunk({applicationId, secret}) as any);
|
||||
const authResult = await store.dispatch(authenticateCredentialsThunk({ applicationId, secret }) as any);
|
||||
|
||||
if (authResult.type.endsWith('/rejected') || authResult.payload === 'Authentication failed') {
|
||||
console.log('[authenticateRequest] Authentication failed');
|
||||
return next(setUnauthorized());
|
||||
}
|
||||
|
||||
|
||||
console.log('[authenticateRequest] Auto-authentication successful, proceeding with action');
|
||||
return next(action);
|
||||
} catch (error) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {Middleware} from '@reduxjs/toolkit';
|
||||
* Logs all actions and states after they are dispatched.
|
||||
*/
|
||||
export const loggerMiddleware: Middleware = store => next => action => {
|
||||
console.group((action as any).type);
|
||||
console.group((action as {type: string}).type);
|
||||
console.info('dispatching', action);
|
||||
const result = next(action);
|
||||
console.log('next state', store.getState());
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {Middleware} from '@reduxjs/toolkit';
|
||||
|
||||
export const vanillaPromiseMiddleware: Middleware = store => next => (action: any) => {
|
||||
if (typeof action.then !== 'function') {
|
||||
export const vanillaPromiseMiddleware: Middleware = store => next => (action: unknown) => {
|
||||
if (typeof (action as {then?: unknown}).then !== 'function') {
|
||||
return next(action);
|
||||
}
|
||||
|
||||
return Promise.resolve(action).then((resolvedAction: any) => store.dispatch(resolvedAction));
|
||||
return Promise.resolve(action).then((resolvedAction: unknown) => store.dispatch(resolvedAction));
|
||||
};
|
||||
|
||||
7
src/store/reducers/screens.ts
Normal file
7
src/store/reducers/screens.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
|
||||
// Re-export from the action module for backwards compatibility
|
||||
export {StoreScreensType} from '../action/screens';
|
||||
export type {ScreenState, ScreenProps} from '../action/screens';
|
||||
@@ -2,6 +2,8 @@ import {createSlice, PayloadAction, createAsyncThunk, createSelector} from '@red
|
||||
import AuthenticationService from '../../services/Authentication.service';
|
||||
import {PhenixWebSocketStatusType} from 'services/net/websockets/PhenixWebSocketStatus';
|
||||
import {IPhenixWebSocketResponse} from 'services/net/websockets/PhenixWebSocket';
|
||||
import PCastApiService from 'services/PCastApi.service';
|
||||
import ChannelService from 'services/Channel.service';
|
||||
|
||||
export interface IAuthenticationState {
|
||||
applicationId: string | null;
|
||||
@@ -137,6 +139,12 @@ const authenticationSlice = createSlice({
|
||||
state.roles = authenticationResponse.roles ?? [];
|
||||
state.status = 'Online';
|
||||
state.isLoading = false;
|
||||
|
||||
PCastApiService.initialize('https://pcast-stg.phenixrts.com', {
|
||||
id: state.applicationId ?? 'phenixrts.com-alex.zinn',
|
||||
secret: state.secret ?? ''
|
||||
});
|
||||
ChannelService.initializeWithPCastApi(PCastApiService.channels);
|
||||
} else {
|
||||
state.applicationId = null;
|
||||
state.sessionId = null;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {createAsyncThunk, createSelector, createSlice, PayloadAction, WritableDraft} from '@reduxjs/toolkit';
|
||||
import {ApplicationCredentials, Channel} from '@techniker-me/pcast-api';
|
||||
import PCastApiService from 'services/PCastApi.service';
|
||||
import {listChannels, createChannelThunk, deleteChannelThunk, getChannelThunk, getPublisherCountThunk} from '../action/channels';
|
||||
|
||||
export interface IChannelsState {
|
||||
isLoading: boolean;
|
||||
@@ -23,6 +24,7 @@ export const selectChannelList = createSelector([selectChannels], channels => ch
|
||||
export const fetchChannelList = createAsyncThunk('channels/fetchChannelList', async (_, {rejectWithValue}) => {
|
||||
try {
|
||||
return PCastApiService.channels.list();
|
||||
|
||||
} catch (error) {
|
||||
return rejectWithValue(error);
|
||||
}
|
||||
@@ -72,30 +74,110 @@ const channelsSlice = createSlice({
|
||||
}
|
||||
},
|
||||
extraReducers: builder => {
|
||||
builder.addCase(fetchChannelList.pending, state => {
|
||||
state.isLoading = true;
|
||||
});
|
||||
builder.addCase(fetchChannelList.fulfilled, (state, action) => {
|
||||
state.channels = action.payload as WritableDraft<Channel[]>;
|
||||
state.isLoading = false;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchChannelList.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
builder.addCase(fetchChannelsListPublisherStatus.pending, state => {
|
||||
state.isLoading = true;
|
||||
});
|
||||
builder.addCase(fetchChannelsListPublisherStatus.fulfilled, (state, action) => {
|
||||
state.channels = action.payload as WritableDraft<Channel[]>;
|
||||
state.isLoading = false;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchChannelsListPublisherStatus.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
builder
|
||||
// fetchChannelList cases
|
||||
.addCase(fetchChannelList.pending, state => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(fetchChannelList.fulfilled, (state, action) => {
|
||||
state.channels = action.payload as WritableDraft<Channel[]>;
|
||||
state.isLoading = false;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(fetchChannelList.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload as string;
|
||||
})
|
||||
// fetchChannelsListPublisherStatus cases
|
||||
.addCase(fetchChannelsListPublisherStatus.pending, state => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(fetchChannelsListPublisherStatus.fulfilled, (state, action) => {
|
||||
state.channels = action.payload as WritableDraft<Channel[]>;
|
||||
state.isLoading = false;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(fetchChannelsListPublisherStatus.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload as string;
|
||||
})
|
||||
// listChannels cases (used by the component)
|
||||
.addCase(listChannels.pending, state => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(listChannels.fulfilled, (state, action) => {
|
||||
state.channels = action.payload as WritableDraft<Channel[]>;
|
||||
state.isLoading = false;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(listChannels.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload as string;
|
||||
})
|
||||
// createChannelThunk cases
|
||||
.addCase(createChannelThunk.pending, state => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(createChannelThunk.fulfilled, (state, _action) => {
|
||||
// Channel is already added to the list by the thunk calling listChannels
|
||||
state.isLoading = false;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(createChannelThunk.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload as string;
|
||||
})
|
||||
// deleteChannelThunk cases
|
||||
.addCase(deleteChannelThunk.pending, state => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(deleteChannelThunk.fulfilled, (state, _action) => {
|
||||
// Channel is already removed from the list by the thunk calling listChannels
|
||||
state.isLoading = false;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(deleteChannelThunk.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload as string;
|
||||
})
|
||||
// getChannelThunk cases
|
||||
.addCase(getChannelThunk.pending, state => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(getChannelThunk.fulfilled, (state, action) => {
|
||||
state.selectedChannel = action.payload as WritableDraft<Channel>;
|
||||
state.isLoading = false;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(getChannelThunk.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload as string;
|
||||
})
|
||||
// getPublisherCountThunk cases
|
||||
.addCase(getPublisherCountThunk.pending, state => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(getPublisherCountThunk.fulfilled, (state, action) => {
|
||||
// Update the specific channel with publisher count
|
||||
const {channelId, count} = action.payload;
|
||||
const channelIndex = state.channels.findIndex(channel => channel.channelId === channelId);
|
||||
if (channelIndex !== -1) {
|
||||
(state.channels[channelIndex] as any).publisherCount = count;
|
||||
}
|
||||
state.isLoading = false;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(getPublisherCountThunk.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload as string;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import {configureStore} from '@reduxjs/toolkit';
|
||||
import AuthenticationReducer from './slices/Authentication.slice';
|
||||
import ChannelsReducer from './slices/Channels.slice';
|
||||
import ChannelsPublishingReducer from './action/channels-publishing';
|
||||
import ScreensReducer from './action/screens';
|
||||
import {authenticateRequestMiddleware, loggerMiddleware, vanillaPromiseMiddleware} from './middlewares';
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
authentication: AuthenticationReducer,
|
||||
channels: ChannelsReducer
|
||||
channels: ChannelsReducer,
|
||||
channelsPublishing: ChannelsPublishingReducer,
|
||||
screens: ScreensReducer
|
||||
},
|
||||
middleware: getDefaultMiddleware =>
|
||||
getDefaultMiddleware({
|
||||
|
||||
Reference in New Issue
Block a user