159 lines
5.3 KiB
TypeScript
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;
|