Skip to content

Commit 8f24ef2

Browse files
authored
Disable popup controls when schedule is active (#90)
1 parent 78c9ba8 commit 8f24ef2

12 files changed

Lines changed: 103 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- Disabled popup global controls when scheduling is active and added inline guidance
11+
so users know adjustments are driven by their schedule.
12+
1013
## [1.6.0] - 2025-11-16
1114

1215
### Added

_locales/en/messages.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373
"popupGlobalStatusOff": {
7474
"message": "Dimmer is off."
7575
},
76+
"popupScheduleLockNotice": {
77+
"message": "Scheduling is on. Global controls follow your schedule and can't be adjusted here."
78+
},
7679
"popupSiteLockedStatus": {
7780
"message": "Locked at $PERCENT$%.",
7881
"placeholders": {

_locales/es/messages.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373
"popupGlobalStatusOff": {
7474
"message": "El atenuador está desactivado."
7575
},
76+
"popupScheduleLockNotice": {
77+
"message": "La programación está activada. Los controles globales siguen tu horario y no se pueden ajustar aquí."
78+
},
7679
"popupSiteLockedStatus": {
7780
"message": "Bloqueado al $PERCENT$%.",
7881
"placeholders": {

_locales/fr/messages.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373
"popupGlobalStatusOff": {
7474
"message": "L'atténuateur est désactivé."
7575
},
76+
"popupScheduleLockNotice": {
77+
"message": "La planification est activée. Les commandes globales suivent votre programme et ne peuvent pas être ajustées ici."
78+
},
7679
"popupSiteLockedStatus": {
7780
"message": "Verrouillé à $PERCENT$ %.",
7881
"placeholders": {

_locales/pt_BR/messages.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373
"popupGlobalStatusOff": {
7474
"message": "Escurecedor desligado."
7575
},
76+
"popupScheduleLockNotice": {
77+
"message": "O agendamento está ativado. Os controles globais seguem sua programação e não podem ser ajustados aqui."
78+
},
7679
"popupSiteLockedStatus": {
7780
"message": "Bloqueado em $PERCENT$%.",
7881
"placeholders": {

src/popup/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ <h2 id="global-controls-heading" class="section-heading" data-i18n="popupGlobalH
3232
</div>
3333
<input id="level" type="range" min="0" max="1" step="0.01" value="0.25">
3434
</div>
35+
<p id="schedule-notice" class="muted small-text" hidden data-i18n="popupScheduleLockNotice"></p>
3536
<p id="global-status" class="muted small-text" role="status" aria-live="polite" data-i18n="popupGlobalStatusOff"></p>
3637
<button id="toggle" type="button" data-i18n="popupToggleOn"></button>
3738
</section>

src/popup/popup.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,14 @@ body.manager-open .site-manager-content {
143143
margin-bottom: 0;
144144
}
145145

146+
.controls-section.global-controls.schedule-locked {
147+
opacity: 0.6;
148+
}
149+
150+
.controls-section.global-controls.schedule-locked button:not(.settings-button) {
151+
cursor: not-allowed;
152+
}
153+
146154
.section-heading {
147155
margin: 0 0 8px;
148156
font-size: 12px;

src/popup/popup.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
let currentSiteLevel = null;
2424
let managerVisible = false;
2525
let blockedHost = null;
26+
let scheduleLocked = false;
2627

2728
function openOptionsPage() {
2829
if (!global.chrome || !chrome.runtime) {
@@ -59,6 +60,11 @@
5960
});
6061
}
6162

63+
function applyScheduleLock(enabled) {
64+
scheduleLocked = Boolean(enabled);
65+
ui.setScheduleLock(scheduleLocked);
66+
}
67+
6268
function updateSiteUI(message) {
6369
let finalMessage = message;
6470
if (!finalMessage && blockedHost) {
@@ -85,18 +91,21 @@
8591
}
8692

8793
function handleLevelInput(event) {
94+
if (scheduleLocked) return;
8895
const value = clamp01(event.target.value);
8996
applyLevel(value);
9097
scheduleGlobalWrite(value);
9198
}
9299

93100
function handleLevelChange(event) {
101+
if (scheduleLocked) return;
94102
const value = clamp01(event.target.value);
95103
applyLevel(value);
96104
scheduleGlobalWrite(value);
97105
}
98106

99107
async function handleToggleClick() {
108+
if (scheduleLocked) return;
100109
const nextLevel = lastLevel > 0 ? 0 : DEFAULT_LEVEL;
101110
applyLevel(nextLevel);
102111
try {
@@ -246,6 +255,7 @@
246255
blockedHost = initial.blockedHost || null;
247256
siteStorage.setCache(initial.siteLevels || {});
248257
currentSiteLevel = siteStorage.getLevel(currentHost);
258+
applyScheduleLock(initial.scheduleEnabled);
249259
applyLevel(lastLevel);
250260
updateSiteUI();
251261
syncManagerUI('');

src/popup/state.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,20 @@
7777
const currentSiteLevel = host && typeof siteLevels[host] === 'number'
7878
? clamp01(siteLevels[host])
7979
: null;
80+
const scheduleEnabled = Boolean(
81+
Object.prototype.hasOwnProperty.call(levelState, 'scheduleEnabled')
82+
? levelState.scheduleEnabled
83+
: levelState.schedule && levelState.schedule.enabled
84+
);
8085

8186
return {
8287
host,
8388
blockedHost,
8489
siteLevels,
8590
globalLevel: normalizedGlobal,
8691
currentSiteLevel,
87-
schedule: levelState.schedule
92+
schedule: levelState.schedule,
93+
scheduleEnabled
8894
};
8995
}
9096

src/popup/ui.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
const toggleBtn = document.querySelector('#toggle');
1818
const globalStatus = document.querySelector('#global-status');
1919
const globalControls = document.querySelector('.global-controls');
20+
const scheduleNotice = document.querySelector('#schedule-notice');
2021
const siteControls = document.querySelector('#site-controls');
2122
const siteHostLabel = document.querySelector('#site-host');
2223
const siteStatus = document.querySelector('#site-status');
@@ -121,6 +122,46 @@
121122
}
122123
}
123124

125+
function setScheduleLock(enabled) {
126+
const isLocked = Boolean(enabled);
127+
const ariaValue = String(isLocked);
128+
const noticeId = scheduleNotice && scheduleNotice.id ? scheduleNotice.id : null;
129+
130+
if (slider) {
131+
slider.disabled = isLocked;
132+
slider.setAttribute('aria-disabled', ariaValue);
133+
if (isLocked && noticeId) {
134+
slider.setAttribute('aria-describedby', noticeId);
135+
} else {
136+
slider.removeAttribute('aria-describedby');
137+
}
138+
}
139+
140+
if (toggleBtn) {
141+
toggleBtn.disabled = isLocked;
142+
toggleBtn.setAttribute('aria-disabled', ariaValue);
143+
if (isLocked && noticeId) {
144+
toggleBtn.setAttribute('aria-describedby', noticeId);
145+
} else {
146+
toggleBtn.removeAttribute('aria-describedby');
147+
}
148+
}
149+
150+
if (globalControls) {
151+
globalControls.classList.toggle('schedule-locked', isLocked);
152+
}
153+
154+
if (scheduleNotice) {
155+
if (isLocked) {
156+
scheduleNotice.hidden = false;
157+
scheduleNotice.textContent = getMessage('popupScheduleLockNotice');
158+
} else {
159+
scheduleNotice.hidden = true;
160+
scheduleNotice.textContent = '';
161+
}
162+
}
163+
}
164+
124165
function updateManageSummary(levels) {
125166
if (!manageBtn) return;
126167
const entries = levels && typeof levels === 'object'
@@ -321,6 +362,7 @@
321362
updateGlobal,
322363
renderSite,
323364
setSiteToggleDisabled,
365+
setScheduleLock,
324366
updateManageSummary,
325367
renderManager,
326368
setManagerVisible,

0 commit comments

Comments
 (0)