@@ -20,6 +20,7 @@ import {
2020 readLocalInterfaceColors ,
2121 readLocalScreensaverSettings ,
2222 readLocalAutoDarkModeSettings ,
23+ readLocalVacationModeSettings ,
2324} from './utils/interfaceSettings.js' ;
2425import { normalizeWidgetSettings , BASE_WIDGET_SETTINGS } from './utils/widgetSettings.js' ;
2526import { buildMobileWidgetList } from './utils/mobileWidgets.js' ;
@@ -32,6 +33,7 @@ const loadWeatherWidget = () => import('./components/WeatherWidget.jsx');
3233const loadChoreWidget = ( ) => import ( './components/ChoreWidget.jsx' ) ;
3334const loadTabIconModal = ( ) => import ( './components/TabIconModal.jsx' ) ;
3435const loadScreenSaver = ( ) => import ( './components/ScreenSaver.jsx' ) ;
36+ const loadVacationScreensaver = ( ) => import ( './components/VacationScreensaver.jsx' ) ;
3537
3638const MAX_IDLE_WARM_IMPORTS = 3 ;
3739const WIDGETS_LOCKED_STORAGE_KEY = 'widgetsLocked' ;
@@ -72,6 +74,7 @@ const WeatherWidget = lazy(loadWeatherWidget);
7274const ChoreWidget = lazy ( loadChoreWidget ) ;
7375const TabIconModal = lazy ( loadTabIconModal ) ;
7476const ScreenSaver = lazy ( loadScreenSaver ) ;
77+ const VacationScreensaver = lazy ( loadVacationScreensaver ) ;
7578
7679// region #98 - expected to get removed in the future (localStorage migration bridge)
7780const DEVICE_SETTINGS_UPDATED_EVENT = 'homeglow:device-settings-updated' ;
@@ -147,6 +150,7 @@ const App = () => {
147150 const [ widgetsLocked , setWidgetsLocked ] = useState ( readLocalWidgetsLocked ) ;
148151 const [ screensaverActive , setScreensaverActive ] = useState ( false ) ;
149152 const [ screensaverSettings , setScreensaverSettings ] = useState ( readLocalScreensaverSettings ) ;
153+ const [ vacationModeSettings , setVacationModeSettings ] = useState ( readLocalVacationModeSettings ) ;
150154 const inactivityTimerRef = useRef ( null ) ;
151155 const lastActivityRef = useRef ( Date . now ( ) ) ;
152156 const [ widgetSettings , setWidgetSettings ] = useState ( { ...DEFAULT_WIDGET_SETTINGS } ) ;
@@ -500,6 +504,7 @@ const App = () => {
500504 setInterfaceColors ( readLocalInterfaceColors ( ) ) ;
501505 setScreensaverSettings ( readLocalScreensaverSettings ( ) ) ;
502506 setAutoDarkModeSettings ( readLocalAutoDarkModeSettings ( ) ) ;
507+ setVacationModeSettings ( readLocalVacationModeSettings ( ) ) ;
503508
504509 const localTheme = readLocalTheme ( ) ;
505510 const localThemeMode = readLocalThemeMode ( localTheme ) ;
@@ -588,7 +593,7 @@ const App = () => {
588593 ! ! widgetSettings ?. chores ?. enabled && loadChoreWidget ,
589594 ! ! widgetSettings ?. weather ?. enabled && loadWeatherWidget ,
590595 ! ! widgetSettings ?. photos ?. enabled && loadPhotoWidget ,
591- ! ! screensaverSettings ?. enabled && loadScreenSaver ,
596+ ! ! screensaverSettings ?. enabled && ( vacationModeSettings ?. enabled ? loadVacationScreensaver : loadScreenSaver ) ,
592597 ]
593598 . filter ( Boolean )
594599 . slice ( 0 , MAX_IDLE_WARM_IMPORTS ) ;
@@ -606,6 +611,7 @@ const App = () => {
606611 widgetSettings ?. weather ?. enabled ,
607612 widgetSettings ?. photos ?. enabled ,
608613 screensaverSettings ?. enabled ,
614+ vacationModeSettings ?. enabled ,
609615 ] ) ;
610616
611617 const screensaverActiveRef = useRef ( false ) ;
@@ -924,7 +930,9 @@ const App = () => {
924930 enabled :
925931 widgetSettings . chores . enabled &&
926932 choreSoundGlobalEnabled &&
927- choreSoundDeviceEnabled ,
933+ choreSoundDeviceEnabled &&
934+ // Vacation mode (issue #121) mutes chore due-time sounds.
935+ ! ( vacationModeSettings . enabled && vacationModeSettings . muteSounds ) ,
928936 defaultSound : apiKeys . CHORE_SOUND_DEFAULT || null ,
929937 volume : Number . isFinite ( parsedSoundVolume ) ? parsedSoundVolume / 100 : 1 ,
930938 } ) ;
@@ -954,6 +962,32 @@ const App = () => {
954962 Demo Mode — sample data resets every { demoStatus . resetHours || 6 } hours
955963 </ Box >
956964 ) }
965+ { vacationModeSettings . enabled && (
966+ < Box
967+ aria-label = "Vacation mode active"
968+ sx = { {
969+ position : 'fixed' ,
970+ top : 8 ,
971+ right : 8 ,
972+ zIndex : 1200 ,
973+ px : 1.5 ,
974+ py : 0.5 ,
975+ borderRadius : '16px' ,
976+ backgroundColor : 'var(--card-bg)' ,
977+ border : '1px solid var(--card-border)' ,
978+ color : 'var(--text-color)' ,
979+ fontSize : '0.8rem' ,
980+ fontWeight : 600 ,
981+ boxShadow : 'var(--shadow)' ,
982+ pointerEvents : 'none' ,
983+ display : 'flex' ,
984+ alignItems : 'center' ,
985+ gap : 0.5 ,
986+ } }
987+ >
988+ 🏖️ Vacation Mode
989+ </ Box >
990+ ) }
957991 { /* The one mobile/kiosk fork (issue #118): below 600px the grid —
958992 react-grid-layout, drag/resize, lock — never mounts. */ }
959993 { isMobile && mobileWidgets . length > 0 && (
@@ -1095,13 +1129,19 @@ const App = () => {
10951129
10961130 { ! isMobile && screensaverActive && screensaverSettings . enabled && (
10971131 < Suspense fallback = { null } >
1098- < ScreenSaver
1099- mode = { screensaverSettings . mode }
1100- slideshowInterval = { screensaverSettings . slideshowInterval }
1101- tabs = { tabs }
1102- onExit = { handleExitScreensaver }
1103- onTabChange = { handleScreensaverTabChange }
1104- />
1132+ { vacationModeSettings . enabled ? (
1133+ // Vacation mode (issue #121) replaces the standard screensaver
1134+ // with the popcorn vacation-emoji one.
1135+ < VacationScreensaver onExit = { handleExitScreensaver } />
1136+ ) : (
1137+ < ScreenSaver
1138+ mode = { screensaverSettings . mode }
1139+ slideshowInterval = { screensaverSettings . slideshowInterval }
1140+ tabs = { tabs }
1141+ onExit = { handleExitScreensaver }
1142+ onTabChange = { handleScreensaverTabChange }
1143+ />
1144+ ) }
11051145 </ Suspense >
11061146 ) }
11071147 </ >
0 commit comments