diff --git a/assets/js/main.js b/assets/js/main.js index 2b7ce8a..70599c1 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,102 +1,176 @@ document.addEventListener('DOMContentLoaded', function () { - const alarmForm = document.getElementById('alarm-form'); - const alarmsList = document.getElementById('alarms-list'); - const alarmModal = new bootstrap.Modal(document.getElementById('alarm-modal')); - const alarmSound = document.getElementById('alarm-sound'); - const dismissAlarmBtn = document.getElementById('dismiss-alarm'); + const alarmForm = document.getElementById('createAlarmForm'); + const alarmsList = document.getElementById('alarmList'); + const alarmModal = new bootstrap.Modal(document.getElementById('alarmModal')); + const alarmSound = document.getElementById('alarmSound'); + const dismissAlarmBtn = document.getElementById('dismissAlarmBtn'); const enableNotificationsBtn = document.getElementById('enable-notifications'); + const notificationPermissionCard = document.getElementById('notification-permission-card'); - let notificationPermission = false; + // --- Notification Permission Handling --- - // Request notification permission + function handleNotificationPermission(permission) { + if (permission === 'granted') { + if (notificationPermissionCard) { + notificationPermissionCard.style.display = 'none'; + } + } else if (permission === 'denied') { + if (notificationPermissionCard) { + notificationPermissionCard.innerHTML = '
You have blocked notifications. To use alarms, you need to enable them in your browser settings.
'; + } + } else { + if (notificationPermissionCard) { + notificationPermissionCard.style.display = 'block'; + } + } + } + + // Check initial notification permission status + if ('Notification' in window) { + handleNotificationPermission(Notification.permission); + } else { + // Notifications not supported + if (notificationPermissionCard) { + notificationPermissionCard.innerHTML = '
This browser does not support desktop notifications.
'; + } + } + + // Request notification permission on button click if (enableNotificationsBtn) { enableNotificationsBtn.addEventListener('click', () => { Notification.requestPermission().then(permission => { - if (permission === 'granted') { - notificationPermission = true; - alert('Notifications enabled!'); - enableNotificationsBtn.style.display = 'none'; - } else { - alert('Notification permission denied.'); - } + handleNotificationPermission(permission); }); }); } + // --- Alarm Logic --- // Function to fetch and display alarms const fetchAlarms = () => { fetch('/api/alarms.php?action=get') .then(response => response.json()) .then(data => { - alarmsList.innerHTML = ''; - if (data.success) { + if (data.success && data.alarms) { + // Clear only if there are alarms to show + if(data.alarms.length > 0) { + alarmsList.innerHTML = ''; + } + document.getElementById('noAlarmsMessage')?.remove(); data.alarms.forEach(alarm => { - addAlarmToList(alarm.id, alarm.alarm_time, alarm.note_id, alarm.is_active); + addAlarmToList(alarm); }); } }); }; // Function to add a single alarm to the list - const addAlarmToList = (id, time, noteId, isActive) => { + const addAlarmToList = (alarm) => { + const { id, alarm_time, label, is_active, note_id } = alarm; + const listItem = document.createElement('li'); listItem.className = 'list-group-item d-flex justify-content-between align-items-center'; listItem.dataset.id = id; - const timeText = document.createElement('span'); - timeText.textContent = new Date(`1970-01-01T${time}`).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + const alarmDisplay = document.createElement('div'); + alarmDisplay.className = 'd-flex align-items-center'; - const noteLink = document.createElement('a'); - noteLink.href = `/note.php?id=${noteId}`; - noteLink.textContent = `Note #${noteId}`; - noteLink.className = 'mx-3'; + const switchDiv = document.createElement('div'); + switchDiv.className = 'form-check form-switch me-3'; + const switchInput = document.createElement('input'); + switchInput.className = 'form-check-input toggle-alarm-switch'; + switchInput.type = 'checkbox'; + switchInput.role = 'switch'; + switchInput.id = `toggle-${id}`; + switchInput.checked = !!parseInt(is_active); + switchInput.addEventListener('change', () => handleToggleAlarm(id, switchInput.checked)); + const switchLabel = document.createElement('label'); + switchLabel.className = 'form-check-label'; + switchLabel.setAttribute('for', `toggle-${id}`); - const controls = document.createElement('div'); + switchDiv.appendChild(switchInput); + switchDiv.appendChild(switchLabel); - const toggleSwitch = document.createElement('div'); - toggleSwitch.className = 'form-check form-switch'; - const toggleInput = document.createElement('input'); - toggleInput.className = 'form-check-input'; - toggleInput.type = 'checkbox'; - toggleInput.role = 'switch'; - toggleInput.checked = isActive; - toggleInput.addEventListener('change', () => handleToggleAlarm(id, toggleInput.checked)); - toggleSwitch.appendChild(toggleInput); + const timeDiv = document.createElement('div'); + const timeSpan = document.createElement('span'); + timeSpan.className = 'fw-bold fs-5'; + timeSpan.textContent = new Date(`1970-01-01T${alarm_time}`).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: true }); + const labelSpan = document.createElement('span'); + labelSpan.className = 'text-muted ms-2'; + labelSpan.textContent = label; - const deleteBtn = document.createElement('button'); - deleteBtn.className = 'btn btn-danger btn-sm ms-3'; - deleteBtn.textContent = 'Delete'; - deleteBtn.addEventListener('click', () => handleDeleteAlarm(id)); + timeDiv.appendChild(timeSpan); + timeDiv.appendChild(labelSpan); - controls.appendChild(toggleSwitch); - controls.appendChild(deleteBtn); + alarmDisplay.appendChild(switchDiv); + alarmDisplay.appendChild(timeDiv); + + const deleteForm = document.createElement('form'); + deleteForm.className = 'delete-alarm-form'; + deleteForm.addEventListener('submit', (e) => { + e.preventDefault(); + handleDeleteAlarm(id); + }); + const hiddenAction = document.createElement('input'); + hiddenAction.type = 'hidden'; + hiddenAction.name = 'action'; + hiddenAction.value = 'delete'; + const hiddenId = document.createElement('input'); + hiddenId.type = 'hidden'; + hiddenId.name = 'alarm_id'; + hiddenId.value = id; + const deleteButton = document.createElement('button'); + deleteButton.type = 'submit'; + deleteButton.className = 'btn btn-sm btn-outline-danger'; + deleteButton.innerHTML = ''; + + deleteForm.appendChild(hiddenAction); + deleteForm.appendChild(hiddenId); + deleteForm.appendChild(deleteButton); + + listItem.appendChild(alarmDisplay); + listItem.appendChild(deleteForm); + + // Remove "no alarms" message if it exists + const noAlarmsMsg = document.getElementById('noAlarmsMessage'); + if (noAlarmsMsg) { + noAlarmsMsg.remove(); + } - listItem.appendChild(timeText); - listItem.appendChild(noteLink); - listItem.appendChild(controls); alarmsList.appendChild(listItem); + feather.replace(); }; // Handle form submission to create a new alarm if (alarmForm) { alarmForm.addEventListener('submit', function (e) { e.preventDefault(); - const formData = new FormData(alarmForm); + const time = document.getElementById('alarmTime').value; + const label = document.getElementById('alarmLabel').value; + + if (!time) { + alert('Please select a time for the alarm.'); + return; + } + + const formData = new FormData(); + formData.append('alarm_time', time); + formData.append('label', label); + fetch('/api/alarms.php?action=create', { method: 'POST', body: formData }) - .then(response => response.json()) - .then(data => { - if (data.success) { - addAlarmToList(data.id, data.alarm_time, data.note_id, true); - alarmForm.reset(); - } else { - alert('Error: ' + data.error); - } - }); + .then(response => response.json()) + .then(data => { + if (data.success) { + addAlarmToList(data.alarm); + alarmForm.reset(); + } else { + alert('Error: ' + (data.error || 'Could not create alarm.')); + } + }); }); } @@ -108,8 +182,11 @@ document.addEventListener('DOMContentLoaded', function () { .then(data => { if (data.success) { document.querySelector(`li[data-id='${id}']`).remove(); + if (alarmsList.children.length === 0) { + alarmsList.innerHTML = '
  • No alarms set yet.
  • '; + } } else { - alert('Error: ' + data.error); + alert('Error: ' + (data.error || 'Could not delete alarm.')); } }); }; @@ -127,24 +204,29 @@ document.addEventListener('DOMContentLoaded', function () { .then(data => { if (!data.success) { alert('Error updating alarm status.'); + // Revert the toggle switch if the server update fails + const toggleInput = document.getElementById(`toggle-${id}`); + if(toggleInput) { + toggleInput.checked = !isActive; + } } }); }; - // Function to check for due alarms const checkAlarms = () => { fetch('/api/alarms.php?action=check') .then(response => response.json()) .then(data => { - if (data.success && data.alarms.length > 0) { - const alarm = data.alarms[0]; // Assuming one alarm for now - showNotification(alarm); - // Also update the toggle on the main page - const alarmToggle = document.querySelector(`li[data-id='${alarm.id}'] input[type='checkbox']`); - if (alarmToggle) { - alarmToggle.checked = false; - } + if (data.success && data.alarms && data.alarms.length > 0) { + data.alarms.forEach(alarm => { + showNotification(alarm); + // Update the toggle on the main page to off + const alarmToggle = document.querySelector(`#toggle-${alarm.id}`); + if (alarmToggle) { + alarmToggle.checked = false; + } + }); } }); }; @@ -152,35 +234,49 @@ document.addEventListener('DOMContentLoaded', function () { // Function to show notification const showNotification = (alarm) => { const alarmTime = new Date(`1970-01-01T${alarm.alarm_time}`).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); - const notificationTitle = `Alarm! It's ${alarmTime}`; - const notificationBody = `Click to open your note.`; + const notificationTitle = `Alarm: ${alarm.label || alarmTime}`; + const notificationBody = 'Time to write your notes. Click here!'; + const bellIcon = '/assets/images/bell.png'; + const alarmSoundSrc = '/assets/sounds/alarm.mp3.ogg'; - if (notificationPermission) { + // Use browser notification if permission is granted + if ('Notification' in window && Notification.permission === 'granted') { const notification = new Notification(notificationTitle, { body: notificationBody, - icon: '/assets/images/bell.png', // Optional: add an icon - sound: '/assets/sounds/alarm.mp3', // This doesn't work, sound is handled separately - requireInteraction: true // Keeps notification open until user interacts + icon: bellIcon, + requireInteraction: true }); notification.onclick = () => { - window.open(`/note.php?id=${alarm.note_id}`, '_blank'); + window.open(`note.php?id=${alarm.note_id}`, '_blank'); notification.close(); }; + + // Play sound along with notification + const audio = new Audio(alarmSoundSrc); + audio.play().catch(e => console.error("Audio playback failed:", e)); + + } else { + // Fallback to modal + document.getElementById('alarmModalMessage').textContent = `It's time for your alarm: ${alarm.label || alarmTime}`; + const audio = document.getElementById('alarmSound'); + if (audio) { + audio.src = alarmSoundSrc; // Ensure src is set + audio.play().catch(e => console.error("Audio playback failed:", e)); + } + alarmModal.show(); } - // Fallback to modal - alarmSound.play().catch(e => console.error("Audio playback failed:", e)); - alarmModal.show(); - dismissAlarmBtn.onclick = () => { - alarmSound.pause(); - alarmSound.currentTime = 0; + const audio = document.getElementById('alarmSound'); + if (audio) { + audio.pause(); + audio.currentTime = 0; + } alarmModal.hide(); }; }; - // Initial fetch and periodic check fetchAlarms(); setInterval(checkAlarms, 5000); // Check every 5 seconds diff --git a/index.php b/index.php index 28b96b0..e28369c 100644 --- a/index.php +++ b/index.php @@ -36,6 +36,14 @@ try {
    + +
    +
    +

    For alarms to work even when the browser is in the background, please enable notifications.

    + +
    +
    +
    @@ -108,7 +116,6 @@ try {