Compare commits
14 Commits
0ac6db65cb
...
v1.17.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
913d5c915c
|
|||
|
89f667b17e
|
|||
|
22d4ef8d96
|
|||
|
b17793af9d
|
|||
|
dc9e0f210a
|
|||
|
2b219fe0cf
|
|||
|
9fd6c4d64d
|
|||
|
8e5b2c7439
|
|||
|
0f29430e1a
|
|||
|
0059d89cc3
|
|||
|
54e2766dc4
|
|||
|
5978f67d48
|
|||
|
d72e5f3de2
|
|||
|
582ab4f8b3
|
@@ -10,6 +10,7 @@ import CategoryChips from '#components/category-chips';
|
|||||||
|
|
||||||
export default class AppHeaderComponent extends Component {
|
export default class AppHeaderComponent extends Component {
|
||||||
@service storage;
|
@service storage;
|
||||||
|
@service settings;
|
||||||
@tracked isUserMenuOpen = false;
|
@tracked isUserMenuOpen = false;
|
||||||
@tracked searchQuery = '';
|
@tracked searchQuery = '';
|
||||||
|
|
||||||
@@ -49,9 +50,11 @@ export default class AppHeaderComponent extends Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-center {{if this.hasQuery 'searching'}}">
|
{{#if this.settings.showQuickSearchButtons}}
|
||||||
<CategoryChips @onSelect={{this.handleChipSelect}} />
|
<div class="header-center {{if this.hasQuery 'searching'}}">
|
||||||
</div>
|
<CategoryChips @onSelect={{this.handleChipSelect}} />
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="user-menu-container">
|
<div class="user-menu-container">
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ export default class AppMenuSettings extends Component {
|
|||||||
this.settings.updateMapKinetic(event.target.value === 'true');
|
this.settings.updateMapKinetic(event.target.value === 'true');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleQuickSearchButtons(event) {
|
||||||
|
this.settings.updateShowQuickSearchButtons(event.target.value === 'true');
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
updatePhotonApi(event) {
|
updatePhotonApi(event) {
|
||||||
this.settings.updatePhotonApi(event.target.value);
|
this.settings.updatePhotonApi(event.target.value);
|
||||||
@@ -36,6 +41,30 @@ export default class AppMenuSettings extends Component {
|
|||||||
|
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<section class="settings-section">
|
<section class="settings-section">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="show-quick-search">Quick search buttons visible</label>
|
||||||
|
<select
|
||||||
|
id="show-quick-search"
|
||||||
|
class="form-control"
|
||||||
|
{{on "change" this.toggleQuickSearchButtons}}
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value="true"
|
||||||
|
selected={{if this.settings.showQuickSearchButtons "selected"}}
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value="false"
|
||||||
|
selected={{unless
|
||||||
|
this.settings.showQuickSearchButtons
|
||||||
|
"selected"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="map-kinetic">Map Inertia (Kinetic Panning)</label>
|
<label for="map-kinetic">Map Inertia (Kinetic Panning)</label>
|
||||||
<select
|
<select
|
||||||
|
|||||||
@@ -42,7 +42,10 @@ export default class CategoryChipsComponent extends Component {
|
|||||||
class="category-chip"
|
class="category-chip"
|
||||||
{{on "click" (fn this.searchCategory category)}}
|
{{on "click" (fn this.searchCategory category)}}
|
||||||
aria-label={{category.label}}
|
aria-label={{category.label}}
|
||||||
disabled={{and (eq this.mapUi.loadingState.type "category") (eq this.mapUi.loadingState.value category.id)}}
|
disabled={{and
|
||||||
|
(eq this.mapUi.loadingState.type "category")
|
||||||
|
(eq this.mapUi.loadingState.value category.id)
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Icon @name={{category.icon}} @size={{16}} />
|
<Icon @name={{category.icon}} @size={{16}} />
|
||||||
<span>{{category.label}}</span>
|
<span>{{category.label}}</span>
|
||||||
|
|||||||
@@ -119,6 +119,28 @@ export default class MapComponent extends Component {
|
|||||||
|
|
||||||
const searchResultStyle = (feature) => {
|
const searchResultStyle = (feature) => {
|
||||||
const originalPlace = feature.get('originalPlace');
|
const originalPlace = feature.get('originalPlace');
|
||||||
|
|
||||||
|
// If this place is currently selected, hide the search result marker
|
||||||
|
// because the main red drop pin will be shown instead.
|
||||||
|
const selectedPlace = this.mapUi.selectedPlace;
|
||||||
|
if (selectedPlace) {
|
||||||
|
const isSameOsmId =
|
||||||
|
originalPlace.osmId &&
|
||||||
|
selectedPlace.osmId &&
|
||||||
|
originalPlace.osmId === selectedPlace.osmId;
|
||||||
|
const isSameId =
|
||||||
|
originalPlace.id &&
|
||||||
|
selectedPlace.id &&
|
||||||
|
originalPlace.id === selectedPlace.id;
|
||||||
|
const isSameCoords =
|
||||||
|
originalPlace.lat === selectedPlace.lat &&
|
||||||
|
originalPlace.lon === selectedPlace.lon;
|
||||||
|
|
||||||
|
if (isSameOsmId || isSameId || isSameCoords) {
|
||||||
|
return new Style({}); // Empty style makes it invisible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Some search results might be just the place object without separate tags
|
// Some search results might be just the place object without separate tags
|
||||||
// If it's a raw place object, it might have osmTags property.
|
// If it's a raw place object, it might have osmTags property.
|
||||||
// Or it might be the tags object itself.
|
// Or it might be the tags object itself.
|
||||||
@@ -599,6 +621,11 @@ export default class MapComponent extends Component {
|
|||||||
const selected = this.mapUi.selectedPlace;
|
const selected = this.mapUi.selectedPlace;
|
||||||
const options = this.mapUi.selectionOptions || {};
|
const options = this.mapUi.selectionOptions || {};
|
||||||
|
|
||||||
|
// Force a redraw of the search results layer so it can hide/show the selected pin
|
||||||
|
if (this.searchResultsSource) {
|
||||||
|
this.searchResultsSource.changed();
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.selectedPinOverlay || !this.selectedPinElement) return;
|
if (!this.selectedPinOverlay || !this.selectedPinElement) return;
|
||||||
|
|
||||||
// Clear any previous shape
|
// Clear any previous shape
|
||||||
|
|||||||
@@ -217,7 +217,12 @@ export default class SearchBoxComponent extends Component {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<button type="submit" class="search-submit-btn" aria-label="Search">
|
<button type="submit" class="search-submit-btn" aria-label="Search">
|
||||||
{{#if (or (eq this.mapUi.loadingState.type "text") (eq this.mapUi.loadingState.type "category"))}}
|
{{#if
|
||||||
|
(or
|
||||||
|
(eq this.mapUi.loadingState.type "text")
|
||||||
|
(eq this.mapUi.loadingState.type "category")
|
||||||
|
)
|
||||||
|
}}
|
||||||
<Icon @name="loading-ring" @size={{20}} />
|
<Icon @name="loading-ring" @size={{20}} />
|
||||||
{{else}}
|
{{else}}
|
||||||
<Icon @name="search" @size={{20}} @color="#5f6368" />
|
<Icon @name="search" @size={{20}} @color="#5f6368" />
|
||||||
|
|||||||
14
app/components/toast.gjs
Normal file
14
app/components/toast.gjs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import Component from '@glimmer/component';
|
||||||
|
import { service } from '@ember/service';
|
||||||
|
|
||||||
|
export default class ToastComponent extends Component {
|
||||||
|
@service toast;
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#if this.toast.isVisible}}
|
||||||
|
<div class="toast-notification">
|
||||||
|
{{this.toast.message}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ export default class SearchRoute extends Route {
|
|||||||
@service mapUi;
|
@service mapUi;
|
||||||
@service storage;
|
@service storage;
|
||||||
@service router;
|
@service router;
|
||||||
|
@service toast;
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
lat: { refreshModel: true },
|
lat: { refreshModel: true },
|
||||||
@@ -199,8 +200,12 @@ export default class SearchRoute extends Route {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
error() {
|
error(error, transition) {
|
||||||
this.mapUi.stopSearch();
|
this.mapUi.stopSearch();
|
||||||
return true; // Bubble error
|
this.toast.show('Search request failed. Please try again.');
|
||||||
|
if (transition) {
|
||||||
|
transition.abort();
|
||||||
|
}
|
||||||
|
return false; // Prevent bubble and stop transition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ out center;
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
console.error('Category search failed', e);
|
console.error('Category search failed', e);
|
||||||
return [];
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export default class SettingsService extends Service {
|
|||||||
@tracked overpassApi = 'https://overpass-api.de/api/interpreter';
|
@tracked overpassApi = 'https://overpass-api.de/api/interpreter';
|
||||||
@tracked mapKinetic = true;
|
@tracked mapKinetic = true;
|
||||||
@tracked photonApi = 'https://photon.komoot.io/api/';
|
@tracked photonApi = 'https://photon.komoot.io/api/';
|
||||||
|
@tracked showQuickSearchButtons = true;
|
||||||
|
|
||||||
overpassApis = [
|
overpassApis = [
|
||||||
{
|
{
|
||||||
@@ -56,6 +57,13 @@ export default class SettingsService extends Service {
|
|||||||
this.mapKinetic = savedKinetic === 'true';
|
this.mapKinetic = savedKinetic === 'true';
|
||||||
}
|
}
|
||||||
// Default is true (initialized in class field)
|
// Default is true (initialized in class field)
|
||||||
|
|
||||||
|
const savedShowQuickSearch = localStorage.getItem(
|
||||||
|
'marco:show-quick-search'
|
||||||
|
);
|
||||||
|
if (savedShowQuickSearch !== null) {
|
||||||
|
this.showQuickSearchButtons = savedShowQuickSearch === 'true';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOverpassApi(url) {
|
updateOverpassApi(url) {
|
||||||
@@ -68,6 +76,11 @@ export default class SettingsService extends Service {
|
|||||||
localStorage.setItem('marco:map-kinetic', String(enabled));
|
localStorage.setItem('marco:map-kinetic', String(enabled));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateShowQuickSearchButtons(enabled) {
|
||||||
|
this.showQuickSearchButtons = enabled;
|
||||||
|
localStorage.setItem('marco:show-quick-search', String(enabled));
|
||||||
|
}
|
||||||
|
|
||||||
updatePhotonApi(url) {
|
updatePhotonApi(url) {
|
||||||
this.photonApi = url;
|
this.photonApi = url;
|
||||||
}
|
}
|
||||||
|
|||||||
21
app/services/toast.js
Normal file
21
app/services/toast.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import Service from '@ember/service';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
|
export default class ToastService extends Service {
|
||||||
|
@tracked message = null;
|
||||||
|
@tracked isVisible = false;
|
||||||
|
timeoutId = null;
|
||||||
|
|
||||||
|
show(message, duration = 3000) {
|
||||||
|
this.message = message;
|
||||||
|
this.isVisible = true;
|
||||||
|
|
||||||
|
if (this.timeoutId) {
|
||||||
|
clearTimeout(this.timeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.timeoutId = setTimeout(() => {
|
||||||
|
this.isVisible = false;
|
||||||
|
}, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1321,3 +1321,36 @@ button.create-place {
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Toast Notification */
|
||||||
|
.toast-notification {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 2rem;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-color: rgb(51 51 51 / 85%);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
color: white;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
z-index: 9999;
|
||||||
|
box-shadow: 0 4px 12px rgb(0 0 0 / 15%);
|
||||||
|
animation: fade-in-up 0.3s ease-out forwards;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 90%;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in-up {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(-50%, 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { pageTitle } from 'ember-page-title';
|
|||||||
import Map from '#components/map';
|
import Map from '#components/map';
|
||||||
import AppHeader from '#components/app-header';
|
import AppHeader from '#components/app-header';
|
||||||
import AppMenu from '#components/app-menu/index';
|
import AppMenu from '#components/app-menu/index';
|
||||||
|
import Toast from '#components/toast';
|
||||||
import { service } from '@ember/service';
|
import { service } from '@ember/service';
|
||||||
import { tracked } from '@glimmer/tracking';
|
import { tracked } from '@glimmer/tracking';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
@@ -89,6 +90,8 @@ export default class ApplicationComponent extends Component {
|
|||||||
<AppMenu @onClose={{this.closeAppMenu}} />
|
<AppMenu @onClose={{this.closeAppMenu}} />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
<Toast />
|
||||||
|
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
</template>
|
</template>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,8 +79,8 @@ export default class PlaceTemplate extends Component {
|
|||||||
if (place === null) {
|
if (place === null) {
|
||||||
// If we have an active search context, return to it (UP navigation)
|
// If we have an active search context, return to it (UP navigation)
|
||||||
if (this.mapUi.returnToSearch && this.mapUi.currentSearch) {
|
if (this.mapUi.returnToSearch && this.mapUi.currentSearch) {
|
||||||
this.router.transitionTo('search', {
|
this.router.transitionTo('search', {
|
||||||
queryParams: this.mapUi.currentSearch
|
queryParams: this.mapUi.currentSearch,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Otherwise just close the sidebar (return to map index)
|
// Otherwise just close the sidebar (return to map index)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import loadingRing from '../icons/270-ring.svg?raw';
|
|||||||
|
|
||||||
import angelfish from '@waysidemapping/pinhead/dist/icons/angelfish.svg?raw';
|
import angelfish from '@waysidemapping/pinhead/dist/icons/angelfish.svg?raw';
|
||||||
import barbell from '@waysidemapping/pinhead/dist/icons/barbell.svg?raw';
|
import barbell from '@waysidemapping/pinhead/dist/icons/barbell.svg?raw';
|
||||||
|
import climbingWall from '@waysidemapping/pinhead/dist/icons/climbing_wall.svg?raw';
|
||||||
import banknote from '@waysidemapping/pinhead/dist/icons/banknote.svg?raw';
|
import banknote from '@waysidemapping/pinhead/dist/icons/banknote.svg?raw';
|
||||||
import badgeShieldWithFire from '@waysidemapping/pinhead/dist/icons/badge_shield_with_fire.svg?raw';
|
import badgeShieldWithFire from '@waysidemapping/pinhead/dist/icons/badge_shield_with_fire.svg?raw';
|
||||||
import beachUmbrellaInGround from '@waysidemapping/pinhead/dist/icons/beach_umbrella_in_ground.svg?raw';
|
import beachUmbrellaInGround from '@waysidemapping/pinhead/dist/icons/beach_umbrella_in_ground.svg?raw';
|
||||||
@@ -106,6 +107,7 @@ import womensAndMensRestroomSymbol from '@waysidemapping/pinhead/dist/icons/wome
|
|||||||
|
|
||||||
import wikipedia from '../icons/wikipedia.svg?raw';
|
import wikipedia from '../icons/wikipedia.svg?raw';
|
||||||
import parkingP from '@waysidemapping/pinhead/dist/icons/parking_p.svg?raw';
|
import parkingP from '@waysidemapping/pinhead/dist/icons/parking_p.svg?raw';
|
||||||
|
import car from '@waysidemapping/pinhead/dist/icons/car.svg?raw';
|
||||||
|
|
||||||
const ICONS = {
|
const ICONS = {
|
||||||
activity,
|
activity,
|
||||||
@@ -123,6 +125,7 @@ const ICONS = {
|
|||||||
camera,
|
camera,
|
||||||
'check-square': checkSquare,
|
'check-square': checkSquare,
|
||||||
'cigarette-with-smoke-curl': cigaretteWithSmokeCurl,
|
'cigarette-with-smoke-curl': cigaretteWithSmokeCurl,
|
||||||
|
climbing_wall: climbingWall,
|
||||||
'classical-building': classicalBuilding,
|
'classical-building': classicalBuilding,
|
||||||
'classical-building-with-dome-and-flag': classicalBuildingWithDomeAndFlag,
|
'classical-building-with-dome-and-flag': classicalBuildingWithDomeAndFlag,
|
||||||
'classical-building-with-flag': classicalBuildingWithFlag,
|
'classical-building-with-flag': classicalBuildingWithFlag,
|
||||||
@@ -212,6 +215,7 @@ const ICONS = {
|
|||||||
'womens-and-mens-restroom-symbol': womensAndMensRestroomSymbol,
|
'womens-and-mens-restroom-symbol': womensAndMensRestroomSymbol,
|
||||||
wikipedia,
|
wikipedia,
|
||||||
parking_p: parkingP,
|
parking_p: parkingP,
|
||||||
|
car,
|
||||||
x,
|
x,
|
||||||
zap,
|
zap,
|
||||||
'loading-ring': loadingRing,
|
'loading-ring': loadingRing,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export const POI_ICON_RULES = [
|
|||||||
{ tags: { amenity: 'police' }, icon: 'police-officer-with-stop-arm' },
|
{ tags: { amenity: 'police' }, icon: 'police-officer-with-stop-arm' },
|
||||||
{ tags: { amenity: 'toilets' }, icon: 'womens-and-mens-restroom-symbol' },
|
{ tags: { amenity: 'toilets' }, icon: 'womens-and-mens-restroom-symbol' },
|
||||||
{ tags: { amenity: 'school' }, icon: 'open-book' },
|
{ tags: { amenity: 'school' }, icon: 'open-book' },
|
||||||
|
{ tags: { amenity: 'driving_school' }, icon: 'car' },
|
||||||
|
|
||||||
{ tags: { shop: 'coffee' }, icon: 'coffee-bean' },
|
{ tags: { shop: 'coffee' }, icon: 'coffee-bean' },
|
||||||
{ tags: { shop: 'tea' }, icon: 'coffee-bean' },
|
{ tags: { shop: 'tea' }, icon: 'coffee-bean' },
|
||||||
@@ -144,6 +145,7 @@ export const POI_ICON_RULES = [
|
|||||||
{ tags: { sport: 'squash' }, icon: 'person-playing-tennis' },
|
{ tags: { sport: 'squash' }, icon: 'person-playing-tennis' },
|
||||||
{ tags: { sport: 'padel' }, icon: 'person-playing-tennis' },
|
{ tags: { sport: 'padel' }, icon: 'person-playing-tennis' },
|
||||||
{ tags: { sport: 'table_tennis' }, icon: 'table-tennis-paddle' },
|
{ tags: { sport: 'table_tennis' }, icon: 'table-tennis-paddle' },
|
||||||
|
{ tags: { sport: 'climbing' }, icon: 'climbing_wall' },
|
||||||
{ tags: { leisure: 'water_park' }, icon: 'person-swimming-in-water' },
|
{ tags: { leisure: 'water_park' }, icon: 'person-swimming-in-water' },
|
||||||
{ tags: { sport: 'swimming' }, icon: 'person-swimming-in-water' },
|
{ tags: { sport: 'swimming' }, icon: 'person-swimming-in-water' },
|
||||||
{ tags: { sport: 'golf' }, icon: 'person-swinging-golf-club' },
|
{ tags: { sport: 'golf' }, icon: 'person-swinging-golf-club' },
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "marco",
|
"name": "marco",
|
||||||
"version": "1.16.0",
|
"version": "1.17.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Unhosted maps app",
|
"description": "Unhosted maps app",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
"edition": "octane"
|
"edition": "octane"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@waysidemapping/pinhead": "^15.17.0",
|
"@waysidemapping/pinhead": "^15.20.0",
|
||||||
"ember-concurrency": "^5.2.0",
|
"ember-concurrency": "^5.2.0",
|
||||||
"ember-lifeline": "^7.0.0"
|
"ember-lifeline": "^7.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -9,8 +9,8 @@ importers:
|
|||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@waysidemapping/pinhead':
|
'@waysidemapping/pinhead':
|
||||||
specifier: ^15.17.0
|
specifier: ^15.20.0
|
||||||
version: 15.17.0
|
version: 15.20.0
|
||||||
ember-concurrency:
|
ember-concurrency:
|
||||||
specifier: ^5.2.0
|
specifier: ^5.2.0
|
||||||
version: 5.2.0(@babel/core@7.28.6)
|
version: 5.2.0(@babel/core@7.28.6)
|
||||||
@@ -1654,8 +1654,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@warp-drive/core': 5.8.1
|
'@warp-drive/core': 5.8.1
|
||||||
|
|
||||||
'@waysidemapping/pinhead@15.17.0':
|
'@waysidemapping/pinhead@15.20.0':
|
||||||
resolution: {integrity: sha512-XcL/0Ll+gkRIpXlO+skwd6USynA+mX3DNwqrWDMhgRmLP4DNRPTeaecK64BBxk1bB/F9Xi/9kgN6JA5zbdgejQ==}
|
resolution: {integrity: sha512-JD9XINaMhtEy3VEjvc+l4r1sLwbyOKoYdD2IYY2QNKP3FeeNwE/2gcUly631JH9jPymoFeOix0f3o9L/n9YDSQ==}
|
||||||
|
|
||||||
'@xmldom/xmldom@0.8.11':
|
'@xmldom/xmldom@0.8.11':
|
||||||
resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
|
resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
|
||||||
@@ -7245,7 +7245,7 @@ snapshots:
|
|||||||
- '@glint/template'
|
- '@glint/template'
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@waysidemapping/pinhead@15.17.0': {}
|
'@waysidemapping/pinhead@15.20.0': {}
|
||||||
|
|
||||||
'@xmldom/xmldom@0.8.11': {}
|
'@xmldom/xmldom@0.8.11': {}
|
||||||
|
|
||||||
|
|||||||
2
release/assets/main-B8Ckz4Ru.js
Normal file
2
release/assets/main-B8Ckz4Ru.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
1
release/assets/main-OLSOzTKA.css
Normal file
1
release/assets/main-OLSOzTKA.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
@@ -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-C4F17h3W.js"></script>
|
<script type="module" crossorigin src="/assets/main-B8Ckz4Ru.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/main-CKp1bFPU.css">
|
<link rel="stylesheet" crossorigin href="/assets/main-OLSOzTKA.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import {
|
import { visit, click, fillIn, currentURL } from '@ember/test-helpers';
|
||||||
visit,
|
|
||||||
click,
|
|
||||||
fillIn,
|
|
||||||
triggerEvent,
|
|
||||||
currentURL,
|
|
||||||
} from '@ember/test-helpers';
|
|
||||||
import { setupApplicationTest } from 'marco/tests/helpers';
|
import { setupApplicationTest } from 'marco/tests/helpers';
|
||||||
import Service from '@ember/service';
|
import Service from '@ember/service';
|
||||||
import { Promise } from 'rsvp';
|
import { Promise } from 'rsvp';
|
||||||
|
|||||||
@@ -218,4 +218,56 @@ module('Acceptance | search', function (hooks) {
|
|||||||
// Ensure it shows "Results" not "Nearby"
|
// Ensure it shows "Results" not "Nearby"
|
||||||
assert.dom('.sidebar-header h2').includesText('Results');
|
assert.dom('.sidebar-header h2').includesText('Results');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('search error handling prevents opening empty panel and shows toast', async function (assert) {
|
||||||
|
// Mock Osm Service to throw an error
|
||||||
|
class MockOsmService extends Service {
|
||||||
|
async getCategoryPois() {
|
||||||
|
throw new Error('Overpass request failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.owner.register('service:osm', MockOsmService);
|
||||||
|
|
||||||
|
class MockStorageService extends Service {
|
||||||
|
savedPlaces = [];
|
||||||
|
findPlaceById() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
isPlaceSaved() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rs = { on: () => {} };
|
||||||
|
placesInView = [];
|
||||||
|
loadPlacesInBounds() {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.owner.register('service:storage', MockStorageService);
|
||||||
|
|
||||||
|
class MockMapService extends Service {
|
||||||
|
getBounds() {
|
||||||
|
return {
|
||||||
|
minLat: 52.5,
|
||||||
|
minLon: 13.4,
|
||||||
|
maxLat: 52.6,
|
||||||
|
maxLon: 13.5,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.owner.register('service:map', MockMapService);
|
||||||
|
|
||||||
|
await visit('/');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await visit('/search?category=coffee&lat=52.52&lon=13.405');
|
||||||
|
} catch {
|
||||||
|
// Aborted transition throws, which is expected
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.dom('.toast-notification').exists('Toast should be visible');
|
||||||
|
assert
|
||||||
|
.dom('.toast-notification')
|
||||||
|
.hasText('Search request failed. Please try again.');
|
||||||
|
assert.dom('.places-sidebar').doesNotExist('Results panel should not open');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,11 +14,15 @@ module('Integration | Component | app-header', function (hooks) {
|
|||||||
class MockRouterService extends Service {}
|
class MockRouterService extends Service {}
|
||||||
class MockMapUiService extends Service {}
|
class MockMapUiService extends Service {}
|
||||||
class MockMapService extends Service {}
|
class MockMapService extends Service {}
|
||||||
|
class MockSettingsService extends Service {
|
||||||
|
showQuickSearchButtons = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.owner.register('service:photon', MockPhotonService);
|
this.owner.register('service:photon', MockPhotonService);
|
||||||
this.owner.register('service:router', MockRouterService);
|
this.owner.register('service:router', MockRouterService);
|
||||||
this.owner.register('service:map-ui', MockMapUiService);
|
this.owner.register('service:map-ui', MockMapUiService);
|
||||||
this.owner.register('service:map', MockMapService);
|
this.owner.register('service:map', MockMapService);
|
||||||
|
this.owner.register('service:settings', MockSettingsService);
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
<template><AppHeader @onToggleMenu={{this.noop}} /></template>
|
<template><AppHeader @onToggleMenu={{this.noop}} /></template>
|
||||||
@@ -43,11 +47,15 @@ module('Integration | Component | app-header', function (hooks) {
|
|||||||
currentCenter = null;
|
currentCenter = null;
|
||||||
}
|
}
|
||||||
class MockMapService extends Service {}
|
class MockMapService extends Service {}
|
||||||
|
class MockSettingsService extends Service {
|
||||||
|
showQuickSearchButtons = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.owner.register('service:photon', MockPhotonService);
|
this.owner.register('service:photon', MockPhotonService);
|
||||||
this.owner.register('service:router', MockRouterService);
|
this.owner.register('service:router', MockRouterService);
|
||||||
this.owner.register('service:map-ui', MockMapUiService);
|
this.owner.register('service:map-ui', MockMapUiService);
|
||||||
this.owner.register('service:map', MockMapService);
|
this.owner.register('service:map', MockMapService);
|
||||||
|
this.owner.register('service:settings', MockSettingsService);
|
||||||
|
|
||||||
await render(
|
await render(
|
||||||
<template><AppHeader @onToggleMenu={{this.noop}} /></template>
|
<template><AppHeader @onToggleMenu={{this.noop}} /></template>
|
||||||
|
|||||||
Reference in New Issue
Block a user