positionsControl

This commit is contained in:
Laura Klünder 2024-12-29 18:54:46 +01:00
parent 1cbada095e
commit 25e8ed6464
4 changed files with 86 additions and 16 deletions

View file

@ -275,6 +275,18 @@ def location_by_slug_geometry(request, location_slug: NonEmptyStr):
) )
@map_api_router.get('/positions/my/', summary="all moving position coordinates",
description="get current coordinates of all moving positions owned be the current users",
response={200: list[AnyPositionStatusSchema], **API404.dict(), **auth_responses})
@api_stats('get_positions')
def get_my_positions(request):
# no caching for obvious reasons!
return [
position.serialize_position(request=request)
for position in Position.objects.filter(owner=request.user)
]
@map_api_router.get('/positions/{position_id}/', summary="moving position coordinates", @map_api_router.get('/positions/{position_id}/', summary="moving position coordinates",
description="get current coordinates of a moving position / dynamic location", description="get current coordinates of a moving position / dynamic location",
response={200: AnyPositionStatusSchema, **API404.dict(), **auth_responses}) response={200: AnyPositionStatusSchema, **API404.dict(), **auth_responses})
@ -295,18 +307,6 @@ def get_position_by_id(request, position_id: AnyPositionID):
return location.serialize_position(request=request) return location.serialize_position(request=request)
@map_api_router.get('/positions/my/', summary="all moving position coordinates",
description="get current coordinates of all moving positions owned be the current users",
response={200: list[AnyPositionStatusSchema], **API404.dict(), **auth_responses})
@api_stats('get_position')
def get_my_positions(request, position_id: AnyPositionID):
# no caching for obvious reasons!
return [
position.serialize_position(request=request)
for position in Position.objects.filter(owner=request.user)
]
class UpdatePositionSchema(BaseSchema): class UpdatePositionSchema(BaseSchema):
coordinates_id: Union[ coordinates_id: Union[
Annotated[CustomLocationID, APIField(title="set coordinates")], Annotated[CustomLocationID, APIField(title="set coordinates")],

View file

@ -635,9 +635,12 @@ class Position(CustomLocationProxyMixin, models.Model):
return { return {
'id': 'm:%s' % self.secret, 'id': 'm:%s' % self.secret,
'slug': 'm:%s' % self.secret, 'slug': 'm:%s' % self.secret,
'effective_slug': 'm:%s' % self.secret,
'available': False, 'available': False,
'icon': 'my_location', 'icon': 'my_location',
'effective_icon': 'my_location',
'title': self.name, 'title': self.name,
'short_name': self.short_name,
'subtitle': _('currently unavailable'), 'subtitle': _('currently unavailable'),
} }
# todo: is this good? # todo: is this good?
@ -647,8 +650,10 @@ class Position(CustomLocationProxyMixin, models.Model):
'available': True, 'available': True,
'id': 'm:%s' % self.secret, 'id': 'm:%s' % self.secret,
'slug': 'm:%s' % self.secret, 'slug': 'm:%s' % self.secret,
'effective_slug': 'm:%s' % self.secret,
'icon': 'my_location', 'icon': 'my_location',
'title': self.name, 'title': self.name,
'short_name': self.short_name,
'subtitle': '%s, %s, %s' % ( 'subtitle': '%s, %s, %s' % (
_('Position'), _('Position'),
result['title'], result['title'],

View file

@ -1933,7 +1933,7 @@ blink {
} }
} }
.symbol-icon { .symbol-icon, .text-icon {
--icon-color: var(--color-primary); --icon-color: var(--color-primary);
> span { > span {
display: inline-block; display: inline-block;
@ -1942,7 +1942,6 @@ blink {
line-height: 30px; line-height: 30px;
text-align: center; text-align: center;
font-size: 22px; font-size: 22px;
font-family: 'Material Symbols Outlined';
background-color: white; background-color: white;
color: var(--icon-color); color: var(--icon-color);
border-radius: 100%; border-radius: 100%;
@ -1953,8 +1952,11 @@ blink {
cursor: default; cursor: default;
} }
&.symbol-icon {
font-family: 'Material Symbols Outlined';
}
&.symbol-icon-interactive { &.symbol-icon-interactive, &.text-icon-interactive {
> span { > span {
cursor: pointer; cursor: pointer;

View file

@ -1765,6 +1765,7 @@ c3nav = {
c3nav._userLocationLayers = {}; c3nav._userLocationLayers = {};
c3nav._overlayLayers = {}; c3nav._overlayLayers = {};
c3nav._questsLayers = {}; c3nav._questsLayers = {};
c3nav._positionsLayers = {};
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);
c3nav._loadIndicatorLayer = L.LayerGroup.collision({margin: 5}).addTo(c3nav.map); c3nav._loadIndicatorLayer = L.LayerGroup.collision({margin: 5}).addTo(c3nav.map);
@ -1791,6 +1792,7 @@ c3nav = {
showCoverageOnHover: false, showCoverageOnHover: false,
iconCreateFunction: makeClusterIconCreate('var(--color-primary)'), iconCreateFunction: makeClusterIconCreate('var(--color-primary)'),
}).addTo(layerGroup); }).addTo(layerGroup);
c3nav._positionsLayers[level[0]] = L.layerGroup().addTo(layerGroup);
} }
c3nav._levelControl.finalize(); c3nav._levelControl.finalize();
c3nav._levelControl.setLevel(c3nav.initial_level); c3nav._levelControl.setLevel(c3nav.initial_level);
@ -1849,6 +1851,7 @@ c3nav = {
c3nav._update_overlays(); c3nav._update_overlays();
c3nav._update_quests(); c3nav._update_quests();
c3nav._update_positions();
c3nav._update_loadinfo_labels(); c3nav._update_loadinfo_labels();
c3nav.map.on('click', c3nav._click_anywhere); c3nav.map.on('click', c3nav._click_anywhere);
@ -2278,6 +2281,18 @@ c3nav = {
} }
}, },
_update_positions: function () {
if (!c3nav.map) return;
if (c3nav._positionsControl) {
if (!c3nav.user_data.has_positions) {
c3nav.map.removeControl(c3nav._positionsControl);
c3nav._positionsControl = null;
}
} else {
if (c3nav.user_data.has_positions) c3nav._positionsControl = (new PositionsControl()).addTo(c3nav.map);
}
},
_hasLocationPermission: undefined, _hasLocationPermission: undefined,
hasLocationPermission: function (nocache) { hasLocationPermission: function (nocache) {
if (c3nav._hasLocationPermission === undefined || (nocache !== undefined && nocache === true)) { if (c3nav._hasLocationPermission === undefined || (nocache !== undefined && nocache === true)) {
@ -2996,7 +3011,7 @@ QuestsControl = ExpandingControl.extend({
this.reloadQuests().catch(err => console.error(err)); this.reloadQuests().catch(err => console.error(err));
}, },
reloadQuests: async function (force = false) { reloadQuests: async function (force = false) {
const activeQuests = this._activeQuests; const activeQuests = this._activeQuests;
const removed = this._loadedQuests.difference(activeQuests); const removed = this._loadedQuests.difference(activeQuests);
const added = force ? activeQuests : activeQuests.difference(this._loadedQuests); const added = force ? activeQuests : activeQuests.difference(this._loadedQuests);
@ -3059,6 +3074,54 @@ QuestsControl = ExpandingControl.extend({
}, },
}); });
PositionsControl = ToggleControl.extend({
options: {
position: 'topright',
addClasses: 'leaflet-control-positions',
enabledIcon: c3nav._map_material_icon('location_on'),
disabledIcon: c3nav._map_material_icon('location_off'),
storageId: 'positions',
onEnable: () => {
c3nav._positionsControl.reloadPositions().catch(err => console.error(err));
},
},
reloadPositions: async function () {
console.log("abc");
for (const level_id in c3nav._positionsLayers) {
c3nav._positionsLayers[level_id].clearLayers();
}
const data = await c3nav_api.get(`map/positions/my/`);
for (const position of data) {
if (!position.available) continue;
L.geoJson(position.geometry, {
pointToLayer: (geom, latlng) => {
const span = document.createElement('span');
span.innerText = position.short_name;
return L.marker(latlng, {
icon: L.divIcon({
className: 'text-icon symbol-icon-interactive',
html: span,
iconSize: [24, 24],
iconAnchor: [12, 12],
})
});
}
})
.addTo(c3nav._positionsLayers[position.level])
.bindPopup(() => {
const span = document.createElement('span');
span.innerText = position.name;
return span;
}, {
className: 'data-overlay-popup'
})
}
},
});
LegendControl = ExpandingControl.extend({ LegendControl = ExpandingControl.extend({
options: { options: {
position: 'topright', position: 'topright',