Compare commits
7 Commits
990f3afa88
...
v1.14.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
3b71531de2
|
|||
|
6ef7549ea9
|
|||
|
9097c63a55
|
|||
|
ec0d5a30f9
|
|||
|
f1779131e8
|
|||
|
37cf47b3dd
|
|||
|
ff68b5addc
|
@@ -1,65 +1,10 @@
|
|||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import { htmlSafe } from '@ember/template';
|
import { htmlSafe } from '@ember/template';
|
||||||
|
import { getIcon } from '../utils/icons';
|
||||||
import arrowLeft from 'feather-icons/dist/icons/arrow-left.svg?raw';
|
|
||||||
import activity from 'feather-icons/dist/icons/activity.svg?raw';
|
|
||||||
import bookmark from 'feather-icons/dist/icons/bookmark.svg?raw';
|
|
||||||
import clock from 'feather-icons/dist/icons/clock.svg?raw';
|
|
||||||
import edit from 'feather-icons/dist/icons/edit.svg?raw';
|
|
||||||
import facebook from 'feather-icons/dist/icons/facebook.svg?raw';
|
|
||||||
import globe from 'feather-icons/dist/icons/globe.svg?raw';
|
|
||||||
import home from 'feather-icons/dist/icons/home.svg?raw';
|
|
||||||
import instagram from 'feather-icons/dist/icons/instagram.svg?raw';
|
|
||||||
import logIn from 'feather-icons/dist/icons/log-in.svg?raw';
|
|
||||||
import logOut from 'feather-icons/dist/icons/log-out.svg?raw';
|
|
||||||
import mail from 'feather-icons/dist/icons/mail.svg?raw';
|
|
||||||
import map from 'feather-icons/dist/icons/map.svg?raw';
|
|
||||||
import mapPin from 'feather-icons/dist/icons/map-pin.svg?raw';
|
|
||||||
import menu from 'feather-icons/dist/icons/menu.svg?raw';
|
|
||||||
import navigation from 'feather-icons/dist/icons/navigation.svg?raw';
|
|
||||||
import phone from 'feather-icons/dist/icons/phone.svg?raw';
|
|
||||||
import plus from 'feather-icons/dist/icons/plus.svg?raw';
|
|
||||||
import server from 'feather-icons/dist/icons/server.svg?raw';
|
|
||||||
import search from 'feather-icons/dist/icons/search.svg?raw';
|
|
||||||
import settings from 'feather-icons/dist/icons/settings.svg?raw';
|
|
||||||
import target from 'feather-icons/dist/icons/target.svg?raw';
|
|
||||||
import user from 'feather-icons/dist/icons/user.svg?raw';
|
|
||||||
import x from 'feather-icons/dist/icons/x.svg?raw';
|
|
||||||
import zap from 'feather-icons/dist/icons/zap.svg?raw';
|
|
||||||
import wikipedia from '../icons/wikipedia.svg?raw';
|
|
||||||
|
|
||||||
const ICONS = {
|
|
||||||
'arrow-left': arrowLeft,
|
|
||||||
activity,
|
|
||||||
bookmark,
|
|
||||||
clock,
|
|
||||||
edit,
|
|
||||||
facebook,
|
|
||||||
globe,
|
|
||||||
home,
|
|
||||||
instagram,
|
|
||||||
'log-in': logIn,
|
|
||||||
'log-out': logOut,
|
|
||||||
mail,
|
|
||||||
map,
|
|
||||||
'map-pin': mapPin,
|
|
||||||
menu,
|
|
||||||
navigation,
|
|
||||||
phone,
|
|
||||||
plus,
|
|
||||||
server,
|
|
||||||
search,
|
|
||||||
settings,
|
|
||||||
target,
|
|
||||||
user,
|
|
||||||
wikipedia,
|
|
||||||
x,
|
|
||||||
zap,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class IconComponent extends Component {
|
export default class IconComponent extends Component {
|
||||||
get svg() {
|
get svg() {
|
||||||
return ICONS[this.args.name];
|
return getIcon(this.args.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
get size() {
|
get size() {
|
||||||
|
|||||||
@@ -63,7 +63,10 @@ export default class MapComponent extends Component {
|
|||||||
|
|
||||||
const bookmarkStyleFunction = (feature) => {
|
const bookmarkStyleFunction = (feature) => {
|
||||||
const originalPlace = feature.get('originalPlace');
|
const originalPlace = feature.get('originalPlace');
|
||||||
let color = '#ffcc33'; // Default Yellow
|
let color =
|
||||||
|
getComputedStyle(document.documentElement)
|
||||||
|
.getPropertyValue('--default-list-color')
|
||||||
|
.trim() || '#000000'; // Fallback to black if variable is missing to make error obvious
|
||||||
|
|
||||||
if (
|
if (
|
||||||
originalPlace &&
|
originalPlace &&
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
|
import { service } from '@ember/service';
|
||||||
import { on } from '@ember/modifier';
|
import { on } from '@ember/modifier';
|
||||||
import { htmlSafe } from '@ember/template';
|
import { htmlSafe } from '@ember/template';
|
||||||
import { humanizeOsmTag } from '../utils/format-text';
|
import { humanizeOsmTag } from '../utils/format-text';
|
||||||
@@ -13,9 +14,14 @@ import { tracked } from '@glimmer/tracking';
|
|||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
|
|
||||||
export default class PlaceDetails extends Component {
|
export default class PlaceDetails extends Component {
|
||||||
|
@service storage;
|
||||||
@tracked isEditing = false;
|
@tracked isEditing = false;
|
||||||
@tracked showLists = false;
|
@tracked showLists = false;
|
||||||
|
|
||||||
|
get isSaved() {
|
||||||
|
return this.storage.isPlaceSaved(this.place.id || this.place.osmId);
|
||||||
|
}
|
||||||
|
|
||||||
get place() {
|
get place() {
|
||||||
return this.args.place || {};
|
return this.args.place || {};
|
||||||
}
|
}
|
||||||
@@ -38,7 +44,7 @@ export default class PlaceDetails extends Component {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
startEditing() {
|
startEditing() {
|
||||||
if (!this.place.createdAt) return; // Only allow editing saved places
|
if (!this.isSaved) return; // Only allow editing saved places
|
||||||
this.isEditing = true;
|
this.isEditing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,29 +281,26 @@ export default class PlaceDetails extends Component {
|
|||||||
<div class="save-button-wrapper">
|
<div class="save-button-wrapper">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class={{if
|
class={{if this.isSaved "btn btn-secondary" "btn btn-outline"}}
|
||||||
this.place.createdAt
|
|
||||||
"btn btn-secondary"
|
|
||||||
"btn btn-outline"
|
|
||||||
}}
|
|
||||||
{{on "click" this.toggleLists}}
|
{{on "click" this.toggleLists}}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
@name="bookmark"
|
@name="bookmark"
|
||||||
@color={{if this.place.createdAt "currentColor" "#007bff"}}
|
@color={{if this.isSaved "currentColor" "#007bff"}}
|
||||||
/>
|
/>
|
||||||
{{if this.place.createdAt "Saved" "Save"}}
|
{{if this.isSaved "Saved" "Save"}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{{#if this.showLists}}
|
{{#if this.showLists}}
|
||||||
<PlaceListsManager
|
<PlaceListsManager
|
||||||
@place={{this.saveablePlace}}
|
@place={{this.saveablePlace}}
|
||||||
@onClose={{this.closeLists}}
|
@onClose={{this.closeLists}}
|
||||||
|
@isSaved={{this.isSaved}}
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if this.place.createdAt}}
|
{{#if this.isSaved}}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-outline"
|
class="btn btn-outline"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import { service } from '@ember/service';
|
import { service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
import { on } from '@ember/modifier';
|
import { on } from '@ember/modifier';
|
||||||
import { fn } from '@ember/helper';
|
import { fn } from '@ember/helper';
|
||||||
import { htmlSafe } from '@ember/template';
|
import { htmlSafe } from '@ember/template';
|
||||||
@@ -8,12 +9,15 @@ import onClickOutside from '../modifiers/on-click-outside';
|
|||||||
|
|
||||||
export default class PlaceListsManager extends Component {
|
export default class PlaceListsManager extends Component {
|
||||||
@service storage;
|
@service storage;
|
||||||
|
@service router;
|
||||||
|
@tracked _forceClear = false;
|
||||||
|
|
||||||
get isSaved() {
|
get isSaved() {
|
||||||
return !!this.args.place.createdAt;
|
return this.args.isSaved;
|
||||||
}
|
}
|
||||||
|
|
||||||
get placeListIds() {
|
get placeListIds() {
|
||||||
|
if (this._forceClear) return [];
|
||||||
return this.args.place._listIds || [];
|
return this.args.place._listIds || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +34,35 @@ export default class PlaceListsManager extends Component {
|
|||||||
@action
|
@action
|
||||||
async toggleSaved() {
|
async toggleSaved() {
|
||||||
if (this.isSaved) {
|
if (this.isSaved) {
|
||||||
|
const { osmId, osmType } = this.args.place;
|
||||||
|
|
||||||
await this.storage.removePlace(this.args.place);
|
await this.storage.removePlace(this.args.place);
|
||||||
|
|
||||||
|
// Clean up the local object reference immediately to prevent UI flicker
|
||||||
|
// or stale state if the transition is delayed/cancelled.
|
||||||
|
if (this.args.place) {
|
||||||
|
this.args.place.id = null;
|
||||||
|
this.args.place.createdAt = null;
|
||||||
|
this.args.place._listIds = [];
|
||||||
|
this._forceClear = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transition immediately to the canonical state
|
||||||
|
if (osmId && osmType) {
|
||||||
|
// Create a transient copy that looks like a fresh OSM result
|
||||||
|
const rawPlace = { ...this.args.place };
|
||||||
|
delete rawPlace.id;
|
||||||
|
delete rawPlace.createdAt;
|
||||||
|
delete rawPlace._listIds;
|
||||||
|
|
||||||
|
// Transition to the place route using the raw object
|
||||||
|
// This updates the URL to 'osm:...' and renders immediately
|
||||||
|
this.router.transitionTo('place', rawPlace);
|
||||||
|
} else {
|
||||||
|
// Custom place deleted -> go home
|
||||||
|
this.router.transitionTo('index');
|
||||||
|
}
|
||||||
|
|
||||||
if (this.args.onClose) this.args.onClose();
|
if (this.args.onClose) this.args.onClose();
|
||||||
} else {
|
} else {
|
||||||
await this.storage.storePlace(this.args.place);
|
await this.storage.storePlace(this.args.place);
|
||||||
@@ -71,7 +103,8 @@ export default class PlaceListsManager extends Component {
|
|||||||
checked={{this.isSaved}}
|
checked={{this.isSaved}}
|
||||||
{{on "change" this.toggleSaved}}
|
{{on "change" this.toggleSaved}}
|
||||||
/>
|
/>
|
||||||
<span class="list-name">Saved</span>
|
<span class="list-color"></span>
|
||||||
|
<span class="list-name">Saved places</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ export default class StorageService extends Service {
|
|||||||
this.loadLists();
|
this.loadLists();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.rs.on('not-connected', () => {
|
||||||
|
this.loadLists();
|
||||||
|
});
|
||||||
|
|
||||||
this.rs.on('disconnected', () => {
|
this.rs.on('disconnected', () => {
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.userAddress = null;
|
this.userAddress = null;
|
||||||
@@ -137,6 +141,16 @@ export default class StorageService extends Service {
|
|||||||
|
|
||||||
const lists = await this.places.lists.getAll();
|
const lists = await this.places.lists.getAll();
|
||||||
this.lists = lists || [];
|
this.lists = lists || [];
|
||||||
|
|
||||||
|
// Decorate with hardcoded icons for default lists (in-memory only)
|
||||||
|
this.lists.forEach((list) => {
|
||||||
|
if (list.id === 'to-go') {
|
||||||
|
list.icon = 'bookmark';
|
||||||
|
} else if (list.id === 'to-do') {
|
||||||
|
list.icon = 'check-square';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.refreshPlaceListAssociations();
|
this.refreshPlaceListAssociations();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to load lists:', e);
|
console.error('Failed to load lists:', e);
|
||||||
@@ -282,11 +296,22 @@ export default class StorageService extends Service {
|
|||||||
let place = this.savedPlaces.find((p) => p.id && String(p.id) === strId);
|
let place = this.savedPlaces.find((p) => p.id && String(p.id) === strId);
|
||||||
if (place) return place;
|
if (place) return place;
|
||||||
|
|
||||||
|
// Check placesInView as fallback
|
||||||
|
place = this.placesInView.find((p) => p.id && String(p.id) === strId);
|
||||||
|
if (place) return place;
|
||||||
|
|
||||||
// Then search by OSM ID
|
// Then search by OSM ID
|
||||||
place = this.savedPlaces.find((p) => p.osmId && String(p.osmId) === strId);
|
place = this.savedPlaces.find((p) => p.osmId && String(p.osmId) === strId);
|
||||||
|
if (place) return place;
|
||||||
|
|
||||||
|
place = this.placesInView.find((p) => p.osmId && String(p.osmId) === strId);
|
||||||
return place;
|
return place;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isPlaceSaved(id) {
|
||||||
|
return !!this.findPlaceById(id);
|
||||||
|
}
|
||||||
|
|
||||||
async storePlace(placeData) {
|
async storePlace(placeData) {
|
||||||
const savedPlace = await this.places.store(placeData);
|
const savedPlace = await this.places.store(placeData);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
/* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */
|
/* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--default-list-color: #fc3;
|
||||||
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -1031,6 +1035,7 @@ button.create-place {
|
|||||||
.place-lists-manager .list-color {
|
.place-lists-manager .list-color {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
|
background-color: var(--default-list-color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border: 1px solid rgb(0 0 0 / 10%);
|
border: 1px solid rgb(0 0 0 / 10%);
|
||||||
|
|||||||
61
app/utils/icons.js
Normal file
61
app/utils/icons.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import arrowLeft from 'feather-icons/dist/icons/arrow-left.svg?raw';
|
||||||
|
import activity from 'feather-icons/dist/icons/activity.svg?raw';
|
||||||
|
import bookmark from 'feather-icons/dist/icons/bookmark.svg?raw';
|
||||||
|
import checkSquare from 'feather-icons/dist/icons/check-square.svg?raw';
|
||||||
|
import clock from 'feather-icons/dist/icons/clock.svg?raw';
|
||||||
|
import edit from 'feather-icons/dist/icons/edit.svg?raw';
|
||||||
|
import facebook from 'feather-icons/dist/icons/facebook.svg?raw';
|
||||||
|
import globe from 'feather-icons/dist/icons/globe.svg?raw';
|
||||||
|
import home from 'feather-icons/dist/icons/home.svg?raw';
|
||||||
|
import instagram from 'feather-icons/dist/icons/instagram.svg?raw';
|
||||||
|
import logIn from 'feather-icons/dist/icons/log-in.svg?raw';
|
||||||
|
import logOut from 'feather-icons/dist/icons/log-out.svg?raw';
|
||||||
|
import mail from 'feather-icons/dist/icons/mail.svg?raw';
|
||||||
|
import map from 'feather-icons/dist/icons/map.svg?raw';
|
||||||
|
import mapPin from 'feather-icons/dist/icons/map-pin.svg?raw';
|
||||||
|
import menu from 'feather-icons/dist/icons/menu.svg?raw';
|
||||||
|
import navigation from 'feather-icons/dist/icons/navigation.svg?raw';
|
||||||
|
import phone from 'feather-icons/dist/icons/phone.svg?raw';
|
||||||
|
import plus from 'feather-icons/dist/icons/plus.svg?raw';
|
||||||
|
import server from 'feather-icons/dist/icons/server.svg?raw';
|
||||||
|
import search from 'feather-icons/dist/icons/search.svg?raw';
|
||||||
|
import settings from 'feather-icons/dist/icons/settings.svg?raw';
|
||||||
|
import target from 'feather-icons/dist/icons/target.svg?raw';
|
||||||
|
import user from 'feather-icons/dist/icons/user.svg?raw';
|
||||||
|
import x from 'feather-icons/dist/icons/x.svg?raw';
|
||||||
|
import zap from 'feather-icons/dist/icons/zap.svg?raw';
|
||||||
|
import wikipedia from '../icons/wikipedia.svg?raw';
|
||||||
|
|
||||||
|
const ICONS = {
|
||||||
|
'arrow-left': arrowLeft,
|
||||||
|
activity,
|
||||||
|
bookmark,
|
||||||
|
'check-square': checkSquare,
|
||||||
|
clock,
|
||||||
|
edit,
|
||||||
|
facebook,
|
||||||
|
globe,
|
||||||
|
home,
|
||||||
|
instagram,
|
||||||
|
'log-in': logIn,
|
||||||
|
'log-out': logOut,
|
||||||
|
mail,
|
||||||
|
map,
|
||||||
|
'map-pin': mapPin,
|
||||||
|
menu,
|
||||||
|
navigation,
|
||||||
|
phone,
|
||||||
|
plus,
|
||||||
|
server,
|
||||||
|
search,
|
||||||
|
settings,
|
||||||
|
target,
|
||||||
|
user,
|
||||||
|
wikipedia,
|
||||||
|
x,
|
||||||
|
zap,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getIcon(name) {
|
||||||
|
return ICONS[name];
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "marco",
|
"name": "marco",
|
||||||
"version": "1.13.3",
|
"version": "1.14.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Unhosted maps app",
|
"description": "Unhosted maps app",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"@embroider/vite": "^1.5.0",
|
"@embroider/vite": "^1.5.0",
|
||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^9.39.2",
|
||||||
"@glimmer/component": "^2.0.0",
|
"@glimmer/component": "^2.0.0",
|
||||||
"@remotestorage/module-places": "1.x",
|
"@remotestorage/module-places": "~1.2.1",
|
||||||
"@rollup/plugin-babel": "^6.1.0",
|
"@rollup/plugin-babel": "^6.1.0",
|
||||||
"@warp-drive/core": "~5.8.0",
|
"@warp-drive/core": "~5.8.0",
|
||||||
"@warp-drive/ember": "~5.8.0",
|
"@warp-drive/ember": "~5.8.0",
|
||||||
|
|||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -55,8 +55,8 @@ importers:
|
|||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
'@remotestorage/module-places':
|
'@remotestorage/module-places':
|
||||||
specifier: 1.x
|
specifier: ~1.2.1
|
||||||
version: 1.0.0
|
version: 1.2.1
|
||||||
'@rollup/plugin-babel':
|
'@rollup/plugin-babel':
|
||||||
specifier: ^6.1.0
|
specifier: ^6.1.0
|
||||||
version: 6.1.0(@babel/core@7.28.6)(rollup@4.55.1)
|
version: 6.1.0(@babel/core@7.28.6)(rollup@4.55.1)
|
||||||
@@ -1380,8 +1380,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-4rdu8GPY9TeQwsYp5D2My74dC3dSVS3tghAvisG80ybK4lqa0gvlrglaSTBxogJbxqHRw/NjI/liEtb3+SD+Bw==}
|
resolution: {integrity: sha512-4rdu8GPY9TeQwsYp5D2My74dC3dSVS3tghAvisG80ybK4lqa0gvlrglaSTBxogJbxqHRw/NjI/liEtb3+SD+Bw==}
|
||||||
engines: {node: '>=18.12'}
|
engines: {node: '>=18.12'}
|
||||||
|
|
||||||
'@remotestorage/module-places@1.0.0':
|
'@remotestorage/module-places@1.2.1':
|
||||||
resolution: {integrity: sha512-vaqJeTw658gjPyLz70Mq2AbGfDZ66O2mpDFME+gtaGFYl2+UvrvRLCrXWHYuyTE21f3TJdegeXM6C5nZMxLv9A==}
|
resolution: {integrity: sha512-hNRuhGoG8RS+cieVvDVzXWBEuNPfyeFirhgNH3z1WoKw9ngHdPY6V0sT0vKbsxB8xaODReZfo2ZKHLTmdFunlw==}
|
||||||
|
|
||||||
'@rollup/plugin-babel@6.1.0':
|
'@rollup/plugin-babel@6.1.0':
|
||||||
resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==}
|
resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==}
|
||||||
@@ -7002,7 +7002,7 @@ snapshots:
|
|||||||
'@pnpm/error': 1000.0.5
|
'@pnpm/error': 1000.0.5
|
||||||
find-up: 5.0.0
|
find-up: 5.0.0
|
||||||
|
|
||||||
'@remotestorage/module-places@1.0.0':
|
'@remotestorage/module-places@1.2.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
latlon-geohash: 2.0.0
|
latlon-geohash: 2.0.0
|
||||||
ulid: 3.0.2
|
ulid: 3.0.2
|
||||||
|
|||||||
1
release/assets/main-BT0n1kYB.css
Normal file
1
release/assets/main-BT0n1kYB.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
release/assets/main-GynTgP18.js
Normal file
2
release/assets/main-GynTgP18.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -39,8 +39,8 @@
|
|||||||
<meta name="msapplication-TileColor" content="#F6E9A6">
|
<meta name="msapplication-TileColor" content="#F6E9A6">
|
||||||
<meta name="msapplication-TileImage" content="/icons/icon-144.png">
|
<meta name="msapplication-TileImage" content="/icons/icon-144.png">
|
||||||
|
|
||||||
<script type="module" crossorigin src="/assets/main-gjk9d6Ld.js"></script>
|
<script type="module" crossorigin src="/assets/main-GynTgP18.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/main-DAo4Q0R2.css">
|
<link rel="stylesheet" crossorigin href="/assets/main-BT0n1kYB.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ class MockStorageService extends Service {
|
|||||||
findPlaceById() {
|
findPlaceById() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
isPlaceSaved() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
loadPlacesInBounds() {
|
loadPlacesInBounds() {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ module('Acceptance | search', function (hooks) {
|
|||||||
findPlaceById() {
|
findPlaceById() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
isPlaceSaved() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
rs = {
|
rs = {
|
||||||
on: () => {},
|
on: () => {},
|
||||||
};
|
};
|
||||||
@@ -85,6 +88,9 @@ module('Acceptance | search', function (hooks) {
|
|||||||
findPlaceById() {
|
findPlaceById() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
isPlaceSaved() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
rs = {
|
rs = {
|
||||||
on: () => {},
|
on: () => {},
|
||||||
};
|
};
|
||||||
@@ -130,6 +136,9 @@ module('Acceptance | search', function (hooks) {
|
|||||||
if (id === '999') return this.savedPlaces[0];
|
if (id === '999') return this.savedPlaces[0];
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
isPlaceSaved(id) {
|
||||||
|
return !!this.findPlaceById(id);
|
||||||
|
}
|
||||||
rs = {
|
rs = {
|
||||||
on: () => {},
|
on: () => {},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,10 +9,18 @@ module('Integration | Component | place-details', function (hooks) {
|
|||||||
|
|
||||||
class StorageService extends Service {
|
class StorageService extends Service {
|
||||||
lists = [
|
lists = [
|
||||||
{ id: 'to-go', title: 'Want to go', color: '#ff00ff' },
|
{ id: 'to-go', title: 'Want to go', color: '#2e9e4f' },
|
||||||
{ id: 'to-do', title: 'To do', color: '#008000' },
|
{ id: 'to-do', title: 'To do', color: '#2a7fff' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
isPlaceSaved() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
findPlaceById() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async storePlace(place) {
|
async storePlace(place) {
|
||||||
return { ...place, id: '123', createdAt: new Date().toISOString() };
|
return { ...place, id: '123', createdAt: new Date().toISOString() };
|
||||||
}
|
}
|
||||||
@@ -28,6 +36,12 @@ module('Integration | Component | place-details', function (hooks) {
|
|||||||
|
|
||||||
hooks.beforeEach(function () {
|
hooks.beforeEach(function () {
|
||||||
this.owner.register('service:storage', StorageService);
|
this.owner.register('service:storage', StorageService);
|
||||||
|
|
||||||
|
// Mock Router for all tests
|
||||||
|
class MockRouter extends Service {
|
||||||
|
transitionTo() {}
|
||||||
|
}
|
||||||
|
this.owner.register('service:router', MockRouter);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it formats coordinates correctly', async function (assert) {
|
test('it formats coordinates correctly', async function (assert) {
|
||||||
@@ -95,6 +109,12 @@ module('Integration | Component | place-details', function (hooks) {
|
|||||||
storedPlace = place;
|
storedPlace = place;
|
||||||
return { ...place, id: 'new-id', createdAt: new Date().toISOString() };
|
return { ...place, id: 'new-id', createdAt: new Date().toISOString() };
|
||||||
}
|
}
|
||||||
|
isPlaceSaved() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
findPlaceById() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.owner.register('service:storage', MockStorage);
|
this.owner.register('service:storage', MockStorage);
|
||||||
|
|
||||||
@@ -126,12 +146,15 @@ module('Integration | Component | place-details', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it handles removing a saved place via master toggle', async function (assert) {
|
test('it handles removing a saved place via master toggle', async function (assert) {
|
||||||
let removedPlace = null;
|
let removedPlaceId = null;
|
||||||
|
|
||||||
class MockStorage extends Service {
|
class MockStorage extends Service {
|
||||||
lists = [];
|
lists = [];
|
||||||
async removePlace(place) {
|
async removePlace(place) {
|
||||||
removedPlace = place;
|
removedPlaceId = place.id;
|
||||||
|
}
|
||||||
|
isPlaceSaved() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.owner.register('service:storage', MockStorage);
|
this.owner.register('service:storage', MockStorage);
|
||||||
@@ -160,7 +183,9 @@ module('Integration | Component | place-details', function (hooks) {
|
|||||||
// Click it to remove
|
// Click it to remove
|
||||||
await click(masterToggle);
|
await click(masterToggle);
|
||||||
|
|
||||||
assert.strictEqual(removedPlace.id, 'saved-id', 'removePlace was called');
|
assert.strictEqual(removedPlaceId, 'saved-id', 'removePlace was called');
|
||||||
|
|
||||||
|
assert.deepEqual(place._listIds, [], '_listIds was cleared on the object');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it adds place to a list', async function (assert) {
|
test('it adds place to a list', async function (assert) {
|
||||||
@@ -175,6 +200,9 @@ module('Integration | Component | place-details', function (hooks) {
|
|||||||
listId = id;
|
listId = id;
|
||||||
shouldAdd = add;
|
shouldAdd = add;
|
||||||
}
|
}
|
||||||
|
isPlaceSaved() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.owner.register('service:storage', MockStorage);
|
this.owner.register('service:storage', MockStorage);
|
||||||
|
|
||||||
@@ -202,4 +230,29 @@ module('Integration | Component | place-details', function (hooks) {
|
|||||||
assert.strictEqual(placeArg.id, 'p1');
|
assert.strictEqual(placeArg.id, 'p1');
|
||||||
assert.true(shouldAdd);
|
assert.true(shouldAdd);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it respects storage service state over stale place object', async function (assert) {
|
||||||
|
class MockStorage extends Service {
|
||||||
|
lists = [];
|
||||||
|
isPlaceSaved() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
findPlaceById() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.owner.register('service:storage', MockStorage);
|
||||||
|
|
||||||
|
const place = {
|
||||||
|
id: 'stale-id',
|
||||||
|
title: 'Stale Place',
|
||||||
|
createdAt: '2023-01-01', // Looks saved
|
||||||
|
};
|
||||||
|
|
||||||
|
await render(<template><PlaceDetails @place={{place}} /></template>);
|
||||||
|
|
||||||
|
// Button should say "Save", not "Saved" because isPlaceSaved returns false
|
||||||
|
assert.dom('.actions button').hasText('Save');
|
||||||
|
assert.dom('.actions button').doesNotHaveClass('btn-secondary');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user