WIP Add places to lists
This commit is contained in:
@@ -60,9 +60,27 @@ export default class MapComponent extends Component {
|
||||
|
||||
// Create a vector source and layer for bookmarks
|
||||
this.bookmarkSource = new VectorSource();
|
||||
const bookmarkLayer = new VectorLayer({
|
||||
source: this.bookmarkSource,
|
||||
style: [
|
||||
|
||||
const bookmarkStyleFunction = (feature) => {
|
||||
const originalPlace = feature.get('originalPlace');
|
||||
let color = '#ffcc33'; // Default Yellow
|
||||
|
||||
if (
|
||||
originalPlace &&
|
||||
originalPlace._listIds &&
|
||||
originalPlace._listIds.length > 0
|
||||
) {
|
||||
// Find the first list color
|
||||
// We need access to storage.lists.
|
||||
// Since this is inside setupMap, 'this' refers to the component instance.
|
||||
const firstListId = originalPlace._listIds[0];
|
||||
const list = this.storage.lists.find((l) => l.id === firstListId);
|
||||
if (list && list.color) {
|
||||
color = list.color;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
new Style({
|
||||
image: new Circle({
|
||||
radius: 10,
|
||||
@@ -73,14 +91,19 @@ export default class MapComponent extends Component {
|
||||
new Style({
|
||||
image: new Circle({
|
||||
radius: 9,
|
||||
fill: new Fill({ color: '#ffcc33' }), // Gold/Yellow
|
||||
fill: new Fill({ color: color }),
|
||||
stroke: new Stroke({
|
||||
color: '#fff',
|
||||
width: 2,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
];
|
||||
};
|
||||
|
||||
const bookmarkLayer = new VectorLayer({
|
||||
source: this.bookmarkSource,
|
||||
style: bookmarkStyleFunction,
|
||||
zIndex: 10, // Ensure it sits above the map tiles
|
||||
});
|
||||
|
||||
|
||||
@@ -4,20 +4,31 @@ import { on } from '@ember/modifier';
|
||||
import { htmlSafe } from '@ember/template';
|
||||
import { humanizeOsmTag } from '../utils/format-text';
|
||||
import { getLocalizedName, getPlaceType } from '../utils/osm';
|
||||
import { mapToStorageSchema } from '../utils/place-mapping';
|
||||
import { getSocialInfo } from '../utils/social-links';
|
||||
import Icon from '../components/icon';
|
||||
import PlaceEditForm from './place-edit-form';
|
||||
import PlaceListsManager from './place-lists-manager';
|
||||
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class PlaceDetails extends Component {
|
||||
@tracked isEditing = false;
|
||||
@tracked showLists = false;
|
||||
|
||||
get place() {
|
||||
return this.args.place || {};
|
||||
}
|
||||
|
||||
get saveablePlace() {
|
||||
if (this.place.createdAt) {
|
||||
return this.place;
|
||||
}
|
||||
|
||||
return mapToStorageSchema(this.place);
|
||||
}
|
||||
|
||||
get tags() {
|
||||
return this.place.osmTags || {};
|
||||
}
|
||||
@@ -37,6 +48,16 @@ export default class PlaceDetails extends Component {
|
||||
this.isEditing = false;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleLists() {
|
||||
this.showLists = !this.showLists;
|
||||
}
|
||||
|
||||
@action
|
||||
closeLists() {
|
||||
this.showLists = false;
|
||||
}
|
||||
|
||||
@action
|
||||
async saveChanges(changes) {
|
||||
if (this.args.onSave) {
|
||||
@@ -247,21 +268,30 @@ export default class PlaceDetails extends Component {
|
||||
{{/if}}
|
||||
|
||||
<div class="actions">
|
||||
<button
|
||||
type="button"
|
||||
class={{if
|
||||
this.place.createdAt
|
||||
"btn btn-secondary"
|
||||
"btn btn-outline"
|
||||
}}
|
||||
{{on "click" (fn @onToggleSave this.place)}}
|
||||
>
|
||||
<Icon
|
||||
@name="bookmark"
|
||||
@color={{if this.place.createdAt "currentColor" "#007bff"}}
|
||||
/>
|
||||
{{if this.place.createdAt "Saved" "Save"}}
|
||||
</button>
|
||||
<div class="save-button-wrapper">
|
||||
<button
|
||||
type="button"
|
||||
class={{if
|
||||
this.place.createdAt
|
||||
"btn btn-secondary"
|
||||
"btn btn-outline"
|
||||
}}
|
||||
{{on "click" this.toggleLists}}
|
||||
>
|
||||
<Icon
|
||||
@name="bookmark"
|
||||
@color={{if this.place.createdAt "currentColor" "#007bff"}}
|
||||
/>
|
||||
{{if this.place.createdAt "Saved" "Save"}}
|
||||
</button>
|
||||
|
||||
{{#if this.showLists}}
|
||||
<PlaceListsManager
|
||||
@place={{this.saveablePlace}}
|
||||
@onClose={{this.closeLists}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.place.createdAt}}
|
||||
<button
|
||||
|
||||
95
app/components/place-lists-manager.gjs
Normal file
95
app/components/place-lists-manager.gjs
Normal file
@@ -0,0 +1,95 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { on } from '@ember/modifier';
|
||||
import { fn } from '@ember/helper';
|
||||
import Icon from './icon';
|
||||
|
||||
export default class PlaceListsManager extends Component {
|
||||
@service storage;
|
||||
|
||||
get isSaved() {
|
||||
return !!this.args.place.createdAt;
|
||||
}
|
||||
|
||||
get placeListIds() {
|
||||
return this.args.place._listIds || [];
|
||||
}
|
||||
|
||||
@action
|
||||
isInList(list) {
|
||||
if (!this.placeListIds) return false;
|
||||
return this.placeListIds.includes(list.id);
|
||||
}
|
||||
|
||||
@action
|
||||
async toggleSaved() {
|
||||
if (this.isSaved) {
|
||||
if (confirm(`Remove "${this.args.place.title}" from saved places?`)) {
|
||||
await this.storage.removePlace(this.args.place);
|
||||
if (this.args.onClose) this.args.onClose();
|
||||
}
|
||||
} else {
|
||||
await this.storage.storePlace(this.args.place);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
async toggleList(list) {
|
||||
const isMember = this.placeListIds.includes(list.id);
|
||||
const shouldAdd = !isMember;
|
||||
|
||||
if (shouldAdd && !this.isSaved) {
|
||||
// Auto-save if adding to list
|
||||
await this.storage.storePlace(this.args.place);
|
||||
}
|
||||
|
||||
try {
|
||||
// Toggle membership
|
||||
// We must pass the SAVED place (with ID) to the toggle function
|
||||
// If we just saved it above, the args.place might still be the old object reference unless storage updates it in-place?
|
||||
// StorageService.storePlace returns the new object.
|
||||
// But togglePlaceList handles saving internally if ID is missing.
|
||||
|
||||
// Let's rely on storage.togglePlaceList to handle the "save if needed" part.
|
||||
await this.storage.togglePlaceList(this.args.place, list.id, shouldAdd);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert('Failed to update list: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="place-lists-manager">
|
||||
<div class="list-item master-toggle">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={{this.isSaved}}
|
||||
{{on "change" this.toggleSaved}}
|
||||
/>
|
||||
<span class="list-name">Saved</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="lists-container">
|
||||
{{#each this.storage.lists as |list|}}
|
||||
<div class="list-item">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={{this.isInList list}}
|
||||
{{on "change" (fn this.toggleList list)}}
|
||||
disabled={{unless this.isSaved true}}
|
||||
/>
|
||||
<span class="list-color" style="background-color: {{list.color}}"></span>
|
||||
<span class="list-name">{{list.title}}</span>
|
||||
</label>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
Reference in New Issue
Block a user