/** * 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 => { 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) => { state.error = action.payload; }, updateChannelState: (state, action: PayloadAction) => { 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;