141 lines
4.3 KiB
JavaScript
141 lines
4.3 KiB
JavaScript
const API_BASE = "http://127.0.0.1:5000/api";
|
|
const THEME_STORAGE_KEY = "journey-theme";
|
|
const DEFAULT_THEME = "dark";
|
|
const VALID_THEMES = ["dark", "light", "system"];
|
|
let currentUser = null;
|
|
|
|
function getThemePreference() {
|
|
const storedTheme = localStorage.getItem(THEME_STORAGE_KEY);
|
|
return VALID_THEMES.includes(storedTheme) ? storedTheme : DEFAULT_THEME;
|
|
}
|
|
|
|
function applyThemePreference(theme = getThemePreference()) {
|
|
const selectedTheme = VALID_THEMES.includes(theme) ? theme : DEFAULT_THEME;
|
|
document.documentElement.dataset.theme = selectedTheme;
|
|
}
|
|
|
|
function setThemePreference(theme) {
|
|
const selectedTheme = VALID_THEMES.includes(theme) ? theme : DEFAULT_THEME;
|
|
localStorage.setItem(THEME_STORAGE_KEY, selectedTheme);
|
|
applyThemePreference(selectedTheme);
|
|
}
|
|
|
|
function getThemeControlHtml() {
|
|
return `
|
|
<div class="theme-switcher">
|
|
<label for="theme-select">Theme</label>
|
|
<select id="theme-select" class="theme-select" aria-label="Theme">
|
|
<option value="dark">Dark</option>
|
|
<option value="light">Light</option>
|
|
<option value="system">System</option>
|
|
</select>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function attachThemeControl(root = document) {
|
|
const select = root.querySelector("#theme-select");
|
|
if (!select) return;
|
|
select.value = getThemePreference();
|
|
select.addEventListener("change", () => setThemePreference(select.value));
|
|
}
|
|
|
|
function ensureStandaloneThemeControl() {
|
|
if (document.getElementById("user-menu") || document.getElementById("theme-switcher-floating")) return;
|
|
const container = document.createElement("div");
|
|
container.id = "theme-switcher-floating";
|
|
container.className = "theme-switcher-floating";
|
|
container.innerHTML = getThemeControlHtml();
|
|
document.body.appendChild(container);
|
|
attachThemeControl(container);
|
|
}
|
|
|
|
applyThemePreference();
|
|
|
|
async function checkAuth() {
|
|
try {
|
|
const res = await fetch(`${API_BASE}/me`, { credentials: "include" });
|
|
if (res.ok) {
|
|
currentUser = await res.json();
|
|
return true;
|
|
}
|
|
return false;
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function checkAuthAndRedirect() {
|
|
const ok = await checkAuth();
|
|
if (!ok) {
|
|
window.location.href = "login.html";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function updateUserMenu() {
|
|
const container = document.getElementById("user-menu");
|
|
if (!container) return;
|
|
if (currentUser) {
|
|
container.innerHTML = `
|
|
${getThemeControlHtml()}
|
|
<span class="username"><i class="fas fa-user"></i> ${escapeHtml(currentUser.username)}</span>
|
|
<button id="logout-btn" class="logout-btn"><i class="fas fa-sign-out-alt"></i> Logout</button>
|
|
`;
|
|
attachThemeControl(container);
|
|
document.getElementById("logout-btn")?.addEventListener("click", logout);
|
|
} else {
|
|
container.innerHTML = `
|
|
${getThemeControlHtml()}
|
|
<button id="login-open-btn" class="login-btn"><i class="fas fa-sign-in-alt"></i> Login</button>
|
|
`;
|
|
attachThemeControl(container);
|
|
document.getElementById("login-open-btn")?.addEventListener("click", () => {
|
|
window.location.href = "login.html";
|
|
});
|
|
}
|
|
}
|
|
|
|
async function logout() {
|
|
await fetch(`${API_BASE}/logout`, { method: "POST", credentials: "include" });
|
|
window.location.href = "login.html";
|
|
}
|
|
|
|
function escapeHtml(str) {
|
|
if (!str) return "";
|
|
return String(str).replace(/[&<>"']/g, function (m) {
|
|
if (m === "&") return "&";
|
|
if (m === "<") return "<";
|
|
if (m === ">") return ">";
|
|
if (m === '"') return """;
|
|
if (m === "'") return "'";
|
|
return m;
|
|
});
|
|
}
|
|
|
|
function escapeAttribute(str) {
|
|
return escapeHtml(str);
|
|
}
|
|
|
|
function sanitizeDisplayUrl(url) {
|
|
const value = String(url || "").trim().replace(/[\u0000-\u001f\u007f\s]+/g, "");
|
|
if (/^(https?:\/\/|\/uploads\/|uploads\/)/i.test(value)) {
|
|
return value;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function showToast(msg, isError = false) {
|
|
const toast = document.getElementById("toast");
|
|
if (!toast) return;
|
|
toast.textContent = msg;
|
|
toast.style.backgroundColor = isError ? "var(--red-7)" : "var(--green-7)";
|
|
toast.style.display = "block";
|
|
setTimeout(() => {
|
|
toast.style.display = "none";
|
|
}, 3000);
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", ensureStandaloneThemeControl);
|