Files
WebControlCenter/src/store/action/channels-publishing.ts
2025-09-05 03:05:41 -04:00

159 lines
5.3 KiB
TypeScript

/**
* 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;