Fix sidebar content switching

This commit is contained in:
2026-01-16 12:08:56 +07:00
parent 12253a41b9
commit 83461b0748
3 changed files with 81 additions and 88 deletions

View File

@@ -74,8 +74,9 @@ export default class MapComponent extends Component {
this.loadBookmarks(); this.loadBookmarks();
}); });
this.storage.places.on('change', (event) => { // Listen to changes in the /places/ scope
// Ideally we would only update the changed one, but refreshing all is safer for now this.storage.rs.scope('/places/').on('change', (event) => {
console.log('RemoteStorage change detected:', event);
this.loadBookmarks(); this.loadBookmarks();
}); });
}); });
@@ -106,49 +107,56 @@ export default class MapComponent extends Component {
} }
handleMapClick = async (event) => { handleMapClick = async (event) => {
// 0. Handle closing sidebar if open and clicked on empty map area // Check if user clicked on a rendered feature (POI or Bookmark) FIRST
const features = this.mapInstance.getFeaturesAtPixel(event.pixel);
let clickedBookmark = null;
let selectedFeatureName = null;
let selectedFeatureType = null;
if (features && features.length > 0) {
const bookmarkFeature = features.find(f => f.get('isBookmark'));
if (bookmarkFeature) {
clickedBookmark = bookmarkFeature.get('originalPlace');
}
// Also get visual props for standard map click logic later
const props = features[0].getProperties();
if (props.name) {
selectedFeatureName = props.name;
selectedFeatureType = props.class || props.subclass;
}
}
// Special handling when sidebar is OPEN
if (this.args.isSidebarOpen) { if (this.args.isSidebarOpen) {
// We can just trigger the outside click and return. // If it's a bookmark, we allow "switching" to it even if sidebar is open
// However, if the user clicked on a feature, maybe they want to switch selection? if (clickedBookmark) {
// The requirement says "when clicking on the map while the sidebar is open, it should close the sidebar instead of executing the normal map click logic" console.log("Clicked bookmark while sidebar open (switching):", clickedBookmark);
// This implies strict closing behavior. if (this.args.onPlacesFound) {
this.args.onPlacesFound([], clickedBookmark);
}
return;
}
// Otherwise (empty map or non-bookmark feature), close the sidebar
if (this.args.onOutsideClick) { if (this.args.onOutsideClick) {
this.args.onOutsideClick(); this.args.onOutsideClick();
} }
return; return;
} }
// Normal behavior (sidebar is closed)
if (clickedBookmark) {
console.log("Clicked bookmark:", clickedBookmark);
if (this.args.onPlacesFound) {
this.args.onPlacesFound([], clickedBookmark);
}
return;
}
const coords = toLonLat(event.coordinate); const coords = toLonLat(event.coordinate);
const [lon, lat] = coords; const [lon, lat] = coords;
// 1. Check if user clicked on a rendered feature (POI or Bookmark) // ... continue with normal OSM fetch logic ...
const features = this.mapInstance.getFeaturesAtPixel(event.pixel);
let selectedFeatureName = null;
let selectedFeatureType = null;
let clickedBookmark = null;
if (features && features.length > 0) {
// Prioritize bookmarks if clicked
const bookmarkFeature = features.find(f => f.get('isBookmark'));
if (bookmarkFeature) {
clickedBookmark = bookmarkFeature.get('originalPlace');
console.log("Clicked bookmark:", clickedBookmark);
// Notify parent to show bookmark details
if (this.args.onPlacesFound) {
// We pass it as a "selectedPlace" but with an empty list of nearby items since it's a specific bookmark
this.args.onPlacesFound([], clickedBookmark);
}
return; // Stop processing to avoid fetching OSM data for a known bookmark
}
const props = features[0].getProperties();
if (props.name) {
selectedFeatureName = props.name;
selectedFeatureType = props.class || props.subclass; // e.g., 'cafe'
console.log(`Clicked visual feature: "${selectedFeatureName}" (${selectedFeatureType})`);
}
}
// 2. Fetch authoritative data via Overpass // 2. Fetch authoritative data via Overpass
try { try {

View File

@@ -8,31 +8,26 @@ import or from 'ember-truth-helpers/helpers/or';
export default class PlacesSidebar extends Component { export default class PlacesSidebar extends Component {
@service storage; @service storage;
@tracked selectedPlace = null;
constructor() { @action
super(...arguments); selectPlace(place) {
// If a specific place was passed in (pre-selected by map), show it immediately if (this.args.onSelect) {
if (this.args.initialPlace) { this.args.onSelect(place);
this.selectedPlace = this.args.initialPlace;
} }
} }
@action
selectPlace(place) {
this.selectedPlace = place;
}
@action @action
clearSelection() { clearSelection() {
this.selectedPlace = null; // Going "back" clears the specific selection but keeps the sidebar open (showing list)
// If we were initialized with a single place (direct click), if (this.args.onSelect) {
// going "back" might mean closing or showing the list if available. this.args.onSelect(null);
// Logic: if we have a list (@places), go back to list. }
// If we only had one place (@initialPlace) and no list, maybe close?
// For now, assuming @places is always passed if we want a list fallback. // Fallback logic: if no list available, close sidebar
if (!this.args.places || this.args.places.length === 0) { if (!this.args.places || this.args.places.length === 0) {
this.args.onClose(); if (this.args.onClose) {
this.args.onClose();
}
} }
} }
@@ -44,16 +39,11 @@ export default class PlacesSidebar extends Component {
// It's a saved bookmark -> Delete it // It's a saved bookmark -> Delete it
if (confirm(`Delete "${place.title}"?`)) { if (confirm(`Delete "${place.title}"?`)) {
try { try {
// We need geohash to delete.
// Existing bookmarks have it.
// If for some reason it's missing (shouldn't happen for saved items), we can't delete easily.
if (place.id && place.geohash) { if (place.id && place.geohash) {
await this.storage.places.remove(place.id, place.geohash); await this.storage.places.remove(place.id, place.geohash);
console.log('Place deleted:', place.title); console.log('Place deleted:', place.title);
// Close sidebar after delete since the item is gone from "Saved" context // Close sidebar after delete
// Or we could revert to "Save" state if we had the original POI data,
// but usually we just close or show "Nearby".
if (this.args.onClose) { if (this.args.onClose) {
this.args.onClose(); this.args.onClose();
} }
@@ -67,8 +57,6 @@ export default class PlacesSidebar extends Component {
} }
} else { } else {
// It's a fresh POI -> Save it // It's a fresh POI -> Save it
// Map Overpass POI to our Place schema
const placeData = { const placeData = {
title: place.tags.name || place.tags['name:en'] || 'Untitled Place', title: place.tags.name || place.tags['name:en'] || 'Untitled Place',
lat: place.lat, lat: place.lat,
@@ -76,23 +64,15 @@ export default class PlacesSidebar extends Component {
tags: [], tags: [],
url: place.tags.website, url: place.tags.website,
osmId: String(place.id), osmId: String(place.id),
// We rely on the module to generate ID and Geohash
}; };
try { try {
const savedPlace = await this.storage.places.store(placeData); const savedPlace = await this.storage.places.store(placeData);
console.log('Place saved:', placeData.title); console.log('Place saved:', placeData.title);
// Update the selected place in the UI to be the saved bookmark // Update selection to the new saved place object
// (so the button turns to "Saved") if (this.args.onSelect) {
// We can update the local tracked property if we are viewing a single item this.args.onSelect(savedPlace);
// or let the parent update.
// Ideally, we switch `this.selectedPlace` to the `savedPlace` returned by the store.
this.selectedPlace = savedPlace;
// Notify parent if needed (map will auto-update via events)
if (this.args.onBookmarkSaved) {
this.args.onBookmarkSaved();
} }
} catch (error) { } catch (error) {
console.error('Failed to save place:', error); console.error('Failed to save place:', error);
@@ -104,7 +84,7 @@ export default class PlacesSidebar extends Component {
<template> <template>
<div class="sidebar"> <div class="sidebar">
<div class="sidebar-header"> <div class="sidebar-header">
{{#if this.selectedPlace}} {{#if @selectedPlace}}
<button type="button" class="back-btn" {{on "click" this.clearSelection}}>←</button> <button type="button" class="back-btn" {{on "click" this.clearSelection}}>←</button>
<h2>Details</h2> <h2>Details</h2>
{{else}} {{else}}
@@ -114,35 +94,34 @@ export default class PlacesSidebar extends Component {
</div> </div>
<div class="sidebar-content"> <div class="sidebar-content">
{{#if this.selectedPlace}} {{#if @selectedPlace}}
<div class="place-details"> <div class="place-details">
<h3>{{or this.selectedPlace.title this.selectedPlace.tags.name this.selectedPlace.tags.name:en "Unnamed Place"}}</h3> <h3>{{or @selectedPlace.title @selectedPlace.tags.name @selectedPlace.tags.name:en "Unnamed Place"}}</h3>
<p class="place-meta"> <p class="place-meta">
{{#if this.selectedPlace.tags.amenity}} {{#if @selectedPlace.tags.amenity}}
{{or this.selectedPlace.tags.amenity this.selectedPlace.tags.shop this.selectedPlace.tags.tourism}} {{or @selectedPlace.tags.amenity @selectedPlace.tags.shop @selectedPlace.tags.tourism}}
{{else}} {{else}}
{{!-- If it is a bookmark, it might just have an array of tags or description --}} {{@selectedPlace.description}}
{{this.selectedPlace.description}}
{{/if}} {{/if}}
</p> </p>
{{#if (or this.selectedPlace.url this.selectedPlace.tags.website)}} {{#if (or @selectedPlace.url @selectedPlace.tags.website)}}
<p><a href={{or this.selectedPlace.url this.selectedPlace.tags.website}} target="_blank" rel="noopener noreferrer">Website</a></p> <p><a href={{or @selectedPlace.url @selectedPlace.tags.website}} target="_blank" rel="noopener noreferrer">Website</a></p>
{{/if}} {{/if}}
{{#if this.selectedPlace.tags.opening_hours}} {{#if @selectedPlace.tags.opening_hours}}
<p><strong>Open:</strong> {{this.selectedPlace.tags.opening_hours}}</p> <p><strong>Open:</strong> {{@selectedPlace.tags.opening_hours}}</p>
{{/if}} {{/if}}
<div class="actions"> <div class="actions">
<button type="button" class={{if this.selectedPlace.createdAt "btn-secondary" "btn-primary"}} {{on "click" (fn this.toggleSave this.selectedPlace)}}> <button type="button" class={{if @selectedPlace.createdAt "btn-secondary" "btn-primary"}} {{on "click" (fn this.toggleSave @selectedPlace)}}>
{{if this.selectedPlace.createdAt "Saved ✓" "Save"}} {{if @selectedPlace.createdAt "Saved ✓" "Save"}}
</button> </button>
</div> </div>
<div class="meta-info"> <div class="meta-info">
{{#if (or this.selectedPlace.osmId this.selectedPlace.id)}} {{#if (or @selectedPlace.osmId @selectedPlace.id)}}
<p><small>OSM ID: <a href="https://www.openstreetmap.org/{{if this.selectedPlace.type this.selectedPlace.type 'node'}}/{{or this.selectedPlace.osmId this.selectedPlace.id}}" target="_blank" rel="noopener noreferrer">{{or this.selectedPlace.osmId this.selectedPlace.id}}</a></small></p> <p><small>OSM ID: <a href="https://www.openstreetmap.org/{{if @selectedPlace.type @selectedPlace.type 'node'}}/{{or @selectedPlace.osmId @selectedPlace.id}}" target="_blank" rel="noopener noreferrer">{{or @selectedPlace.osmId @selectedPlace.id}}</a></small></p>
{{/if}} {{/if}}
</div> </div>
</div> </div>

View File

@@ -27,6 +27,11 @@ export default class ApplicationComponent extends Component {
this.isSidebarOpen = true; this.isSidebarOpen = true;
} }
@action
selectPlace(place) {
this.selectedPlace = place;
}
@action @action
closeSidebar() { closeSidebar() {
this.isSidebarOpen = false; this.isSidebarOpen = false;
@@ -46,7 +51,8 @@ export default class ApplicationComponent extends Component {
{{#if this.isSidebarOpen}} {{#if this.isSidebarOpen}}
<PlacesSidebar <PlacesSidebar
@places={{this.nearbyPlaces}} @places={{this.nearbyPlaces}}
@initialPlace={{this.selectedPlace}} @selectedPlace={{this.selectedPlace}}
@onSelect={{this.selectPlace}}
@onClose={{this.closeSidebar}} @onClose={{this.closeSidebar}}
/> />
{{/if}} {{/if}}