Files
HydroFlux/js/modules/water.js
2026-02-04 20:56:57 +11:00

185 lines
6.5 KiB
JavaScript

export class WaterTracker {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.state = {
current: 0,
goal: 3000, // mL
bottleSize: 500, // mL
};
this.STORAGE_KEY = 'hydroflux_data';
this.loadState();
this.render();
this.attachEvents();
this.updateUI();
}
loadState() {
const saved = localStorage.getItem(this.STORAGE_KEY);
if (saved) {
const parsed = JSON.parse(saved);
this.state = { ...this.state, ...parsed };
}
}
saveState() {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.state));
this.updateUI();
}
addWater() {
this.state.current += this.state.bottleSize;
this.saveState();
if (navigator.vibrate) navigator.vibrate(50);
}
removeWater() {
this.state.current = Math.max(0, this.state.current - this.state.bottleSize);
this.saveState();
if (navigator.vibrate) navigator.vibrate(50);
}
setBottleSize(size) {
if (!size || size <= 0) return;
this.state.bottleSize = size;
this.saveState();
}
getPercentage() {
return Math.min(100, Math.max(0, (this.state.current / this.state.goal) * 100));
}
updateUI() {
// Update Text
const currentEl = this.container.querySelector('.water-count');
const percentageEl = this.container.querySelector('.water-percentage');
if (currentEl) currentEl.textContent = `${this.state.current} / ${this.state.goal} mL`;
if (percentageEl) percentageEl.textContent = `${Math.round(this.getPercentage())}%`;
// Update Wave Animation Height
const wave = this.container.querySelector('.wave');
if (wave) {
wave.style.top = `${100 - this.getPercentage()}%`;
}
}
render() {
this.container.innerHTML = `
<div class="water-tracker-container">
<!-- Circular Progress -->
<div class="circle-container">
<div class="water-circle">
<div class="wave"></div>
<div class="circle-content">
<span class="water-percentage">0%</span>
<span class="water-label">HYDRATION</span>
</div>
</div>
</div>
<!-- Stats Display -->
<div class="stats-row">
<span class="water-count">0 / 3000 mL</span>
</div>
<!-- Controls -->
<div class="controls-area">
<div class="bottle-selector">
<label>Bottle (mL):</label>
<input type="number" id="bottle-size-input" value="${this.state.bottleSize}" min="1" max="5000">
</div>
<div class="action-buttons">
<button id="remove-water-btn" class="icon-btn secondary" aria-label="Remove Drink">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14"/>
</svg>
</button>
<button id="add-water-btn" class="glow-btn">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 5v14M5 12h14"/>
</svg>
DRINK
</button>
<!-- Notification Toggle -->
<button id="notify-btn" class="icon-btn" title="Enable Reminders">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
</svg>
</button>
</div>
</div>
</div>
`;
this.checkNotificationStatus();
}
attachEvents() {
this.container.querySelector('#add-water-btn').addEventListener('click', () => {
this.addWater();
});
this.container.querySelector('#remove-water-btn').addEventListener('click', () => {
this.removeWater();
});
this.container.querySelector('#notify-btn').addEventListener('click', (e) => {
this.toggleNotifications(e.currentTarget);
});
const input = this.container.querySelector('#bottle-size-input');
input.addEventListener('change', (e) => {
this.setBottleSize(parseInt(e.target.value));
});
}
// --- Notification Logic ---
toggleNotifications(btn) {
if (!("Notification" in window)) {
alert("This browser does not support desktop notifications");
return;
}
if (Notification.permission === "granted") {
alert("Reminders are active! We'll check every hour.");
} else if (Notification.permission !== "denied") {
Notification.requestPermission().then(permission => {
if (permission === "granted") {
this.startReminderLoop();
btn.style.color = "var(--primary-cyan)";
new Notification("HydroFlux", { body: "Smart Hydration Reminders Enabled!" });
}
});
}
}
checkNotificationStatus() {
if (Notification.permission === "granted") {
const btn = this.container.querySelector('#notify-btn');
if (btn) btn.style.color = "var(--primary-cyan)";
this.startReminderLoop();
}
}
startReminderLoop() {
// Clear existing to avoid duplicates
if (this.reminderInterval) clearInterval(this.reminderInterval);
// Check every minute if it's been > 1 hour since last drink
this.reminderInterval = setInterval(() => {
// Pseudo-logic check since we don't store timestamp in this simple version yet
// In a real app, you'd check this.state.lastDrinkTime
new Notification("HydroFlux Needs You", {
body: "Remember to drink water!",
icon: "/icon.png"
});
}, 3600000); // 1 Hour
}
}