github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/webapp/javascript/redux/reducers/ui.ts (about) 1 import { createSlice, createSelector, PayloadAction } from '@reduxjs/toolkit'; 2 import { createMigrate } from 'redux-persist'; 3 import storage from 'redux-persist/lib/storage'; 4 import { PersistedState } from 'redux-persist/lib/types'; 5 import type { RootState } from '@webapp/redux/store'; 6 7 // Persistence Migrations 8 // See examples on https://github.com/rt2zz/redux-persist/blob/master/docs/migrations.md 9 export const migrations = { 10 0: (state: PersistedState) => { 11 if (!state) { 12 return {} as PersistedState; 13 } 14 15 return { ...state }; 16 }, 17 }; 18 19 export const persistConfig = { 20 key: 'pyroscope:ui', 21 version: 0, 22 storage, 23 migrate: createMigrate(migrations, { debug: true }), 24 }; 25 26 type SidebarState = 27 // pristine means user hasn't interacted with it yet 28 // so we default to certain heuristics (eg window size) 29 | { state: 'pristine'; collapsed: true } 30 | { state: 'pristine'; collapsed: false } 31 32 // userInteracted means user has actively clicked on the button 33 // so we should keep whatever state they've chosen 34 | { state: 'userInteracted'; collapsed: true } 35 | { state: 'userInteracted'; collapsed: false }; 36 37 export interface UiState { 38 sidebar: SidebarState; 39 time: { 40 offset: null | number; 41 }; 42 colorMode: 'dark' | 'light'; 43 } 44 45 const initialState: UiState = { 46 sidebar: { state: 'pristine', collapsed: window.innerWidth < 1200 }, 47 time: { 48 offset: null, 49 }, 50 // sidebar: { state: 'pristine' }, 51 colorMode: 'dark', 52 }; 53 54 export const uiSlice = createSlice({ 55 name: 'ui', 56 initialState, 57 reducers: { 58 recalculateSidebar: (state) => { 59 if (state.sidebar.state === 'pristine') { 60 state.sidebar.collapsed = window.innerWidth < 1200; 61 } 62 }, 63 collapseSidebar: (state) => { 64 state.sidebar = { state: 'userInteracted', collapsed: true }; 65 }, 66 uncollapseSidebar: (state) => { 67 state.sidebar = { state: 'userInteracted', collapsed: false }; 68 }, 69 changeTimeZoneOffset: (state, action) => { 70 state.time.offset = action.payload; 71 }, 72 setColorMode: (state, action: PayloadAction<'dark' | 'light'>) => { 73 state.colorMode = action.payload; 74 }, 75 }, 76 }); 77 78 const selectUiState = (state: RootState) => state.ui; 79 80 export const selectSidebarCollapsed = createSelector(selectUiState, (state) => { 81 return state.sidebar.collapsed; 82 }); 83 84 export const selectTimezoneOffset = createSelector( 85 selectUiState, 86 (state) => state.time.offset 87 ); 88 89 export const selectAppColorMode = createSelector( 90 selectUiState, 91 (state) => state.colorMode 92 ); 93 94 export const { 95 collapseSidebar, 96 uncollapseSidebar, 97 recalculateSidebar, 98 changeTimeZoneOffset, 99 setColorMode, 100 } = uiSlice.actions; 101 102 export default uiSlice.reducer;