// ── Telegram Web App init ───────────────────────────────────────
const tg = window.Telegram.WebApp;
tg.ready();
tg.expand();
const API = window.location.origin + "/api";
const userId = tg.initDataUnsafe?.user?.id;
if (!userId) {
document.getElementById("app").innerHTML =
'
Superset
';
} else {
groupsHtml += '
';
}
group.forEach((ex) => {
const machine = ex.machine_id ? `
(${ex.machine_id})` : "";
const details = ex.sets_detail || [];
let detailStr;
if (details.length > 0 && !details.every(
(d) => d.reps === details[0].reps && d.weight_kg === details[0].weight_kg
)) {
detailStr = details.map((d) =>
d.weight_kg ? `${d.reps}x${fmtWeight(d.weight_kg)}kg` : `${d.reps}`
).join(", ");
} else {
const w = ex.weight_kg;
detailStr = w ? `${ex.sets}x${ex.reps}x${fmtWeight(w)}kg` : `${ex.sets}x${ex.reps}`;
}
groupsHtml += `
${ex.name}${machine}
— ${detailStr}
`;
});
groupsHtml += "
";
});
card.innerHTML = `
${groupsHtml}
`;
card.querySelector(".btn-history-edit").addEventListener("click", (e) => {
e.stopPropagation();
editSavedWorkout(w);
});
card.querySelector(".btn-history-delete").addEventListener("click", (e) => {
e.stopPropagation();
confirmDeleteWorkout(w);
});
container.appendChild(card);
});
historyOffset += data.workouts.length;
loadMore.style.display = historyOffset < data.total ? "block" : "none";
} catch (e) {
console.error("Failed to load history", e);
}
}
document.getElementById("btn-load-more").addEventListener("click", () => {
loadHistory(true);
});
function confirmDeleteWorkout(w) {
const label = "Workout #" + (w.user_number ?? w.id);
const prompt = "Delete " + label + "? This can't be undone.";
const onConfirm = async (ok) => {
if (!ok) return;
try {
await api("DELETE", "/workouts/" + w.id);
showToast(label + " deleted");
tg.HapticFeedback.notificationOccurred("success");
loadHistory();
} catch (e) {
showToast(e.message || "Delete failed");
tg.HapticFeedback.notificationOccurred("error");
}
};
if (tg && typeof tg.showConfirm === "function") {
tg.showConfirm(prompt, onConfirm);
} else {
onConfirm(window.confirm(prompt));
}
}
// ── Stats View ──────────────────────────────────────────────────
async function loadStats() {
try {
const data = await api("GET", "/stats");
const container = document.getElementById("stats-content");
if (data.total_workouts === 0) {
container.innerHTML = '
';
return;
}
container.innerHTML = `
${data.total_workouts}
Workouts
${data.unique_exercises}
Exercises
${data.total_sets.toLocaleString()}
Total Sets
${Math.round(data.total_volume).toLocaleString()}
Volume (kg)
`;
} catch (e) {
console.error("Failed to load stats", e);
}
}
// ── Helpers ─────────────────────────────────────────────────────
function fmtWeight(w) {
return w === Math.floor(w) ? Math.floor(w).toString() : w.toString();
}
// ── Settings ────────────────────────────────────────────────────
async function loadSettings() {
if (!userId) return;
try {
const data = await api("GET", "/settings");
settings = { rest_timer: true, weight_sign_button: true, ...(data.settings || {}) };
applySettingsToUI();
updateRestTimer();
} catch (e) {
console.error("Failed to load settings", e);
}
}
function applyWeightSignVisibility() {
if (!btnWeightSign) return;
btnWeightSign.classList.toggle("hidden", !settingEnabled("weight_sign_button"));
}
function applySettingsToUI() {
const restToggle = document.getElementById("setting-rest-timer");
if (restToggle) restToggle.checked = settingEnabled("rest_timer");
const signToggle = document.getElementById("setting-weight-sign");
if (signToggle) signToggle.checked = settingEnabled("weight_sign_button");
applyWeightSignVisibility();
}
async function saveSetting(key, value) {
// Optimistic: update locally first, then sync.
settings[key] = value;
updateRestTimer();
applyWeightSignVisibility();
try {
await api("PUT", "/settings", { [key]: value });
} catch (e) {
showToast("Could not save setting");
console.error(e);
}
}
document.getElementById("setting-rest-timer")?.addEventListener("change", (e) => {
saveSetting("rest_timer", e.target.checked);
});
document.getElementById("setting-weight-sign")?.addEventListener("change", (e) => {
saveSetting("weight_sign_button", e.target.checked);
});
// ── Version badge ───────────────────────────────────────────────
async function loadVersion() {
try {
const res = await fetch(API + "/version");
if (!res.ok) return;
const data = await res.json();
const badge = document.getElementById("version-badge");
if (badge && data.version) badge.textContent = data.version;
} catch (e) {
// Silent — footer just stays empty if unreachable
}
}
// ── Init ────────────────────────────────────────────────────────
async function init() {
loadVersion();
if (!userId) return;
logEvent("miniapp.open");
loadSettings(); // fire-and-forget; UI updates when ready
try {
const data = await api("GET", "/exercises");
knownExercises = data.exercises || [];
} catch (e) {
console.error("Failed to load exercises", e);
}
restoreDraft();
}
init();