update settings page with status message area
This commit is contained in:
+41
-1
@@ -525,7 +525,47 @@
|
||||
margin-top: 18px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid rgba(63, 63, 70, 0.7);
|
||||
justify-content: flex-end;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.settings-page-status {
|
||||
min-width: min(100%, 360px);
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(63, 63, 70, 0.92);
|
||||
background: rgba(9, 9, 11, 0.78);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.settings-page-status[data-tone="info"] {
|
||||
border-color: rgba(59, 130, 246, 0.55);
|
||||
background: rgba(30, 64, 175, 0.14);
|
||||
}
|
||||
.settings-page-status[data-tone="success"] {
|
||||
border-color: rgba(34, 197, 94, 0.5);
|
||||
background: rgba(20, 83, 45, 0.18);
|
||||
}
|
||||
.settings-page-status[data-tone="warning"] {
|
||||
border-color: rgba(245, 158, 11, 0.55);
|
||||
background: rgba(120, 53, 15, 0.18);
|
||||
}
|
||||
.settings-page-status[data-tone="error"] {
|
||||
border-color: rgba(239, 68, 68, 0.55);
|
||||
background: rgba(127, 29, 29, 0.2);
|
||||
}
|
||||
.settings-page-status-text {
|
||||
color: #f8fafc;
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.settings-page-status-time {
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.calendar-year-control {
|
||||
display: flex;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"use strict";
|
||||
|
||||
const SETTINGS_STORAGE_KEY = "tarot-time-settings-v1";
|
||||
const SETTINGS_LAST_SAVED_AT_STORAGE_KEY = "tarot-time-settings-last-saved-at-v1";
|
||||
|
||||
let config = {
|
||||
defaultSettings: {
|
||||
@@ -39,11 +40,89 @@
|
||||
stellariumBackgroundHintEl: document.getElementById("stellarium-background-hint"),
|
||||
apiBaseUrlEl: document.getElementById("api-base-url"),
|
||||
apiKeyEl: document.getElementById("api-key"),
|
||||
settingsPageStatusEl: document.getElementById("settings-page-status"),
|
||||
settingsPageStatusTextEl: document.getElementById("settings-page-status-text"),
|
||||
settingsPageStatusTimeEl: document.getElementById("settings-page-status-time"),
|
||||
saveSettingsEl: document.getElementById("save-settings"),
|
||||
useLocationEl: document.getElementById("use-location")
|
||||
};
|
||||
}
|
||||
|
||||
function loadLastSavedAt() {
|
||||
try {
|
||||
const raw = window.localStorage.getItem(SETTINGS_LAST_SAVED_AT_STORAGE_KEY);
|
||||
const parsed = Number(raw);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function persistLastSavedAt(timestamp) {
|
||||
try {
|
||||
window.localStorage.setItem(SETTINGS_LAST_SAVED_AT_STORAGE_KEY, String(timestamp));
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function formatLastSavedAt(timestamp) {
|
||||
if (!Number.isFinite(timestamp) || timestamp <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "short"
|
||||
}).format(new Date(timestamp));
|
||||
} catch {
|
||||
return new Date(timestamp).toLocaleString();
|
||||
}
|
||||
}
|
||||
|
||||
function setSettingsPageStatus(message, tone = "neutral", options = {}) {
|
||||
const {
|
||||
settingsPageStatusEl,
|
||||
settingsPageStatusTextEl,
|
||||
settingsPageStatusTimeEl
|
||||
} = getElements();
|
||||
|
||||
if (settingsPageStatusEl) {
|
||||
settingsPageStatusEl.dataset.tone = String(tone || "neutral");
|
||||
}
|
||||
|
||||
if (settingsPageStatusTextEl) {
|
||||
settingsPageStatusTextEl.textContent = String(message || "Settings ready.");
|
||||
}
|
||||
|
||||
const savedAt = options.savedAt === undefined ? loadLastSavedAt() : options.savedAt;
|
||||
const formattedSavedAt = formatLastSavedAt(savedAt);
|
||||
if (settingsPageStatusTimeEl) {
|
||||
settingsPageStatusTimeEl.hidden = !formattedSavedAt;
|
||||
settingsPageStatusTimeEl.textContent = formattedSavedAt ? `Last saved ${formattedSavedAt}` : "";
|
||||
}
|
||||
}
|
||||
|
||||
function syncSavedSettingsStatus(message = "Settings ready.") {
|
||||
setSettingsPageStatus(message, "neutral", { savedAt: loadLastSavedAt() });
|
||||
}
|
||||
|
||||
function setSaveButtonBusy(isBusy) {
|
||||
const { saveSettingsEl } = getElements();
|
||||
if (!saveSettingsEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!saveSettingsEl.dataset.defaultLabel) {
|
||||
saveSettingsEl.dataset.defaultLabel = String(saveSettingsEl.textContent || "Save Settings").trim() || "Save Settings";
|
||||
}
|
||||
|
||||
saveSettingsEl.disabled = Boolean(isBusy);
|
||||
saveSettingsEl.textContent = isBusy ? "Saving..." : saveSettingsEl.dataset.defaultLabel;
|
||||
}
|
||||
|
||||
function setLocationEntryState(isExplicit) {
|
||||
const { latEl, lngEl } = getElements();
|
||||
const normalizedValue = isExplicit ? "true" : "false";
|
||||
@@ -466,6 +545,7 @@
|
||||
lastNonSettingsSection = activeSection;
|
||||
}
|
||||
applySettingsToInputs(loadSavedSettings());
|
||||
syncSavedSettingsStatus();
|
||||
config.setActiveSection?.("settings");
|
||||
}
|
||||
|
||||
@@ -474,6 +554,9 @@
|
||||
}
|
||||
|
||||
async function handleSaveSettings() {
|
||||
setSaveButtonBusy(true);
|
||||
setSettingsPageStatus("Saving settings...", "info");
|
||||
|
||||
try {
|
||||
const settings = getSettingsFromInputs();
|
||||
const previousConnectionSettings = getConnectionSettings();
|
||||
@@ -501,22 +584,39 @@
|
||||
}
|
||||
|
||||
if (!didPersist || connectionResult.didPersist === false) {
|
||||
setSettingsPageStatus("Settings applied for this session. Browser storage is unavailable.", "warning", {
|
||||
savedAt: loadLastSavedAt()
|
||||
});
|
||||
setStatus("Settings applied for this session. Browser storage is unavailable.");
|
||||
} else {
|
||||
const savedAt = Date.now();
|
||||
persistLastSavedAt(savedAt);
|
||||
setSettingsPageStatus("Settings saved.", "success", { savedAt });
|
||||
setStatus("Settings saved.");
|
||||
}
|
||||
} catch (error) {
|
||||
setSettingsPageStatus(error?.message || "Unable to save settings.", "error", {
|
||||
savedAt: loadLastSavedAt()
|
||||
});
|
||||
setStatus(error?.message || "Unable to save settings.");
|
||||
} finally {
|
||||
setSaveButtonBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
function requestGeoLocation() {
|
||||
const { latEl, lngEl } = getElements();
|
||||
if (!navigator.geolocation) {
|
||||
setSettingsPageStatus("Geolocation not available in this browser.", "warning", {
|
||||
savedAt: loadLastSavedAt()
|
||||
});
|
||||
setStatus("Geolocation not available in this browser.");
|
||||
return;
|
||||
}
|
||||
|
||||
setSettingsPageStatus("Getting your location...", "info", {
|
||||
savedAt: loadLastSavedAt()
|
||||
});
|
||||
setStatus("Getting your location...");
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
({ coords }) => {
|
||||
@@ -531,10 +631,16 @@
|
||||
hasExplicitLocation: true
|
||||
}
|
||||
);
|
||||
setSettingsPageStatus("Location captured. Save Settings to keep it in this browser.", "info", {
|
||||
savedAt: loadLastSavedAt()
|
||||
});
|
||||
setStatus("Location set from browser. Save Settings to use it across the app.");
|
||||
},
|
||||
(err) => {
|
||||
const detail = err?.message || `code ${err?.code ?? "unknown"}`;
|
||||
setSettingsPageStatus(`Could not get location (${detail}).`, "error", {
|
||||
savedAt: loadLastSavedAt()
|
||||
});
|
||||
setStatus(`Could not get location (${detail}).`);
|
||||
},
|
||||
{ enableHighAccuracy: true, timeout: 10000 }
|
||||
@@ -612,6 +718,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
syncSavedSettingsStatus();
|
||||
bindInteractions();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user