load indicators and some cleanup

This commit is contained in:
Gwendolyn 2024-12-25 19:05:21 +01:00
parent f434e154d0
commit cba970e058
2 changed files with 210 additions and 86 deletions

View file

@ -1921,3 +1921,28 @@ blink {
} }
} }
} }
.location-load-info {
transform: translateX(-50%) translateY(150%);
border: 1px solid var(--color-overlay-background);
margin: 2px auto auto;
width: 40px;
height: 8px;
> .load-indicator {
--location-load-color: color-mix(in hsl, green, red var(--location-load-value));
display: block;
width: 100%;
height: 100%;
appearance: none;
border: 2px solid var(--location-load-color);
background: white;
&::after {
content: '';
display: block;
height: 100%;
width: var(--location-load-value);
background: var(--location-load-color);
}
}
}

View file

@ -104,6 +104,7 @@ c3nav = {
c3nav.load_material_symbols_if_needed(); c3nav.load_material_symbols_if_needed();
c3nav.load_searchable_locations(); c3nav.load_searchable_locations();
c3nav.load_load_indicator_data();
$('#messages').find('ul.messages li').each(function () { $('#messages').find('ul.messages li').each(function () {
$(this).prepend( $(this).prepend(
@ -145,12 +146,31 @@ c3nav = {
c3nav._searchable_locations_timer = null; c3nav._searchable_locations_timer = null;
c3nav_api.get('map/locations?searchable=true') c3nav_api.get('map/locations?searchable=true')
.then(c3nav._searchable_locations_loaded) .then(c3nav._searchable_locations_loaded)
.catch(() => { .catch((err) => {
console.error(err);
if (c3nav._searchable_locations_timer === null) { if (c3nav._searchable_locations_timer === null) {
c3nav._searchable_locations_timer = window.setTimeout(c3nav.load_searchable_locations, c3nav.init_completed ? 300000 : 15000); c3nav._searchable_locations_timer = window.setTimeout(c3nav.load_searchable_locations, c3nav.init_completed ? 300000 : 15000);
} }
}); });
}, },
_load_indicator_timer: null,
load_load_indicator_data: function () {
c3nav._load_indicator_timer = null;
c3nav_api.get('map/load')
.then(data => {
if (data) {
c3nav._location_load_groups = data;
}
if (c3nav._load_indicator_timer === null) {
c3nav._load_indicator_timer = window.setTimeout(c3nav.load_load_indicator_data, c3nav._load_indicator_interval);
}
c3nav.update_load_data();
})
.catch(err => {
console.error(err);
});
},
_sort_labels: function (a, b) { _sort_labels: function (a, b) {
let result = (a[0].effective_label_settings.min_zoom || -10) - (b[0].effective_label_settings.min_zoom || -10); let result = (a[0].effective_label_settings.min_zoom || -10) - (b[0].effective_label_settings.min_zoom || -10);
if (result === 0) { if (result === 0) {
@ -160,12 +180,15 @@ c3nav = {
}, },
_last_time_searchable_locations_loaded: null, _last_time_searchable_locations_loaded: null,
_searchable_locations_interval: 120000, _searchable_locations_interval: 120000,
_load_indicator_interval: 10000, // TODO: set to a sensible number
loadIndicatorLocations: [],
_searchable_locations_loaded: function (data) { _searchable_locations_loaded: function (data) {
c3nav._last_time_searchable_locations_loaded = Date.now(); c3nav._last_time_searchable_locations_loaded = Date.now();
if (data !== undefined) { if (data !== undefined) {
const locations = []; const locations = [];
const locations_by_id = {}; const locations_by_id = {};
const labels = {}; const labels = {};
const loadIndicatorLocations = [];
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const location = data[i]; const location = data[i];
location.elem = c3nav._build_location_html(location); location.elem = c3nav._build_location_html(location);
@ -178,6 +201,15 @@ c3nav = {
if (!(location.point[0] in labels)) labels[location.point[0]] = []; if (!(location.point[0] in labels)) labels[location.point[0]] = [];
labels[location.point[0]].push([location, c3nav._build_location_label(location)]); labels[location.point[0]].push([location, c3nav._build_location_label(location)]);
} }
if (location.point && location.load_group_display) {
loadIndicatorLocations.push({
level: location.point[0],
coords: location.point.slice(1),
load_id: location.load_group_display,
});
}
} }
for (const level_id in labels) { for (const level_id in labels) {
labels[level_id].sort(c3nav._sort_labels); labels[level_id].sort(c3nav._sort_labels);
@ -185,6 +217,8 @@ c3nav = {
c3nav.locations = locations; c3nav.locations = locations;
c3nav.locations_by_id = locations_by_id; c3nav.locations_by_id = locations_by_id;
c3nav.labels = labels; c3nav.labels = labels;
c3nav.loadIndicatorLocations = loadIndicatorLocations;
c3nav.update_load_data();
} else { } else {
// 304, nothing to do! // 304, nothing to do!
} }
@ -195,6 +229,52 @@ c3nav = {
c3nav._searchable_locations_timer = window.setTimeout(c3nav.load_searchable_locations, c3nav._searchable_locations_interval); c3nav._searchable_locations_timer = window.setTimeout(c3nav.load_searchable_locations, c3nav._searchable_locations_interval);
} }
}, },
_loadIndicatorLabels: {},
_location_load_groups: {},
update_load_data: function() {
c3nav._loadIndicatorLabels = {};
for (const location of c3nav.loadIndicatorLocations) {
const load = c3nav._location_load_groups[location.load_id];
if (typeof load != "number") continue;
const load_pct = Math.round(load * 100);
const html = $(`<div class="location-load-info"><div class="load-indicator" style="--location-load-value: ${load_pct}%;"></div>`);
const marker = L.marker(L.GeoJSON.coordsToLatLng(location.coords), {
icon: L.divIcon({
html: html[0].outerHTML,
iconSize: null,
className: ''
}),
interactive: false,
});
let levelLabels = c3nav._loadIndicatorLabels[location.level];
if (!levelLabels) {
levelLabels = c3nav._loadIndicatorLabels[location.level] = [];
}
levelLabels.push(marker);
}
c3nav._update_loadinfo_labels();
},
_update_loadinfo_labels: function () {
if (!c3nav._loadIndicatorLayer) return;
c3nav._loadIndicatorLayer.clearLayers();
if (!c3nav._loadIndicatorControl.enabled) return;
const labels = c3nav._loadIndicatorLabels[c3nav.current_level()] ?? [];
const bounds = c3nav.map.getBounds().pad(0.15);
if (!labels) return;
for (const label of labels) {
if (bounds.contains(label.getLatLng())) {
c3nav._loadIndicatorLayer._maybeAddLayerToRBush(label);
}
}
},
continue_init: function () { continue_init: function () {
c3nav.init_map(); c3nav.init_map();
@ -228,6 +308,7 @@ c3nav = {
c3nav.update_map_state(true); c3nav.update_map_state(true);
} }
c3nav.update_location_labels(); c3nav.update_location_labels();
c3nav._update_loadinfo_labels();
} }
c3nav.init_locationinputs(); c3nav.init_locationinputs();
@ -277,7 +358,8 @@ c3nav = {
window.setTimeout(c3nav.test_location, 1000); window.setTimeout(c3nav.test_location, 1000);
c3nav._set_user_location(data.location); c3nav._set_user_location(data.location);
}) })
.catch(() => { .catch(err => {
console.error(err);
window.setTimeout(c3nav.test_location, 1000); window.setTimeout(c3nav.test_location, 1000);
c3nav._set_user_location(null); c3nav._set_user_location(null);
}); });
@ -398,7 +480,7 @@ c3nav = {
}, },
update_location_labels: function () { update_location_labels: function () {
if (!c3nav._labelControl.labelsActive) return; if (!c3nav._labelControl.enabled) return;
c3nav._labelLayer.clearLayers(); c3nav._labelLayer.clearLayers();
const labels = c3nav.labels[c3nav.current_level()]; const labels = c3nav.labels[c3nav.current_level()];
const bounds = c3nav.map.getBounds().pad(0.15); const bounds = c3nav.map.getBounds().pad(0.15);
@ -1040,6 +1122,8 @@ c3nav = {
for (let i = 0; i < new_text.length; i++) { for (let i = 0; i < new_text.length; i++) {
new_text[i] = new_text[i].trim(); new_text[i] = new_text[i].trim();
} }
const html = $('<div class="location-label-text">').append($('<span>').html('&#8239;' + new_text.join('&#8239;<br>&#8239;') + '&#8239;')); const html = $('<div class="location-label-text">').append($('<span>').html('&#8239;' + new_text.join('&#8239;<br>&#8239;') + '&#8239;'));
html.css('font-size', location.effective_label_settings.font_size + 'px'); html.css('font-size', location.effective_label_settings.font_size + 'px');
return L.marker(L.GeoJSON.coordsToLatLng(location.point.slice(1)), { return L.marker(L.GeoJSON.coordsToLatLng(location.point.slice(1)), {
@ -1486,7 +1570,8 @@ c3nav = {
c3nav._questsLayers = {}; c3nav._questsLayers = {};
c3nav._firstRouteLevel = null; c3nav._firstRouteLevel = null;
c3nav._labelLayer = L.LayerGroup.collision({margin: 5}).addTo(c3nav.map); c3nav._labelLayer = L.LayerGroup.collision({margin: 5}).addTo(c3nav.map);
for (i = c3nav.levels.length - 1; i >= 0; i--) { c3nav._loadIndicatorLayer = L.LayerGroup.collision({margin: 5}).addTo(c3nav.map);
for (let i = c3nav.levels.length - 1; i >= 0; i--) {
const level = c3nav.levels[i]; const level = c3nav.levels[i];
const layerGroup = c3nav._levelControl.addLevel(level[0], level[2]); const layerGroup = c3nav._levelControl.addLevel(level[0], level[2]);
c3nav._detailLayers[level[0]] = L.layerGroup().addTo(layerGroup); c3nav._detailLayers[level[0]] = L.layerGroup().addTo(layerGroup);
@ -1499,12 +1584,48 @@ c3nav = {
c3nav._levelControl.finalize(); c3nav._levelControl.finalize();
c3nav._levelControl.setLevel(c3nav.initial_level); c3nav._levelControl.setLevel(c3nav.initial_level);
c3nav._labelControl = new LabelControl().addTo(c3nav.map); c3nav._labelControl = new ToggleControl({
storageId: 'labels',
enabledIcon: c3nav._map_material_icon('label'),
disabledIcon: c3nav._map_material_icon('label_off'),
onEnable: () => {
c3nav._labelLayer.addTo(c3nav.map);
c3nav.update_location_labels();
},
onDisable: () => {
c3nav._labelLayer.clearLayers();
c3nav._labelLayer.remove();
},
}).addTo(c3nav.map);
c3nav._loadIndicatorControl = new ToggleControl({
storageId: 'load_indicator',
enabledIcon: c3nav._map_material_icon('bar_chart'),
disabledIcon: c3nav._map_material_icon('bar_chart_off'),
onEnable: () => {
c3nav._update_loadinfo_labels();
},
onDisable: () => {
c3nav._update_loadinfo_labels();
},
}).addTo(c3nav.map);
// setup grid control // setup grid control
if ($map.is('[data-grid]')) { if ($map.is('[data-grid]')) {
c3nav._gridLayer = new L.SquareGridLayer(JSON.parse($map.attr('data-grid'))); c3nav._gridLayer = new L.SquareGridLayer(JSON.parse($map.attr('data-grid')));
c3nav._gridControl = new SquareGridControl().addTo(c3nav.map); c3nav._gridControl = new ToggleControl({
storageId: 'grid',
enabledIcon: c3nav._map_material_icon('grid_on'),
disabledIcon: c3nav._map_material_icon('grid_off'),
onEnable: () => {
console.log('grid enable');
c3nav._gridLayer.addTo(c3nav.map);
},
onDisable: () => {
console.log('grid disable');
c3nav._gridLayer.remove();
},
}).addTo(c3nav.map);
} }
if (Object.values(c3nav.themes).length > 1) { if (Object.values(c3nav.themes).length > 1) {
new ThemeControl().addTo(c3nav.map); new ThemeControl().addTo(c3nav.map);
@ -1519,6 +1640,7 @@ c3nav = {
c3nav._update_overlays(); c3nav._update_overlays();
c3nav._update_quests(); c3nav._update_quests();
c3nav._update_loadinfo_labels();
c3nav.map.on('click', c3nav._click_anywhere); c3nav.map.on('click', c3nav._click_anywhere);
@ -1636,10 +1758,12 @@ c3nav = {
_map_moved: function () { _map_moved: function () {
c3nav.update_map_state(); c3nav.update_map_state();
c3nav.update_location_labels(); c3nav.update_location_labels();
c3nav._update_loadinfo_labels();
}, },
_map_zoomed: function () { _map_zoomed: function () {
c3nav.update_map_state(); c3nav.update_map_state();
c3nav.update_location_labels(); c3nav.update_location_labels();
c3nav._update_loadinfo_labels();
}, },
icons: {}, icons: {},
create_icons: function () { create_icons: function () {
@ -1852,7 +1976,8 @@ c3nav = {
c3nav._fetch_updates_timer = null; c3nav._fetch_updates_timer = null;
c3nav_api.get('updates/fetch') c3nav_api.get('updates/fetch')
.then(c3nav._fetch_updates_callback) .then(c3nav._fetch_updates_callback)
.catch(() => { .catch(err => {
console.error(err);
c3nav._fetch_updates_failure_count++; c3nav._fetch_updates_failure_count++;
const waittime = Math.min(5 + c3nav._fetch_updates_failure_count * 5, 120); const waittime = Math.min(5 + c3nav._fetch_updates_failure_count * 5, 120);
c3nav.schedule_fetch_updates(waittime * 1000); c3nav.schedule_fetch_updates(waittime * 1000);
@ -2033,7 +2158,8 @@ c3nav = {
ibeacon_peers: ibeacon_peers, ibeacon_peers: ibeacon_peers,
}) })
.then(data => c3nav._set_user_location(data.location)) .then(data => c3nav._set_user_location(data.location))
.catch(() => { .catch(err => {
console.error(err);
c3nav._set_user_location(null); c3nav._set_user_location(null);
c3nav._last_scan = Date.now() + 20000 c3nav._last_scan = Date.now() + 20000
}); });
@ -2287,6 +2413,7 @@ LevelControl = L.Control.extend({
this.setLevel(e.target.level); this.setLevel(e.target.level);
c3nav.update_map_state(); c3nav.update_map_state();
c3nav.update_location_labels(); c3nav.update_location_labels();
c3nav._update_loadinfo_labels();
}, },
finalize: function () { finalize: function () {
@ -2326,107 +2453,77 @@ UserLocationControl = L.Control.extend({
} }
}); });
ToggleControl = L.Control.extend({
LabelControl = L.Control.extend({
options: { options: {
position: 'bottomright', position: 'bottomright',
addClasses: '' addClasses: '',
initialOn: false,
storageId: null,
enabledIcon: null,
disabledIcon: null,
onEnable: null,
onDisable: null,
}, },
onAdd: function () { onAdd: function () {
this._container = L.DomUtil.create('div', 'leaflet-control-labels leaflet-bar ' + this.options.addClasses); this.toggle = this.toggle.bind(this);
this.toggleOn = this.toggleOn.bind(this);
this.toggleOff = this.toggleOff.bind(this);
this._container = L.DomUtil.create('div', 'leaflet-control-toggle leaflet-bar ' + this.options.addClasses);
this._button = L.DomUtil.create('a', 'material-symbols', this._container); this._button = L.DomUtil.create('a', 'material-symbols', this._container);
$(this._button).click(this.toggleLabels).dblclick(function (e) { $(this._button).click(this.toggle).dblclick(function (e) {
e.stopPropagation(); e.stopPropagation();
}); });
this._button.innerText = c3nav._map_material_icon('label'); this._button.innerText = this.options.enabledIcon;
this._button.href = '#'; this._button.href = '#';
this._button.classList.toggle('control-disabled', false); this._button.classList.toggle('control-disabled', false);
this.labelsActive = true; let initialOn = this.options.initialOn;
if (localStorageWrapper.getItem('hideLabels')) { if (this.options.storageId) {
this.hideLabels(); initialOn = !localStorageWrapper.getItem(`c3nav.toggle-control.${this.options.storageId}.hide`);
} }
window.setTimeout(() => {
if (initialOn) {
this.toggleOn();
} else {
this.toggleOff();
}
}, 1);
return this._container; return this._container;
}, },
toggleLabels: function (e) {
toggle: function (e) {
if (e) e.preventDefault(); if (e) e.preventDefault();
if (c3nav._labelControl.labelsActive) { if (this.enabled) {
c3nav._labelControl.hideLabels(); this.toggleOff();
} else { } else {
c3nav._labelControl.showLabels(); this.toggleOn();
} }
}, },
showLabels: function () { toggleOn: function () {
if (this.labelsActive) return; if (this.enabled === true) return;
c3nav._labelLayer.addTo(c3nav.map); this.enabled = true;
this._button.innerText = c3nav._map_material_icon('label'); if (this.options.onEnable) {
this.options.onEnable();
}
this._button.innerText = this.options.enabledIcon;
this._button.classList.toggle('control-disabled', false); this._button.classList.toggle('control-disabled', false);
this.labelsActive = true; localStorageWrapper.removeItem(`c3nav.toggle-control.${this.options.storageId}.hide`);
localStorageWrapper.removeItem('hideLabels');
c3nav.update_location_labels();
}, },
hideLabels: function () { toggleOff: function () {
if (!this.labelsActive) return; if (this.enabled === false) return;
c3nav._labelLayer.clearLayers(); this.enabled = false;
c3nav._labelLayer.remove(); if (this.options.onDisable) {
this._button.innerText = c3nav._map_material_icon('label_outline'); this.options.onDisable();
this._button.classList.toggle('control-disabled', true);
this.labelsActive = false;
localStorageWrapper.setItem('hideLabels', true);
} }
}); this._button.innerText = this.options.disabledIcon;
SquareGridControl = L.Control.extend({
options: {
position: 'bottomright',
addClasses: ''
},
onAdd: function () {
this._container = L.DomUtil.create('div', 'leaflet-control-grid-layer leaflet-bar ' + this.options.addClasses);
this._button = L.DomUtil.create('a', 'material-symbols', this._container);
$(this._button).click(this.toggleGrid).dblclick(function (e) {
e.stopPropagation();
});
this._button.innerText = c3nav._map_material_icon('grid_off');
this._button.href = '#';
this._button.classList.toggle('control-disabled', true); this._button.classList.toggle('control-disabled', true);
this.gridActive = false; localStorageWrapper.setItem(`c3nav.toggle-control.${this.options.storageId}.hide`, '1');
if (localStorageWrapper.getItem('showGrid')) {
this.showGrid();
}
return this._container;
},
toggleGrid: function (e) {
if (e) e.preventDefault();
if (c3nav._gridControl.gridActive) {
c3nav._gridControl.hideGrid();
} else {
c3nav._gridControl.showGrid();
}
},
showGrid: function () {
if (this.gridActive) return;
c3nav._gridLayer.addTo(c3nav.map);
this._button.innerText = c3nav._map_material_icon('grid_on');
this._button.classList.toggle('control-disabled', false);
this.gridActive = true;
localStorageWrapper.setItem('showGrid', true);
},
hideGrid: function () {
if (!this.gridActive) return;
c3nav._gridLayer.remove();
this._button.innerText = c3nav._map_material_icon('grid_off');
this._button.classList.toggle('control-disabled', true);
this.gridActive = false;
localStorageWrapper.removeItem('showGrid');
} }
}); });
@ -2502,7 +2599,9 @@ QuestsControl = L.Control.extend({
}); });
} }
}) })
.catch(); .catch(err => {
console.error(err);
});
}, },
hideQuests: function () { hideQuests: function () {