11/**
22 * App Slice — global application state: config, theme, WebSocket connection, auth.
33 */
4- import { createSlice , createAsyncThunk , PayloadAction } from '@reduxjs/toolkit' ;
4+ import { createSlice , PayloadAction } from '@reduxjs/toolkit' ;
55import type { RootState } from '../store' ;
6- import { getUserInfo } from '../../api/config ' ;
6+ import { UserInfo } from '../../models ' ;
77
88export interface AppState {
99 /** Has the runtime config been loaded from /config? */
@@ -29,40 +29,6 @@ const initialState: AppState = {
2929 userEmail : '' ,
3030} ;
3131
32- export const fetchCurrentUser = createAsyncThunk (
33- 'app/fetchCurrentUser' ,
34- async ( _arg , { rejectWithValue } ) => {
35- try {
36- const userInfo = await getUserInfo ( ) ;
37-
38- if ( ! userInfo . user_id ) {
39- return rejectWithValue ( 'No user identity found' ) ;
40- }
41-
42- // Extract email from claims (preferred_username, email, or UPN)
43- const userClaims = userInfo . user_claims || [ ] ;
44- let emailVal = userInfo . user_email || '' ;
45- for ( const claim of userClaims ) {
46- if ( claim . typ === 'preferred_username' ||
47- claim . typ === 'email' ||
48- claim . typ === 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress' ||
49- claim . typ === 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn' ) {
50- emailVal = claim . val ;
51- break ;
52- }
53- }
54-
55- return {
56- userId : userInfo . user_id || 'anonymous' ,
57- userName : userInfo . user_first_last_name || '' ,
58- userEmail : emailVal ,
59- } ;
60- } catch ( error ) {
61- return rejectWithValue ( 'Failed to fetch user info' ) ;
62- }
63- }
64- ) ;
65-
6632const appSlice = createSlice ( {
6733 name : 'app' ,
6834 initialState,
@@ -76,26 +42,38 @@ const appSlice = createSlice({
7642 setWsConnected ( state , action : PayloadAction < boolean > ) {
7743 state . wsConnected = action . payload ;
7844 } ,
79- } ,
80- extraReducers : ( builder ) => {
81- builder
82- . addCase ( fetchCurrentUser . fulfilled , ( state , action ) => {
83- state . userId = action . payload . userId ;
84- state . userName = action . payload . userName ;
85- state . userEmail = action . payload . userEmail ;
86- } )
87- . addCase ( fetchCurrentUser . rejected , ( state ) => {
45+ hydrateCurrentUser ( state , action : PayloadAction < UserInfo | null > ) {
46+ const userInfo = action . payload ;
47+ if ( ! userInfo || ! userInfo . user_id ) {
8848 state . userId = 'anonymous' ;
8949 state . userName = '' ;
9050 state . userEmail = '' ;
91- } ) ;
51+ return ;
52+ }
53+ state . userId = userInfo . user_id || 'anonymous' ;
54+ state . userName = userInfo . user_first_last_name || '' ;
55+ // Extract email from claims
56+ const userClaims = userInfo . user_claims || [ ] ;
57+ let emailVal = userInfo . user_email || '' ;
58+ for ( const claim of userClaims ) {
59+ if ( claim . typ === 'preferred_username' ||
60+ claim . typ === 'email' ||
61+ claim . typ === 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress' ||
62+ claim . typ === 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn' ) {
63+ emailVal = claim . val ;
64+ break ;
65+ }
66+ }
67+ state . userEmail = emailVal ;
68+ } ,
9269 } ,
9370} ) ;
9471
9572export const {
9673 setConfigLoaded,
9774 setIsDarkMode,
9875 setWsConnected,
76+ hydrateCurrentUser,
9977} = appSlice . actions ;
10078
10179/* ── Granular Selectors ───────────────────────────────────────── */
0 commit comments