|
3 | 3 | * @typedef {Object} ScheduleItemType - Individual Schedule Item |
4 | 4 | * @property {number} hour - Hour (0-23) |
5 | 5 | * @property {number} minute - Minute (0-59) |
6 | | - * @property {string} face - Clock face source file (e.g. "myclock.js") |
| 6 | + * @property {Array<string>} faces - Clock face source files (e.g. ["myclock.js", "otherclock.js"]) |
7 | 7 | * @property {number} dow - Bitmask for days of week [see Sched documentation] |
8 | 8 | * |
9 | 9 | * @typedef {Object} SettingsType - Overall Settings File/Object |
|
94 | 94 |
|
95 | 95 | // Add existing schedule items to the menu |
96 | 96 | settings.sched.forEach((item, index) => { |
97 | | - const faceName = (clockFaces.find(f => f.src === item.face) || {name: /*LANG*/"Unknown"}).name; |
| 97 | + const faces = item.faces || (item.face ? [item.face] : []); |
| 98 | + const faceName = faces.length > 1 |
| 99 | + ? `${faces.length} ${/*LANG*/"faces"}` // multiple faces |
| 100 | + : (faces.length === 1 |
| 101 | + ? (clockFaces.find(f => f.src === faces[0]) || {name: /*LANG*/"Unknown"}).name // single face |
| 102 | + : /*LANG*/"Unknown"); // no faces |
98 | 103 | const dow = bitmaskToDowIndex(item.dow); |
99 | 104 | const dayName = daysOfWeek[dow === undefined ? IND_EVERY_DAY : dow]; |
100 | 105 | const timeStr = require("locale").time(new Date(1999, 1, 1, item.hour, item.minute, 0),1) |
|
142 | 147 | } |
143 | 148 | }; |
144 | 149 |
|
| 150 | + /** |
| 151 | + * Function to edit clock face selection for a schedule item |
| 152 | + * @param {Object} currentItem The schedule item being edited |
| 153 | + * @param {Array} clockFaces Array of available clock faces |
| 154 | + * @param {Object} parentMenu The parent menu to return to |
| 155 | + */ |
| 156 | + const editClockFaceSelection = function(currentItem, clockFaces, parentMenu) { |
| 157 | + const menu = { |
| 158 | + "": { "title": /*LANG*/"Select Clock Faces" }, |
| 159 | + "< Back": () => E.showMenu(parentMenu), |
| 160 | + }; |
| 161 | + |
| 162 | + // Add checkbox for each clock face |
| 163 | + clockFaces.forEach(face => { |
| 164 | + const isSelected = currentItem.faces.includes(face.src); |
| 165 | + menu[face.name] = { |
| 166 | + value: isSelected, |
| 167 | + onchange: v => { |
| 168 | + if (v) { |
| 169 | + // Add to selection if not already present |
| 170 | + if (!currentItem.faces.includes(face.src)) { |
| 171 | + currentItem.faces.push(face.src); |
| 172 | + } |
| 173 | + } else { |
| 174 | + // Remove from selection |
| 175 | + const idx = currentItem.faces.indexOf(face.src); |
| 176 | + if (idx !== -1) { |
| 177 | + currentItem.faces.splice(idx, 1); |
| 178 | + } |
| 179 | + } |
| 180 | + } |
| 181 | + }; |
| 182 | + }); |
| 183 | + |
| 184 | + E.showMenu(menu); |
| 185 | + }; |
| 186 | + |
145 | 187 | /** |
146 | 188 | * Function to edit a schedule item (or add a new one if index is -1) |
147 | 189 | * @param {number} index index of item to edit, or -1 to add a new item |
|
153 | 195 | const defaultFaceSrc = clockFaces.length > 0 ? clockFaces[0].src : ""; |
154 | 196 |
|
155 | 197 | const currentItem = isNew ? |
156 | | - { hour: 8, minute: 0, face: defaultFaceSrc, dow: BIN_EVERY_DAY } : |
| 198 | + { hour: 8, minute: 0, faces: [defaultFaceSrc], dow: BIN_EVERY_DAY } : |
157 | 199 | Object.assign({}, settings.sched[index]); |
158 | 200 |
|
159 | | - // Default odd items to "Every Day" |
| 201 | + // Handle backwards compatibility: convert single 'face' to 'faces' array |
| 202 | + if (currentItem.face && !currentItem.faces) { |
| 203 | + currentItem.faces = [currentItem.face]; |
| 204 | + delete currentItem.face; |
| 205 | + } |
| 206 | + if (!currentItem.faces) currentItem.faces = []; |
| 207 | + |
| 208 | + // Default invalid items to "Every Day" |
160 | 209 | if (currentItem.dow === undefined) currentItem.dow = BIN_EVERY_DAY; |
161 | 210 |
|
162 | 211 | let dow = bitmaskToDowIndex(currentItem.dow); |
|
179 | 228 | max: 23, |
180 | 229 | format: v => { |
181 | 230 | // Format as 12h time if user has that set |
182 | | - const meridean = require("locale").meridian(new Date(1999, 1, 1, v, 0, 0),1); |
183 | | - return (!meridean) ? v : (v%12||12) + meridean; |
| 231 | + const meridian = require("locale").meridian(new Date(1999, 1, 1, v, 0, 0)); |
| 232 | + return (!meridian) ? v : (v%12||12) + meridian; |
184 | 233 | }, |
185 | 234 | onchange: v => { currentItem.hour = v; } |
186 | 235 | }, |
|
190 | 239 | max: 59, |
191 | 240 | onchange: v => { currentItem.minute = v; } |
192 | 241 | }, |
193 | | - /*LANG*/"Clock Face": { |
194 | | - value: Math.max(0, clockFaces.findIndex(f => f.src === currentItem.face)), |
195 | | - min: 0, |
196 | | - max: clockFaces.length - 1, |
197 | | - format: v => (clockFaces[v] && clockFaces[v].name) || /*LANG*/"None", |
198 | | - onchange: v => { |
199 | | - if (clockFaces[v]) currentItem.face = clockFaces[v].src; |
200 | | - } |
201 | | - }, |
| 242 | + /*LANG*/"Clock Faces": () => editClockFaceSelection(currentItem, clockFaces, menu), |
202 | 243 | /*LANG*/"Save": () => { |
203 | | - const validationError = settings.sched.some((item, i) => { |
| 244 | + |
| 245 | + // Check for schedule conflicts with duplicate times and overlapping days |
| 246 | + const hasScheduleConflict = settings.sched.some((item, i) => { |
204 | 247 | if (!isNew && i === index) return false; // Don't check against self when editing |
205 | 248 |
|
206 | 249 | const timesMatch = item.hour === currentItem.hour |
|
211 | 254 | return (item.dow & currentItem.dow) !== 0; |
212 | 255 | }); |
213 | 256 |
|
214 | | - if (validationError) { |
| 257 | + if (hasScheduleConflict) { |
215 | 258 | E.showAlert( |
216 | 259 | /*LANG*/"An entry for this time already exists.", |
217 | 260 | /*LANG*/"Time conflict" |
|
221 | 264 | return; // Prevent saving |
222 | 265 | } |
223 | 266 |
|
| 267 | + // Ensure at least one face is selected before saving |
| 268 | + if (!Array.isArray(currentItem.faces) || currentItem.faces.length === 0) { |
| 269 | + E.showAlert( |
| 270 | + /*LANG*/"Select at least one clock face.", |
| 271 | + /*LANG*/"No faces" |
| 272 | + ).then( |
| 273 | + ()=>E.showMenu(menu) |
| 274 | + ); |
| 275 | + return; // Prevent saving |
| 276 | + } |
| 277 | + |
224 | 278 | if (isNew) { |
225 | 279 | settings.sched.push(currentItem); |
226 | 280 | } else { |
|
0 commit comments