AeThex-Connect/packages/web/src/store/index.ts

171 lines
4 KiB
TypeScript

import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
// Auth Slice
interface User {
id: string;
email: string;
username?: string;
}
interface AuthState {
user: User | null;
loading: boolean;
error: string | null;
}
const initialAuthState: AuthState = {
user: null,
loading: false,
error: null,
};
const authSlice = createSlice({
name: 'auth',
initialState: initialAuthState,
reducers: {
setLoading: (state, action: PayloadAction<boolean>) => {
state.loading = action.payload;
},
setUser: (state, action: PayloadAction<User | null>) => {
state.user = action.payload;
state.loading = false;
state.error = null;
},
setError: (state, action: PayloadAction<string>) => {
state.error = action.payload;
state.loading = false;
},
logout: (state) => {
state.user = null;
state.loading = false;
state.error = null;
},
},
});
export const { setLoading, setUser, setError, logout } = authSlice.actions;
// Messaging Slice
interface Conversation {
id: string;
participantName: string;
lastMessage: string;
unreadCount: number;
}
interface Message {
id: string;
conversationId: string;
content: string;
senderId: string;
timestamp: string;
createdAt: string;
}
interface MessagingState {
conversations: Conversation[];
messages: Record<string, Message[]>;
}
const initialMessagingState: MessagingState = {
conversations: [],
messages: {},
};
const messagingSlice = createSlice({
name: 'messaging',
initialState: initialMessagingState,
reducers: {
setConversations: (state, action: PayloadAction<Conversation[]>) => {
state.conversations = action.payload;
},
addMessage: (state, action: PayloadAction<Message>) => {
const msg = action.payload;
if (!state.messages[msg.conversationId]) {
state.messages[msg.conversationId] = [];
}
state.messages[msg.conversationId].push(msg);
},
},
});
export const { setConversations, addMessage } = messagingSlice.actions;
// Calls Slice
interface Call {
id: string;
participantName: string;
type: 'audio' | 'video' | 'voice';
status: 'active' | 'ended' | 'missed';
duration?: string;
timestamp: string;
isMuted?: boolean;
isCameraOn?: boolean;
}
interface CallsState {
activeCall: Call | null;
callHistory: Call[];
}
const initialCallsState: CallsState = {
activeCall: null,
callHistory: [],
};
const callsSlice = createSlice({
name: 'calls',
initialState: initialCallsState,
reducers: {
setActiveCall: (state, action: PayloadAction<Call | null>) => {
state.activeCall = action.payload;
},
addCallToHistory: (state, action: PayloadAction<Call>) => {
state.callHistory.unshift(action.payload);
},
},
});
export const { setActiveCall, addCallToHistory } = callsSlice.actions;
// Async thunks
export const loginAsync = (credentials: { email: string; password: string }) => async (dispatch: AppDispatch) => {
dispatch(setLoading(true));
try {
// TODO: Integrate with Supabase auth
const mockUser: User = {
id: '1',
email: credentials.email,
username: credentials.email.split('@')[0],
};
dispatch(setUser(mockUser));
} catch (error) {
dispatch(setError((error as Error).message));
}
};
export const logoutAsync = () => async (dispatch: AppDispatch) => {
dispatch(setLoading(true));
try {
// TODO: Integrate with Supabase auth
dispatch(logout());
} catch (error) {
dispatch(setError((error as Error).message));
}
};
// Store
export const store = configureStore({
reducer: {
auth: authSlice.reducer,
messaging: messagingSlice.reducer,
calls: callsSlice.reducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;