From 6acd163eb621ff63d718af5fd26d6f038764b29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Tue, 24 Dec 2024 20:04:49 +0100 Subject: [PATCH] show quests on map --- src/c3nav/mapdata/api/updates.py | 4 ++ src/c3nav/mapdata/quests.py | 2 +- src/c3nav/mapdata/utils/user.py | 9 +-- src/c3nav/site/static/site/js/c3nav.js | 76 ++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 5 deletions(-) diff --git a/src/c3nav/mapdata/api/updates.py b/src/c3nav/mapdata/api/updates.py index a3f02a87..ee0b1374 100644 --- a/src/c3nav/mapdata/api/updates.py +++ b/src/c3nav/mapdata/api/updates.py @@ -10,6 +10,7 @@ from c3nav.api.auth import auth_responses from c3nav.api.schema import BaseSchema from c3nav.api.utils import NonEmptyStr from c3nav.mapdata.models import MapUpdate +from c3nav.mapdata.schemas.models import DataOverlaySchema from c3nav.mapdata.utils.cache.stats import increment_cache_key from c3nav.mapdata.utils.user import get_user_data from c3nav.mapdata.views import set_tile_access_cookie @@ -18,6 +19,7 @@ updates_api_router = APIRouter(tags=["updates"]) class UserDataSchema(BaseSchema): + # use in more places logged_in: bool = APIField( title="logged in", description="whether a user is logged in", @@ -57,6 +59,8 @@ class UserDataSchema(BaseSchema): description="IDs of access restrictions that this user (even if maybe not signed in) has access to", example=[2, 5], ) + overlays: list[DataOverlaySchema] + quests: bool class FetchUpdatesResponseSchema(BaseSchema): diff --git a/src/c3nav/mapdata/quests.py b/src/c3nav/mapdata/quests.py index c92b09a7..1d88570c 100644 --- a/src/c3nav/mapdata/quests.py +++ b/src/c3nav/mapdata/quests.py @@ -89,7 +89,7 @@ class RangingBeaconAltitudeQuest(Quest): @classmethod def _qs_for_request(cls, request): - return RangingBeacon.qs_for_request(request).select_related('space').filter(altitude_quest=True)[:10] + return RangingBeacon.qs_for_request(request).select_related('space').filter(altitude_quest=True) class QuestSchema(BaseSchema): diff --git a/src/c3nav/mapdata/utils/user.py b/src/c3nav/mapdata/utils/user.py index 05ccaa51..c5b20ae8 100644 --- a/src/c3nav/mapdata/utils/user.py +++ b/src/c3nav/mapdata/utils/user.py @@ -33,14 +33,15 @@ def get_user_data(request): if request.user.is_authenticated: result['title'] = request.user.username - + # todo: cache this result.update({ 'overlays': [ DataOverlaySchema.model_validate(overlay).model_dump() - for overlay - in DataOverlay.qs_for_request(request) - ] + for overlay in DataOverlay.qs_for_request(request) + ], + 'quests': bool(request.user.is_superuser or request.user_permissions.quests), }) + return result diff --git a/src/c3nav/site/static/site/js/c3nav.js b/src/c3nav/site/static/site/js/c3nav.js index 0535c3fa..71f59554 100644 --- a/src/c3nav/site/static/site/js/c3nav.js +++ b/src/c3nav/site/static/site/js/c3nav.js @@ -1482,6 +1482,7 @@ c3nav = { c3nav._routeLayerBounds = {}; c3nav._userLocationLayers = {}; c3nav._overlayLayers = {}; + c3nav._questsLayers = {}; c3nav._firstRouteLevel = null; c3nav._labelLayer = L.LayerGroup.collision({margin: 5}).addTo(c3nav.map); for (i = c3nav.levels.length - 1; i >= 0; i--) { @@ -1492,6 +1493,7 @@ c3nav = { c3nav._routeLayers[level[0]] = L.layerGroup().addTo(layerGroup); c3nav._userLocationLayers[level[0]] = L.layerGroup().addTo(layerGroup); c3nav._overlayLayers[level[0]] = L.layerGroup().addTo(layerGroup); + c3nav._questsLayers[level[0]] = L.layerGroup().addTo(layerGroup); } c3nav._levelControl.finalize(); c3nav._levelControl.setLevel(c3nav.initial_level); @@ -1515,6 +1517,7 @@ c3nav = { }).addTo(c3nav.map); c3nav._update_overlays(); + c3nav._update_quests(); c3nav.map.on('click', c3nav._click_anywhere); @@ -1892,6 +1895,7 @@ c3nav = { c3nav_api.authenticate(); c3nav.user_data = data; c3nav._update_overlays(); + c3nav._update_quests(); var $user = $('header #user'); $user.find('span').text(data.title); $user.find('small').text(data.subtitle || ''); @@ -1919,6 +1923,15 @@ c3nav = { c3nav._overlayControl = control.addTo(c3nav.map); } }, + _update_quests: function () { + if (!c3nav.map) return; + console.log(c3nav.user_data); + if (c3nav._questsControl) { + if (!c3nav.user_data.quests) c3nav.map.removeControl(c3nav._questsControl); + } else { + if (c3nav.user_data.quests) c3nav._questsControl = (new QuestsControl()).addTo(c3nav.map); + } + }, _hasLocationPermission: undefined, hasLocationPermission: function (nocache) { @@ -2438,6 +2451,69 @@ ThemeControl = L.Control.extend({ }) +QuestsControl = L.Control.extend({ + options: { + position: 'topright', + addClasses: '' + }, + + onAdd: function () { + this._container = L.DomUtil.create('div', 'leaflet-control-quests leaflet-bar ' + this.options.addClasses); + this._button = L.DomUtil.create('a', 'material-symbols', this._container); + $(this._button).click(this.toggleQuests).dblclick(function (e) { + e.stopPropagation(); + }); + this._button.innerText = c3nav._map_material_icon('editor_choice'); + this._button.href = '#'; + this._button.classList.toggle('control-disabled', false); + this.questsActive = false; + if (localStorageWrapper.getItem('showQuests')) { + this.showQuests(); + } + return this._container; + }, + + toggleQuests: function (e) { + if (e) e.preventDefault(); + if (c3nav._questsControl.questsActive) { + c3nav._questsControl.hideQuests(); + } else { + c3nav._questsControl.showQuests(); + } + }, + + showQuests: function () { + if (this.questsActive) return; + this._button.innerText = c3nav._map_material_icon('editor_choice'); + this._button.classList.toggle('control-disabled', false); + this.questsActive = true; + localStorageWrapper.setItem('showQuests', true); + this.reloadQuests(); + }, + + reloadQuests: function() { + c3nav_api.get('map/quests/') + .then((data) => { + for (const quest of data) { + const layer = L.geoJson(quest.point, {}).addTo(c3nav._questsLayers[quest.level_id]); + } + }) + .catch(); + }, + + hideQuests: function () { + if (!this.questsActive) return; + for (var level_id in c3nav._questsLayers) { + c3nav._questsLayers[level_id].clearLayers() + } + this._button.innerText = c3nav._map_material_icon('editor_choice'); + this._button.classList.toggle('control-disabled', true); + this.questsActive = false; + localStorageWrapper.removeItem('hideQuests', true); + } +}); + + L.SquareGridLayer = L.Layer.extend({ initialize: function (config) { this.config = config;