Update to v1.0.3: Guided Relaxation & UI Fixes
This commit is contained in:
@@ -1190,30 +1190,171 @@ function startExercise(type) {
|
||||
}
|
||||
|
||||
function quickRelax() {
|
||||
const techniques = [
|
||||
{ icon: '🌬️', title: 'Box Breathing', desc: 'Inhale 4s, Hold 4s, Exhale 4s, Hold 4s' },
|
||||
{ icon: '👁️', title: '5-4-3-2-1 Grounding', desc: 'Name 5 things you see, 4 feel, 3 hear...' },
|
||||
{ icon: '💆', title: 'Muscle Relaxation', desc: 'Tense and release each muscle group' },
|
||||
{ icon: '🌊', title: 'Visualisation', desc: 'Imagine a peaceful place in detail' }
|
||||
];
|
||||
// Launch the new Guided Grounding Experience
|
||||
startGuidedRelaxation();
|
||||
}
|
||||
|
||||
// --- Guided Relaxation (5-4-3-2-1 Grounding) ---
|
||||
let relaxationState = {
|
||||
step: 0,
|
||||
isActive: false,
|
||||
steps: [
|
||||
{
|
||||
title: "Sight",
|
||||
instruction: "Look around you.",
|
||||
sub: "Find 5 things you can see.",
|
||||
count: 5,
|
||||
icon: "👁️",
|
||||
color: "#64B5F6"
|
||||
},
|
||||
{
|
||||
title: "Touch",
|
||||
instruction: "Feel the textures.",
|
||||
sub: "Find 4 things you can touch.",
|
||||
count: 4,
|
||||
icon: "✋",
|
||||
color: "#81C784"
|
||||
},
|
||||
{
|
||||
title: "Sound",
|
||||
instruction: "Listen carefully.",
|
||||
sub: "Identify 3 sounds you hear.",
|
||||
count: 3,
|
||||
icon: "👂",
|
||||
color: "#FFB74D"
|
||||
},
|
||||
{
|
||||
title: "Smell",
|
||||
instruction: "Breathe in deep.",
|
||||
sub: "Notice 2 things you can smell.",
|
||||
count: 2,
|
||||
icon: "👃",
|
||||
color: "#BA68C8"
|
||||
},
|
||||
{
|
||||
title: "Taste",
|
||||
instruction: "Focus on your mouth.",
|
||||
sub: "Find 1 thing you can taste.",
|
||||
count: 1,
|
||||
icon: "👅",
|
||||
color: "#E57373"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
function startGuidedRelaxation() {
|
||||
relaxationState.step = 0;
|
||||
relaxationState.isActive = true;
|
||||
|
||||
const randomTechnique = techniques[Math.floor(Math.random() * techniques.length)];
|
||||
const overlay = document.createElement('div');
|
||||
overlay.id = 'guided-relaxation-overlay';
|
||||
overlay.className = 'guided-overlay';
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'exercise-modal';
|
||||
modal.style.display = 'block';
|
||||
modal.innerHTML = `
|
||||
<div class="card" style="text-align: center;">
|
||||
<div style="font-size: 60px; margin-bottom: 20px; animation: bounce 2s infinite;">${randomTechnique.icon}</div>
|
||||
<h3>${randomTechnique.title}</h3>
|
||||
<p style="font-size: 18px; margin-bottom: 30px;">${randomTechnique.desc}</p>
|
||||
<div class="exercise-actions" style="justify-content: center;">
|
||||
<button class="btn btn-primary" onclick="this.closest('.exercise-modal').remove(); quickRelax()">Try Another</button>
|
||||
<button class="btn btn-secondary" onclick="finishRelaxSession(this)">Done</button>
|
||||
</div>
|
||||
renderRelaxationStep();
|
||||
}
|
||||
|
||||
function renderRelaxationStep() {
|
||||
const overlay = document.getElementById('guided-relaxation-overlay');
|
||||
if (!overlay || !relaxationState.isActive) return;
|
||||
|
||||
const currentStep = relaxationState.steps[relaxationState.step];
|
||||
const progressDots = Array(currentStep.count).fill('<div class="progress-dot"></div>').join('');
|
||||
|
||||
overlay.innerHTML = `
|
||||
<div class="guided-controls">
|
||||
<button class="icon-btn" onclick="closeGuidedRelaxation()">
|
||||
<span class="material-icons">close</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="guided-step-icon" style="color: ${currentStep.color}">
|
||||
${currentStep.icon}
|
||||
</div>
|
||||
|
||||
<div class="guided-instruction">${currentStep.instruction}</div>
|
||||
<div class="guided-sub">${currentStep.sub}</div>
|
||||
|
||||
<div class="guided-progress-dots" id="step-dots">
|
||||
${progressDots}
|
||||
</div>
|
||||
|
||||
<button class="guided-action-btn" onclick="nextRelaxationSubStep()">
|
||||
I found one
|
||||
</button>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
|
||||
// Speak instruction
|
||||
speakText(`${currentStep.instruction} ${currentStep.sub}`);
|
||||
}
|
||||
|
||||
let currentDotIndex = 0;
|
||||
|
||||
function nextRelaxationSubStep() {
|
||||
const dots = document.querySelectorAll('.progress-dot');
|
||||
if (currentDotIndex < dots.length) {
|
||||
dots[currentDotIndex].classList.add('active');
|
||||
// Haptic feedback
|
||||
if (navigator.vibrate) navigator.vibrate(50);
|
||||
// Sound feedback
|
||||
soundManager.playTone(400 + (currentDotIndex * 50), 'sine', 0.1, 0.1);
|
||||
|
||||
currentDotIndex++;
|
||||
|
||||
if (currentDotIndex === dots.length) {
|
||||
setTimeout(() => {
|
||||
currentDotIndex = 0;
|
||||
relaxationState.step++;
|
||||
if (relaxationState.step < relaxationState.steps.length) {
|
||||
renderRelaxationStep();
|
||||
} else {
|
||||
finishGuidedRelaxation();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function finishGuidedRelaxation() {
|
||||
const overlay = document.getElementById('guided-relaxation-overlay');
|
||||
if (overlay) {
|
||||
overlay.innerHTML = `
|
||||
<div class="guided-step-icon">🌟</div>
|
||||
<div class="guided-instruction">You did great!</div>
|
||||
<div class="guided-sub">Feeling more grounded?</div>
|
||||
<button class="guided-action-btn" onclick="closeGuidedRelaxation()">
|
||||
Complete
|
||||
</button>
|
||||
`;
|
||||
speakText("You did great. Feeling more grounded?");
|
||||
triggerSuccessPing();
|
||||
}
|
||||
}
|
||||
|
||||
function closeGuidedRelaxation() {
|
||||
relaxationState.isActive = false;
|
||||
currentDotIndex = 0;
|
||||
const overlay = document.getElementById('guided-relaxation-overlay');
|
||||
if (overlay) overlay.remove();
|
||||
|
||||
if (window.speechSynthesis) {
|
||||
window.speechSynthesis.cancel();
|
||||
}
|
||||
|
||||
// Log session
|
||||
if (relaxationState.step >= relaxationState.steps.length) {
|
||||
exerciseAPI.logSession('grounding', 180).then(() => updateProgress());
|
||||
}
|
||||
}
|
||||
|
||||
function speakText(text) {
|
||||
if ('speechSynthesis' in window) {
|
||||
window.speechSynthesis.cancel(); // Stop previous
|
||||
const utterance = new SpeechSynthesisUtterance(text);
|
||||
utterance.rate = 0.9;
|
||||
utterance.pitch = 1.0;
|
||||
window.speechSynthesis.speak(utterance);
|
||||
}
|
||||
}
|
||||
|
||||
function finishRelaxSession(btn) {
|
||||
|
||||
Reference in New Issue
Block a user