Auto commit: 2025-11-21T18:47:34.824Z

This commit is contained in:
Flatlogic Bot 2025-11-21 18:47:34 +00:00
parent 6a4e5985e5
commit 494f12a424
2 changed files with 181 additions and 78 deletions

View File

@ -1,89 +1,163 @@
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
const alarmForm = document.getElementById('alarm-form'); const alarmForm = document.getElementById('createAlarmForm');
const alarmsList = document.getElementById('alarms-list'); const alarmsList = document.getElementById('alarmList');
const alarmModal = new bootstrap.Modal(document.getElementById('alarm-modal')); const alarmModal = new bootstrap.Modal(document.getElementById('alarmModal'));
const alarmSound = document.getElementById('alarm-sound'); const alarmSound = document.getElementById('alarmSound');
const dismissAlarmBtn = document.getElementById('dismiss-alarm'); const dismissAlarmBtn = document.getElementById('dismissAlarmBtn');
const enableNotificationsBtn = document.getElementById('enable-notifications'); 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 = '<div class="card-body text-center text-danger">You have blocked notifications. To use alarms, you need to enable them in your browser settings.</div>';
}
} 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 = '<div class="card-body text-center text-muted">This browser does not support desktop notifications.</div>';
}
}
// Request notification permission on button click
if (enableNotificationsBtn) { if (enableNotificationsBtn) {
enableNotificationsBtn.addEventListener('click', () => { enableNotificationsBtn.addEventListener('click', () => {
Notification.requestPermission().then(permission => { Notification.requestPermission().then(permission => {
if (permission === 'granted') { handleNotificationPermission(permission);
notificationPermission = true;
alert('Notifications enabled!');
enableNotificationsBtn.style.display = 'none';
} else {
alert('Notification permission denied.');
}
}); });
}); });
} }
// --- Alarm Logic ---
// Function to fetch and display alarms // Function to fetch and display alarms
const fetchAlarms = () => { const fetchAlarms = () => {
fetch('/api/alarms.php?action=get') fetch('/api/alarms.php?action=get')
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.success && data.alarms) {
// Clear only if there are alarms to show
if(data.alarms.length > 0) {
alarmsList.innerHTML = ''; alarmsList.innerHTML = '';
if (data.success) { }
document.getElementById('noAlarmsMessage')?.remove();
data.alarms.forEach(alarm => { 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 // 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'); const listItem = document.createElement('li');
listItem.className = 'list-group-item d-flex justify-content-between align-items-center'; listItem.className = 'list-group-item d-flex justify-content-between align-items-center';
listItem.dataset.id = id; listItem.dataset.id = id;
const timeText = document.createElement('span'); const alarmDisplay = document.createElement('div');
timeText.textContent = new Date(`1970-01-01T${time}`).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); alarmDisplay.className = 'd-flex align-items-center';
const noteLink = document.createElement('a'); const switchDiv = document.createElement('div');
noteLink.href = `/note.php?id=${noteId}`; switchDiv.className = 'form-check form-switch me-3';
noteLink.textContent = `Note #${noteId}`; const switchInput = document.createElement('input');
noteLink.className = 'mx-3'; 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'); const timeDiv = document.createElement('div');
toggleSwitch.className = 'form-check form-switch'; const timeSpan = document.createElement('span');
const toggleInput = document.createElement('input'); timeSpan.className = 'fw-bold fs-5';
toggleInput.className = 'form-check-input'; timeSpan.textContent = new Date(`1970-01-01T${alarm_time}`).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: true });
toggleInput.type = 'checkbox'; const labelSpan = document.createElement('span');
toggleInput.role = 'switch'; labelSpan.className = 'text-muted ms-2';
toggleInput.checked = isActive; labelSpan.textContent = label;
toggleInput.addEventListener('change', () => handleToggleAlarm(id, toggleInput.checked));
toggleSwitch.appendChild(toggleInput);
const deleteBtn = document.createElement('button'); timeDiv.appendChild(timeSpan);
deleteBtn.className = 'btn btn-danger btn-sm ms-3'; timeDiv.appendChild(labelSpan);
deleteBtn.textContent = 'Delete';
deleteBtn.addEventListener('click', () => handleDeleteAlarm(id));
controls.appendChild(toggleSwitch); alarmDisplay.appendChild(switchDiv);
controls.appendChild(deleteBtn); 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 = '<i data-feather="trash-2" class="align-text-bottom"></i>';
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); alarmsList.appendChild(listItem);
feather.replace();
}; };
// Handle form submission to create a new alarm // Handle form submission to create a new alarm
if (alarmForm) { if (alarmForm) {
alarmForm.addEventListener('submit', function (e) { alarmForm.addEventListener('submit', function (e) {
e.preventDefault(); 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', { fetch('/api/alarms.php?action=create', {
method: 'POST', method: 'POST',
body: formData body: formData
@ -91,10 +165,10 @@ document.addEventListener('DOMContentLoaded', function () {
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
addAlarmToList(data.id, data.alarm_time, data.note_id, true); addAlarmToList(data.alarm);
alarmForm.reset(); alarmForm.reset();
} else { } else {
alert('Error: ' + data.error); alert('Error: ' + (data.error || 'Could not create alarm.'));
} }
}); });
}); });
@ -108,8 +182,11 @@ document.addEventListener('DOMContentLoaded', function () {
.then(data => { .then(data => {
if (data.success) { if (data.success) {
document.querySelector(`li[data-id='${id}']`).remove(); document.querySelector(`li[data-id='${id}']`).remove();
if (alarmsList.children.length === 0) {
alarmsList.innerHTML = '<li class="list-group-item text-center text-muted" id="noAlarmsMessage">No alarms set yet.</li>';
}
} else { } else {
alert('Error: ' + data.error); alert('Error: ' + (data.error || 'Could not delete alarm.'));
} }
}); });
}; };
@ -127,24 +204,29 @@ document.addEventListener('DOMContentLoaded', function () {
.then(data => { .then(data => {
if (!data.success) { if (!data.success) {
alert('Error updating alarm status.'); 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 // Function to check for due alarms
const checkAlarms = () => { const checkAlarms = () => {
fetch('/api/alarms.php?action=check') fetch('/api/alarms.php?action=check')
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.success && data.alarms.length > 0) { if (data.success && data.alarms && data.alarms.length > 0) {
const alarm = data.alarms[0]; // Assuming one alarm for now data.alarms.forEach(alarm => {
showNotification(alarm); showNotification(alarm);
// Also update the toggle on the main page // Update the toggle on the main page to off
const alarmToggle = document.querySelector(`li[data-id='${alarm.id}'] input[type='checkbox']`); const alarmToggle = document.querySelector(`#toggle-${alarm.id}`);
if (alarmToggle) { if (alarmToggle) {
alarmToggle.checked = false; alarmToggle.checked = false;
} }
});
} }
}); });
}; };
@ -152,35 +234,49 @@ document.addEventListener('DOMContentLoaded', function () {
// Function to show notification // Function to show notification
const showNotification = (alarm) => { const showNotification = (alarm) => {
const alarmTime = new Date(`1970-01-01T${alarm.alarm_time}`).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); const alarmTime = new Date(`1970-01-01T${alarm.alarm_time}`).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
const notificationTitle = `Alarm! It's ${alarmTime}`; const notificationTitle = `Alarm: ${alarm.label || alarmTime}`;
const notificationBody = `Click to open your note.`; 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, { const notification = new Notification(notificationTitle, {
body: notificationBody, body: notificationBody,
icon: '/assets/images/bell.png', // Optional: add an icon icon: bellIcon,
sound: '/assets/sounds/alarm.mp3', // This doesn't work, sound is handled separately requireInteraction: true
requireInteraction: true // Keeps notification open until user interacts
}); });
notification.onclick = () => { notification.onclick = () => {
window.open(`/note.php?id=${alarm.note_id}`, '_blank'); window.open(`note.php?id=${alarm.note_id}`, '_blank');
notification.close(); 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 = () => { dismissAlarmBtn.onclick = () => {
alarmSound.pause(); const audio = document.getElementById('alarmSound');
alarmSound.currentTime = 0; if (audio) {
audio.pause();
audio.currentTime = 0;
}
alarmModal.hide(); alarmModal.hide();
}; };
}; };
// Initial fetch and periodic check // Initial fetch and periodic check
fetchAlarms(); fetchAlarms();
setInterval(checkAlarms, 5000); // Check every 5 seconds setInterval(checkAlarms, 5000); // Check every 5 seconds

View File

@ -36,6 +36,14 @@ try {
<main class="container mt-5"> <main class="container mt-5">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-8"> <div class="col-md-8">
<!-- Notifications Permission Button -->
<div class="card mb-4" id="notification-permission-card">
<div class="card-body text-center">
<p class="card-text">For alarms to work even when the browser is in the background, please enable notifications.</p>
<button id="enable-notifications" class="btn btn-primary">Enable Notifications</button>
</div>
</div>
<!-- Create Alarm Form --> <!-- Create Alarm Form -->
<div class="card mb-4"> <div class="card mb-4">
<div class="card-body"> <div class="card-body">
@ -108,7 +116,6 @@ try {
<div class="modal-body p-5"> <div class="modal-body p-5">
<i data-feather="bell" class="feather-lg text-warning mb-3"></i> <i data-feather="bell" class="feather-lg text-warning mb-3"></i>
<h1 class="my-4">Alarm Clock</h1> <h1 class="my-4">Alarm Clock</h1>
<button id="enable-notifications" class="btn btn-primary mb-4">Enable Notifications</button>
<p id="alarmModalMessage" class="lead">Time to write your notes.</p> <p id="alarmModalMessage" class="lead">Time to write your notes.</p>
<button type="button" class="btn btn-primary btn-lg mt-3" id="dismissAlarmBtn">Dismiss</button> <button type="button" class="btn btn-primary btn-lg mt-3" id="dismissAlarmBtn">Dismiss</button>
</div> </div>