Auto commit: 2025-11-21T18:47:34.824Z
This commit is contained in:
parent
6a4e5985e5
commit
494f12a424
@ -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
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user