Multiple improvements
Sonarqube Scanner / Prepare environment (push) Successful in 10s
Sonarqube Scanner / Build and test (push) Successful in 28s
Sonarqube Scanner / SonarQube analysis (push) Successful in 1m8s
Sonarqube Scanner / Deploy to Server (push) Successful in 8s

This commit is contained in:
2026-10-05 01:18:14 +02:00
parent 2b07e9bc47
commit 49a9de0355
27 changed files with 797 additions and 80 deletions
+73 -24
View File
@@ -202,7 +202,17 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
return ns.I18n.t("editor.view-route");
}
let poiMediaGeneration = 0;
let previewPoi = null;
function releasePoiPreview() {
poiMediaGeneration += 1;
ns.LocalRoutes?.releasePoiMedia?.(previewPoi);
previewPoi = null;
}
function showListView() {
releasePoiPreview();
ns.RouteRecorder?.endEditing();
setDialogTitle('Streckenverwaltung');
setHidden(byId(ids.routeListView), false);
@@ -211,6 +221,7 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
}
function showRouteView() {
releasePoiPreview();
setDialogTitle(routeDialogTitle());
setHidden(byId(ids.routeListView), true);
setHidden(byId(ids.routeDetailView), false);
@@ -411,8 +422,8 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
list.textContent = '';
const serverRoutes = state.routes.filter(route => routeSource(route) === 'server');
const localRoutes = state.routes.filter(route => routeSource(route) === 'local');
renderRouteGroup(list, ns.I18n.t("editor.public-routes"), serverRoutes, ns.I18n.t("app.no-public-route"));
renderRouteGroup(list, ns.I18n.t("editor.drafts"), localRoutes, ns.I18n.t("editor.no-draft"));
renderRouteGroup(list, ns.I18n.t("editor.public-routes"), serverRoutes, ns.I18n.t("app.no-public-route"));
}
function renderPoiList() {
@@ -438,19 +449,30 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
}));
}
let routeListGeneration = 0;
async function loadRoutes() {
setStatus(ns.I18n.t("editor.loading-routes"));
const payload = await readJson('/routes?activatedOnly=true');
const serverRoutes = routeArray(payload).map(route => ({ ...route, _source: 'server' }));
let localRoutes = [];
try {
localRoutes = localRouteSummaries();
} catch (error) {
setStatus(error.message, true);
}
state.routes = [...serverRoutes, ...localRoutes];
const requestGeneration = ++routeListGeneration;
state.routes = [...localRouteSummaries(), ...state.routes.filter(route => routeSource(route) === 'server')];
renderRouteList();
if (!byId(ids.status)?.dataset.kind || byId(ids.status).dataset.kind !== 'error') setStatus('');
setStatus('');
// Local drafts are usable even while this request is waiting for a connection.
void refreshPublicRoutes(requestGeneration);
}
async function refreshPublicRoutes(requestGeneration) {
const listIsVisible = () => byId(ids.dialog)?.open && !byId(ids.routeListView)?.hidden;
try {
const payload = await readJson('/routes?activatedOnly=true');
if (requestGeneration !== routeListGeneration) return;
const serverRoutes = routeArray(payload).map(route => ({ ...route, _source: 'server' }));
state.routes = [...localRouteSummaries(), ...serverRoutes];
renderRouteList();
} catch {
if (requestGeneration === routeListGeneration && listIsVisible()) {
setStatus(ns.I18n.t('editor.public-unavailable'));
}
}
}
async function openEditor() {
@@ -471,6 +493,7 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
}
function closeEditor() {
releasePoiPreview();
ns.RouteRecorder?.endEditing();
const dialog = byId(ids.dialog);
if (dialog?.open && typeof dialog.close === 'function') dialog.close();
@@ -499,6 +522,7 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
function fillRouteForm(route) {
setHidden(byId('editor-path-positions'), state.currentSource !== 'local');
setHidden(byId('editor-recording-options'), state.currentSource !== 'local');
if (state.currentSource === 'local') ns.RouteRecorder?.renderPoints(route.gpx || ns.LocalRoutes.emptyGpx(route.name));
byId(ids.routeId).value = routeId(route);
byId(ids.routeName).value = routeName(route) === ns.I18n.t("editor.untitled-route") ? '' : routeName(route);
@@ -764,14 +788,14 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
async function loadRoute(route) {
const id = routeId(route);
if (!id) return;
if (ns.RouteRecorder?.routeId && (routeSource(route) !== 'local' || ns.RouteRecorder.routeId !== id)) {
ns.RouteRecorder.endEditing();
if (state.currentRoute && (routeSource(route) !== state.currentSource || routeId(state.currentRoute) !== id)) {
ns.RouteRecorder?.endEditing();
}
setStatus(ns.I18n.t("editor.route.loading"));
state.currentSource = routeSource(route);
if (state.currentSource === 'local') {
const routePackage = await ns.LocalRoutes.getWithMedia(id, { requestPermission: true });
const routePackage = ns.LocalRoutes.get(id);
if (!routePackage) throw new Error(ns.I18n.t("editor.error.draft-missing"));
state.currentRoute = {
...routePackage.route,
@@ -1048,6 +1072,7 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
}
function newPoi() {
releasePoiPreview();
const position = currentPosition();
const lat = position?.lat ?? null;
const lon = position?.lon ?? null;
@@ -1065,11 +1090,26 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
showPoiView();
}
function editPoi(poi) {
async function editPoi(poi) {
if (!poi) return;
releasePoiPreview();
state.currentPoi = poi;
fillPoiForm(poi);
byId(ids.poiFormTitle).textContent = ns.I18n.t("editor.edit-station");
showPoiView();
if (state.currentSource !== 'local') return;
const requestGeneration = poiMediaGeneration;
const hydrated = await ns.LocalRoutes.getPoiWithMedia(routeId(state.currentRoute), poi.id, { requestPermission: true });
if (requestGeneration !== poiMediaGeneration || state.currentPoi !== poi) {
ns.LocalRoutes.releasePoiMedia(hydrated);
return;
}
if (!hydrated) return;
previewPoi = hydrated;
state.currentPoi = hydrated;
// Preserve any text edits and newly selected files while existing previews load.
renderPoiMediaPreview();
updatePoiMediaStatus();
}
async function mediaFromLocalForm(existingPoi, poiIdentifier) {
@@ -1122,13 +1162,16 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
route: state.currentRoute,
pois: state.pois
});
const hydrated = await ns.LocalRoutes.getWithMedia(id) || saved;
state.currentRoute = { ...hydrated.route, id: hydrated.id, gpx: hydrated.gpx, _source: 'local' };
state.pois = hydrated.pois;
const previousPoi = state.currentPoi;
state.currentRoute = { ...saved.route, id: saved.id, gpx: saved.gpx, _source: 'local' };
state.pois = saved.pois;
state.currentPoi = currentPoiId
? state.pois.find(poi => poiId(poi) === currentPoiId) || null
: null;
return hydrated;
if (state.currentPoi && previousPoi) {
state.currentPoi = { ...state.currentPoi, audio: previousPoi.audio, images: previousPoi.images };
}
return saved;
}
async function saveLocalPoi(values) {
@@ -1241,7 +1284,10 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
}
updateCurrentPoi(updated);
await persistLocalCurrentRoute();
if (removedMedia) await ns.LocalRoutes.removeMedia(removedMedia);
if (removedMedia) {
ns.LocalRoutes.releasePoiMedia({ audio: removedMedia });
await ns.LocalRoutes.removeMedia(removedMedia);
}
state.selectedMediaKey = null;
renderPoiMediaPreview();
updatePoiMediaStatus();
@@ -1416,7 +1462,7 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
async function poiListAction(event) {
const action = event.target.closest('[data-editor-action]')?.dataset.editorAction;
if (action === 'edit-poi') {
editPoi(poiForItem(event.target));
await editPoi(poiForItem(event.target));
return;
}
if (action === 'delete-poi') {
@@ -1442,12 +1488,15 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
function bind() {
for (const eventName of ['cancel', 'close']) {
byId(ids.dialog)?.addEventListener(eventName, () => ns.RouteRecorder?.endEditing());
byId(ids.dialog)?.addEventListener(eventName, () => {
releasePoiPreview();
ns.RouteRecorder?.endEditing();
});
}
activate(byId(ids.button), () => guarded(openEditor));
activate(byId(ids.close), closeEditor);
activate(byId(ids.newRoute), newRoute);
activate(byId(ids.routeBack), () => guarded(loadRoutes).then(showListView));
activate(byId(ids.routeBack), () => { showListView(); void guarded(loadRoutes); });
activate(byId(ids.routeUpload), () => guarded(uploadLocalRoute));
activate(byId(ids.rightsOpen), () => ns.LegalUi?.open('terms'));
activate(byId(ids.addPoi), newPoi);