HydroFlux 0.0.1
This commit is contained in:
184
js/modules/water.js
Normal file
184
js/modules/water.js
Normal file
@@ -0,0 +1,184 @@
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user