Skip to content

Commit abb0ca0

Browse files
committed
- Fixed hotkeys not saving
- Fixed highlight searching not working if you typed a query and then selected a filter - Fixed toggle for hiding audio controls in toolbar not working - Fixed settings modal scrollbar; it now always starts at the top - Added more Google STUN servers and increased RECONNECT_INTERVAL_MS to 15000 - Removed auto-sync toggle (always on after first successful synced device) - Fixed some other bugs
1 parent c14e117 commit abb0ca0

17 files changed

Lines changed: 327 additions & 259 deletions

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,20 @@ Data that is transmitted to external servers:
135135
- Icons by [Font Awesome](https://fontawesome.com)
136136
- Markdown processing by [Marked.js](https://cdn.jsdelivr.net/npm/marked/)
137137
- Local device syncing by [PeerJS](https://unpkg.com)
138-
- STUN servers by [Google]("stun.l.google.com" and "stun1.l.google.com")
138+
- STUN servers by Google
139+
- stun.l.google.com:19302
140+
- stun1.l.google.com:19302
141+
- stun2.l.google.com:19302
142+
- stun3.l.google.com:19302
143+
- stun4.l.google.com:19302
144+
139145
- **Reference Bible websites**
140146
- [Bible Gateway](https://www.biblegateway.com)
141147
- [Bible.com (YouVersion)](https://www.bible.com)
142148
- [Bible Hub](https://biblehub.com)
143149
- [STEP Bible](https://www.stepbible.org)
144150
- [eBible.org](https://ebible.org)
151+
145152
- All copyrights, trademarks, and attributions belong to their respective owners
146153

147154
## License

index.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

main.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,12 @@ export function escapeHTML(string) {
201201
return escape[char];
202202
});
203203
}
204+
function syncAudioControlsToggleUI() {
205+
const audioToggle = document.getElementById('audioControlsToggle');
206+
if (audioToggle) {
207+
audioToggle.checked = !!state.settings.audioControlsVisible;
208+
}
209+
}
204210
export function applyTheme() {
205211
document.documentElement.setAttribute('data-theme', state.settings.theme);
206212
const themeIcon = document.getElementById('themeIcon');
@@ -541,8 +547,9 @@ function setupSyncManagement() {
541547
async function init() {
542548
try {
543549
showLoading(true);
544-
await loadFromStorage();
545550
await loadFromCookies();
551+
await loadFromStorage();
552+
syncAudioControlsToggleUI();
546553
const style = document.createElement('style');
547554
style.textContent = OFFLINE_STYLES;
548555
document.head.appendChild(style);
@@ -552,6 +559,7 @@ async function init() {
552559
initBookChapterControls();
553560
initializeAudioControls();
554561
initialiseNarratorSelect();
562+
updateAudioControlsVisibility();
555563
setupNavigationWithURL();
556564
setupPopStateListener();
557565
if (!navigateFromURL()) {

modules/highlights.js

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -124,36 +124,26 @@ export function closeHighlightsModal() {
124124
console.error('Error closing highlights modal:', error);
125125
}
126126
}
127-
function setupHighlightsSearch() {
127+
function handleSearchInput(e) {
128128
try {
129-
const searchInput = document.getElementById('highlightsSearch');
130-
const clearSearchBtn = document.getElementById('clearSearch');
131-
if (searchInput) {
132-
searchInput.addEventListener('input', handleSearchInput);
133-
}
134-
if (clearSearchBtn) {
135-
clearSearchBtn.addEventListener('click', handleClearSearch);
136-
}
137-
document.querySelectorAll('.highlight-filter-btn').forEach(btn => {
138-
btn.addEventListener('click', function() {
139-
const color = this.dataset.color;
140-
const searchTerm = document.getElementById('highlightsSearch').value || '';
141-
renderHighlights(color, searchTerm);
142-
document.querySelectorAll('.highlight-filter-btn').forEach(b => b.classList.remove('active'));
143-
this.classList.add('active');
144-
});
145-
});
129+
const searchTerm = e.target.value.toLowerCase().trim();
130+
const activeFilter = document.querySelector('.highlight-filter-btn.active')?.dataset.color || 'all';
131+
renderHighlights(activeFilter, searchTerm);
146132
} catch (error) {
147-
console.error('Error setting up highlights search:', error);
133+
console.error('Error handling search input:', error);
148134
}
149135
}
150-
function handleSearchInput() {
136+
function handleFilterClick(e) {
151137
try {
152-
const searchTerm = this.value.toLowerCase().trim();
153-
const activeFilter = document.querySelector('.highlight-filter-btn.active')?.dataset.color || 'all';
154-
renderHighlights(activeFilter, searchTerm);
138+
const btn = e.currentTarget;
139+
const color = btn.dataset.color;
140+
const searchInput = document.getElementById('highlightsSearch');
141+
const searchTerm = (searchInput?.value || '').toLowerCase().trim();
142+
renderHighlights(color, searchTerm);
143+
document.querySelectorAll('.highlight-filter-btn').forEach(b => b.classList.remove('active'));
144+
btn.classList.add('active');
155145
} catch (error) {
156-
console.error('Error handling search input:', error);
146+
console.error('Error handling filter click:', error);
157147
}
158148
}
159149
function handleClearSearch() {
@@ -167,6 +157,26 @@ function handleClearSearch() {
167157
console.error('Error clearing search:', error);
168158
}
169159
}
160+
function setupHighlightsSearch() {
161+
try {
162+
const searchInput = document.getElementById('highlightsSearch');
163+
const clearSearchBtn = document.getElementById('clearSearch');
164+
if (searchInput) {
165+
searchInput.removeEventListener('input', handleSearchInput);
166+
searchInput.addEventListener('input', handleSearchInput);
167+
}
168+
if (clearSearchBtn) {
169+
clearSearchBtn.removeEventListener('click', handleClearSearch);
170+
clearSearchBtn.addEventListener('click', handleClearSearch);
171+
}
172+
document.querySelectorAll('.highlight-filter-btn').forEach(btn => {
173+
btn.removeEventListener('click', handleFilterClick);
174+
btn.addEventListener('click', handleFilterClick);
175+
});
176+
} catch (error) {
177+
console.error('Error setting up highlights search:', error);
178+
}
179+
}
170180
function getValidatedCache(forceClearOnInvalid = false) {
171181
try {
172182
const rawData = localStorage.getItem('cachedVerses');

modules/hotkeys.js

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ const DISALLOWED_KEYS = new Set([
2929
]);
3030
function initializeHotkeys() {
3131
try {
32-
if (!state.settings.hotkeys) {
33-
state.settings.hotkeys = { ...DEFAULT_HOTKEYS };
32+
if (!state.hotkeys) {
33+
state.hotkeys = { ...DEFAULT_HOTKEYS };
3434
}
35-
if (state.settings.hotkeysEnabled === undefined) {
36-
state.settings.hotkeysEnabled = true;
35+
if (state.hotkeysEnabled === undefined) {
36+
state.hotkeysEnabled = true;
3737
}
3838
saveToStorage();
3939
} catch (error) {
@@ -73,7 +73,7 @@ function isF1HelpKey(event) {
7373
!event.metaKey;
7474
}
7575
function shouldProcessHotkey(event) {
76-
if (!state.settings.hotkeysEnabled) return false;
76+
if (!state.hotkeysEnabled) return false;
7777
const target = event.target;
7878
if (target.tagName === 'INPUT' ||
7979
target.tagName === 'TEXTAREA' ||
@@ -86,7 +86,7 @@ function shouldProcessHotkey(event) {
8686
return true;
8787
}
8888
function getHotkeyAction(event) {
89-
const hotkeys = state.settings.hotkeys;
89+
const hotkeys = state.hotkeys;
9090
for (const [action, config] of Object.entries(hotkeys)) {
9191
if (checkHotkey(event, config)) {
9292
return action;
@@ -244,7 +244,7 @@ export function updateHotkey(action, newKey, altKey, shiftKey, ctrlKey) {
244244
if (DISALLOWED_KEYS.has(newKey)) {
245245
throw new Error(`Cannot use reserved key: ${newKey}`);
246246
}
247-
state.settings.hotkeys[action] = {
247+
state.hotkeys[action] = {
248248
key: newKey,
249249
altKey: !!altKey,
250250
shiftKey: !!shiftKey,
@@ -258,12 +258,12 @@ export function updateHotkey(action, newKey, altKey, shiftKey, ctrlKey) {
258258
}
259259
export function toggleHotkeysEnabled() {
260260
try {
261-
state.settings.hotkeysEnabled = !state.settings.hotkeysEnabled;
261+
state.hotkeysEnabled = !state.hotkeysEnabled;
262262
saveToStorage();
263-
return state.settings.hotkeysEnabled;
263+
return state.hotkeysEnabled;
264264
} catch (error) {
265265
console.error('Error toggling hotkeys:', error);
266-
return state.settings.hotkeysEnabled;
266+
return state.hotkeysEnabled;
267267
}
268268
}
269269
function toggleReferencePanel() {
@@ -309,11 +309,11 @@ function populateHotkeysList() {
309309
const hotkeysList = document.getElementById('hotkeysList');
310310
const enabledCheckbox = document.getElementById('hotkeysEnabled');
311311
if (!hotkeysList || !enabledCheckbox) return;
312-
enabledCheckbox.checked = state.settings.hotkeysEnabled;
312+
enabledCheckbox.checked = state.hotkeysEnabled;
313313
const hotkeyDefinitions = [
314314
{ action: 'toggleReferencePanel', label: 'Toggle Reference Bible' },
315-
{ action: 'toggleNotes', label: 'Toggle Notes' },
316-
{ action: 'toggleSidebar', label: 'Toggle Sidebar' },
315+
{ action: 'toggleNotes', label: 'Toggle Notes' },
316+
{ action: 'toggleSidebar', label: 'Toggle Sidebar' },
317317
{ action: 'prevChapter', label: 'Previous Chapter' },
318318
{ action: 'nextChapter', label: 'Next Chapter' },
319319
{ action: 'prevBook', label: 'Previous Book' },
@@ -327,7 +327,7 @@ function populateHotkeysList() {
327327
{ action: 'showHelp', label: 'Show This Help' }
328328
];
329329
hotkeysList.innerHTML = hotkeyDefinitions.map(hotkey => {
330-
const display = getHotkeyDisplay(state.settings.hotkeys[hotkey.action]);
330+
const display = getHotkeyDisplay(state.hotkeys[hotkey.action]);
331331
return `
332332
<div class="hotkey-item">
333333
<div class="hotkey-label">${escapeHTML(hotkey.label)}</div>
@@ -369,15 +369,15 @@ function setupHelpModalEvents() {
369369
}
370370
function handleHotkeysEnabledChange(event) {
371371
try {
372-
state.settings.hotkeysEnabled = event.target.checked;
372+
state.hotkeysEnabled = event.target.checked;
373373
saveToStorage();
374374
} catch (error) {
375375
console.error('Error changing hotkeys enabled state:', error);
376376
}
377377
}
378378
function resetHotkeysToDefaults() {
379379
try {
380-
state.settings.hotkeys = { ...DEFAULT_HOTKEYS };
380+
state.hotkeys = { ...DEFAULT_HOTKEYS };
381381
saveToStorage();
382382
populateHotkeysList();
383383
} catch (error) {
@@ -404,10 +404,10 @@ function startHotkeyRecording(action, buttonElement) {
404404
return;
405405
}
406406
const originalText = keysSpan.textContent;
407-
const originalHotkeysEnabled = state.settings.hotkeysEnabled;
407+
const originalHotkeysEnabled = state.hotkeysEnabled;
408408
keysSpan.innerHTML = '<em>Press any key combination...</em>';
409409
keysSpan.style.color = 'var(--accent-color)';
410-
state.settings.hotkeysEnabled = false;
410+
state.hotkeysEnabled = false;
411411
const overlay = createRecordingOverlay();
412412
const cleanup = setupRecordingHandlers(
413413
action,
@@ -448,7 +448,7 @@ function setupRecordingHandlers(action, keysSpan, originalHotkeysEnabled, origin
448448
}
449449
try {
450450
updateHotkey(action, event.key, event.altKey, event.shiftKey, event.ctrlKey);
451-
keysSpan.textContent = getHotkeyDisplay(state.settings.hotkeys[action]);
451+
keysSpan.textContent = getHotkeyDisplay(state.hotkeys[action]);
452452
keysSpan.style.color = '';
453453
finishRecording();
454454
} catch (error) {
@@ -463,7 +463,7 @@ function setupRecordingHandlers(action, keysSpan, originalHotkeysEnabled, origin
463463
finishRecording();
464464
}
465465
function finishRecording() {
466-
state.settings.hotkeysEnabled = originalHotkeysEnabled;
466+
state.hotkeysEnabled = originalHotkeysEnabled;
467467
cleanup();
468468
}
469469
function cleanup() {

modules/settings.js

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,21 @@ function populateSettingsForm() {
175175
});
176176
}
177177
if (translationSelect) translationSelect.value = state.settings.bibleTranslation;
178-
if (audioToggle) audioToggle.checked = state.settings.audioControlsVisible;
179-
if (versionElement) versionElement.textContent = APP_VERSION;
180-
if (themeToggle) themeToggle.checked = state.settings.theme === 'dark';
178+
if (audioToggle) {
179+
audioToggle.checked = state.settings.audioControlsVisible;
180+
audioToggle.addEventListener('change', () => {
181+
state.settings.audioControlsVisible = audioToggle.checked;
182+
saveToStorage();
183+
updateAudioControlsVisibility();
184+
});
185+
}
186+
if (autoplayToggle) {
187+
autoplayToggle.checked = !!state.settings.autoplayAudio;
188+
autoplayToggle.addEventListener('change', () => {
189+
state.settings.autoplayAudio = autoplayToggle.checked;
190+
saveToStorage();
191+
});
192+
}
181193
if (scriptureFontSizeSlider) {
182194
scriptureFontSizeSlider.value = state.settings.scriptureFontSize || 16;
183195
scriptureFontSizeSlider.addEventListener('input', () => {
@@ -198,7 +210,15 @@ function populateSettingsForm() {
198210
if (notesFontSizeValue) {
199211
notesFontSizeValue.textContent = `${state.settings.notesFontSize || 16}px`;
200212
}
201-
if (autoplayToggle) autoplayToggle.checked = !!state.settings.autoplayAudio;
213+
if (themeToggle) {
214+
themeToggle.checked = state.settings.theme === 'dark';
215+
themeToggle.addEventListener('change', () => {
216+
state.settings.theme = themeToggle.checked ? 'dark' : 'light';
217+
saveToStorage();
218+
applyTheme();
219+
});
220+
}
221+
if (versionElement) versionElement.textContent = APP_VERSION;
202222
updateColorThemeSelection();
203223
}
204224
function updateColorThemeSelection() {
@@ -213,6 +233,11 @@ function showSettingsModal() {
213233
if (modal) modal.classList.add('active');
214234
if (overlay) overlay.classList.add('active');
215235
attachSyncUiHandlers();
236+
requestAnimationFrame(() => {
237+
if (modal) {
238+
modal.scrollTop = 0;
239+
}
240+
});
216241
}
217242
export function closeSettings() {
218243
const modal = document.getElementById('settingsModal');
@@ -632,10 +657,6 @@ export function updateAudioControlsVisibility() {
632657
if (audioCheckbox) audioCheckbox.checked = isVisible;
633658
}
634659
export function initializeAudioControls() {
635-
if (typeof state.settings.audioControlsVisible === 'undefined') {
636-
state.settings.audioControlsVisible = true;
637-
saveToStorage();
638-
}
639660
updateAudioControlsVisibility();
640661
}
641662
export function initialiseNarratorSelect() {

0 commit comments

Comments
 (0)