This commit is contained in:
2025-09-05 03:05:41 -04:00
parent c474596339
commit fdefdbd12e
50 changed files with 327 additions and 468 deletions

View File

@@ -30,8 +30,7 @@ const initialState: ChannelsPublishingState = {
};
// Selectors
export const channelsPublishingSelector = (state: RootState): ChannelsPublishingState =>
state.channelsPublishing;
export const channelsPublishingSelector = (state: RootState): ChannelsPublishingState => state.channelsPublishing;
export const selectChannelsPublishingState = createSelector(
[channelsPublishingSelector],
@@ -45,8 +44,7 @@ export const selectChannelsPublishingLoading = createSelector(
export const selectChannelPublishingState = createSelector(
[selectChannelsPublishingState, (_: RootState, channelId: string) => channelId],
(publishingStates: ChannelPublishingState[], channelId: string) =>
publishingStates.find(state => state.channelId === channelId)
(publishingStates: ChannelPublishingState[], channelId: string) => publishingStates.find(state => state.channelId === channelId)
);
// Async thunks
@@ -108,7 +106,7 @@ const channelsPublishingSlice = createSlice({
name: 'channelsPublishing',
initialState,
reducers: {
clearPublishingState: (state) => {
clearPublishingState: state => {
state.publishingState = [];
state.error = null;
},
@@ -116,10 +114,8 @@ const channelsPublishingSlice = createSlice({
state.error = action.payload;
},
updateChannelState: (state, action: PayloadAction<ChannelPublishingState>) => {
const index = state.publishingState.findIndex(
item => item.channelId === action.payload.channelId
);
const index = state.publishingState.findIndex(item => item.channelId === action.payload.channelId);
if (index >= 0) {
state.publishingState[index] = action.payload;
} else {
@@ -127,9 +123,9 @@ const channelsPublishingSlice = createSlice({
}
}
},
extraReducers: (builder) => {
extraReducers: builder => {
builder
.addCase(fetchChannelsPublishingState.pending, (state) => {
.addCase(fetchChannelsPublishingState.pending, state => {
state.isLoading = true;
state.error = null;
})
@@ -144,10 +140,8 @@ const channelsPublishingSlice = createSlice({
state.error = action.payload as string;
})
.addCase(updateChannelPublishingState.fulfilled, (state, action) => {
const index = state.publishingState.findIndex(
item => item.channelId === action.payload.channelId
);
const index = state.publishingState.findIndex(item => item.channelId === action.payload.channelId);
if (index >= 0) {
state.publishingState[index] = action.payload;
} else {
@@ -161,4 +155,4 @@ const channelsPublishingSlice = createSlice({
});
export const {clearPublishingState, setPublishingError, updateChannelState} = channelsPublishingSlice.actions;
export default channelsPublishingSlice.reducer;
export default channelsPublishingSlice.reducer;

View File

@@ -9,118 +9,85 @@ 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 selectChannelList = createSelector([channelsSelector], (channels: IChannelsState) => channels.channels);
export const selectChannelsLoading = createSelector(
[channelsSelector],
(channels: IChannelsState) => channels.isLoading
);
export const selectChannelsLoading = createSelector([channelsSelector], (channels: IChannelsState) => channels.isLoading);
export const selectChannelsError = createSelector(
[channelsSelector],
(channels: IChannelsState) => channels.error
);
export const selectChannelsError = createSelector([channelsSelector], (channels: IChannelsState) => channels.error);
export const selectSelectedChannel = createSelector(
[channelsSelector],
(channels: IChannelsState) => channels.selectedChannel
);
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 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 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 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 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 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 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 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';
export * from '../slices/Channels.slice';

View File

@@ -10,7 +10,7 @@ export enum StoreScreensType {
ChannelDetail = 'channelDetail',
Settings = 'settings',
Login = 'login',
Channels = "Channels"
Channels = 'Channels'
}
// Screen state interface
@@ -35,25 +35,13 @@ const initialState: ScreenState = {
// Selectors
export const screensSelector = (state: RootState): ScreenState => state.screens;
export const selectCurrentScreen = createSelector(
[screensSelector],
(screens: ScreenState) => screens.currentScreen
);
export const selectCurrentScreen = createSelector([screensSelector], (screens: ScreenState) => screens.currentScreen);
export const selectScreenProps = createSelector(
[screensSelector],
(screens: ScreenState) => screens.screenProps
);
export const selectScreenProps = createSelector([screensSelector], (screens: ScreenState) => screens.screenProps);
export const selectPreviousScreen = createSelector(
[screensSelector],
(screens: ScreenState) => screens.previousScreen
);
export const selectPreviousScreen = createSelector([screensSelector], (screens: ScreenState) => screens.previousScreen);
export const selectNavigationHistory = createSelector(
[screensSelector],
(screens: ScreenState) => screens.navigationHistory
);
export const selectNavigationHistory = createSelector([screensSelector], (screens: ScreenState) => screens.navigationHistory);
// Slice
const screensSlice = createSlice({
@@ -63,7 +51,7 @@ const screensSlice = createSlice({
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) {
@@ -82,28 +70,28 @@ const screensSlice = createSlice({
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) => {
navigateBack: state => {
if (state.previousScreen) {
const temp = state.currentScreen;
state.currentScreen = state.previousScreen;
state.previousScreen = temp;
}
},
clearScreenProps: (state) => {
clearScreenProps: state => {
state.screenProps = {};
},
resetNavigation: (state) => {
resetNavigation: state => {
state.currentScreen = StoreScreensType.Login;
state.previousScreen = null;
state.screenProps = {};
@@ -112,14 +100,6 @@ const screensSlice = createSlice({
}
});
export const {
setCurrentScreen,
setScreenProps,
updateScreenProps,
navigateToScreen,
navigateBack,
clearScreenProps,
resetNavigation
} = screensSlice.actions;
export const {setCurrentScreen, setScreenProps, updateScreenProps, navigateToScreen, navigateBack, clearScreenProps, resetNavigation} = screensSlice.actions;
export default screensSlice.reducer;
export default screensSlice.reducer;

View File

@@ -56,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) {

View File

@@ -4,4 +4,4 @@
// Re-export from the action module for backwards compatibility
export {StoreScreensType} from '../action/screens';
export type {ScreenState, ScreenProps} from '../action/screens';
export type {ScreenState, ScreenProps} from '../action/screens';

View File

@@ -24,7 +24,6 @@ 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);
}