Refactor search route/loading
* Fetch results asynchronously after app launch * Hide sidebar and search results when new search is issued
This commit is contained in:
@@ -1,6 +1,16 @@
|
|||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
|
import { service } from '@ember/service';
|
||||||
|
import { task } from 'ember-concurrency';
|
||||||
|
import { getDistance } from '../utils/geo';
|
||||||
|
|
||||||
export default class SearchController extends Controller {
|
export default class SearchController extends Controller {
|
||||||
|
@service osm;
|
||||||
|
@service photon;
|
||||||
|
@service mapUi;
|
||||||
|
@service storage;
|
||||||
|
@service router;
|
||||||
|
@service toast;
|
||||||
|
|
||||||
queryParams = ['lat', 'lon', 'q', 'selected', 'category'];
|
queryParams = ['lat', 'lon', 'q', 'selected', 'category'];
|
||||||
|
|
||||||
lat = null;
|
lat = null;
|
||||||
@@ -8,4 +18,175 @@ export default class SearchController extends Controller {
|
|||||||
q = null;
|
q = null;
|
||||||
selected = null;
|
selected = null;
|
||||||
category = null;
|
category = null;
|
||||||
|
|
||||||
|
fetchResultsTask = task({ restartable: true }, async (params) => {
|
||||||
|
// Hide sidebar and clear previous results immediately to signal a new search
|
||||||
|
this.mapUi.hideSidebar();
|
||||||
|
this.mapUi.clearSearchResults();
|
||||||
|
|
||||||
|
const lat = params.lat ? parseFloat(params.lat) : null;
|
||||||
|
const lon = params.lon ? parseFloat(params.lon) : null;
|
||||||
|
let pois = [];
|
||||||
|
let loadingType = null;
|
||||||
|
let loadingValue = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Case 0: Category Search (category parameter present)
|
||||||
|
if (params.category && lat && lon) {
|
||||||
|
loadingType = 'category';
|
||||||
|
loadingValue = params.category;
|
||||||
|
this.mapUi.startLoading(loadingType, loadingValue);
|
||||||
|
|
||||||
|
// We need bounds. If we have active map state, use it.
|
||||||
|
let bounds = this.mapUi.currentBounds;
|
||||||
|
|
||||||
|
// If we don't have bounds (direct URL visit), estimate them from lat/lon/zoom(16)
|
||||||
|
// or just use a fixed box around the center.
|
||||||
|
if (!bounds) {
|
||||||
|
// Approximate 0.01 degrees ~ 1km at equator. A viewport is roughly 0.02x0.01 at zoom 16?
|
||||||
|
// Let's take a safe box of ~1km radius.
|
||||||
|
const delta = 0.01;
|
||||||
|
bounds = {
|
||||||
|
minLat: lat - delta,
|
||||||
|
maxLat: lat + delta,
|
||||||
|
minLon: lon - delta,
|
||||||
|
maxLon: lon + delta,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pois = await this.osm.getCategoryPois(
|
||||||
|
bounds,
|
||||||
|
params.category,
|
||||||
|
lat,
|
||||||
|
lon
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sort by distance from center
|
||||||
|
pois = pois
|
||||||
|
.map((p) => ({
|
||||||
|
...p,
|
||||||
|
_distance: getDistance(lat, lon, p.lat, p.lon),
|
||||||
|
}))
|
||||||
|
.sort((a, b) => a._distance - b._distance);
|
||||||
|
}
|
||||||
|
// Case 1: Text Search (q parameter present)
|
||||||
|
else if (params.q) {
|
||||||
|
loadingType = 'text';
|
||||||
|
loadingValue = params.q;
|
||||||
|
this.mapUi.startLoading(loadingType, loadingValue);
|
||||||
|
|
||||||
|
// Search with Photon (using lat/lon for bias if available)
|
||||||
|
pois = await this.photon.search(params.q, lat, lon);
|
||||||
|
|
||||||
|
// Search local bookmarks by name
|
||||||
|
const queryLower = params.q.toLowerCase();
|
||||||
|
const localMatches = this.storage.savedPlaces.filter((p) => {
|
||||||
|
return (
|
||||||
|
p.title?.toLowerCase().includes(queryLower) ||
|
||||||
|
p.description?.toLowerCase().includes(queryLower)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merge local matches
|
||||||
|
localMatches.forEach((local) => {
|
||||||
|
const exists = pois.find(
|
||||||
|
(poi) =>
|
||||||
|
(local.osmId && poi.osmId === local.osmId) ||
|
||||||
|
(poi.id && poi.id === local.id)
|
||||||
|
);
|
||||||
|
if (!exists) {
|
||||||
|
pois.push(local);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Case 2: Nearby Search (lat/lon present, no q)
|
||||||
|
else if (lat && lon) {
|
||||||
|
// Nearby search does NOT trigger loading state (pulse is used instead)
|
||||||
|
const searchRadius = 50; // Default radius
|
||||||
|
|
||||||
|
// Fetch POIs from Overpass
|
||||||
|
pois = await this.osm.getNearbyPois(lat, lon, searchRadius);
|
||||||
|
|
||||||
|
// Get cached/saved places in search radius
|
||||||
|
const localMatches = this.storage.savedPlaces.filter((p) => {
|
||||||
|
const dist = getDistance(lat, lon, p.lat, p.lon);
|
||||||
|
return dist <= searchRadius;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merge local matches
|
||||||
|
localMatches.forEach((local) => {
|
||||||
|
const exists = pois.find(
|
||||||
|
(poi) =>
|
||||||
|
(local.osmId && poi.osmId === local.osmId) ||
|
||||||
|
(poi.id && poi.id === local.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
pois.push(local);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort by distance from click
|
||||||
|
pois = pois
|
||||||
|
.map((p) => {
|
||||||
|
return {
|
||||||
|
...p,
|
||||||
|
_distance: getDistance(lat, lon, p.lat, p.lon),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => a._distance - b._distance);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Search request failed.', error);
|
||||||
|
this.toast.show('Search request failed. Please try again.');
|
||||||
|
this.mapUi.stopSearch();
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
if (loadingType && loadingValue) {
|
||||||
|
this.mapUi.stopLoading(loadingType, loadingValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any of these are already bookmarked
|
||||||
|
// We resolve them to the bookmark version if they exist
|
||||||
|
pois = pois.map((p) => {
|
||||||
|
const saved = this.storage.findPlaceById(p.osmId);
|
||||||
|
return saved || p;
|
||||||
|
});
|
||||||
|
|
||||||
|
const targetName = params.selected || params.q;
|
||||||
|
|
||||||
|
if (targetName && pois.length > 0) {
|
||||||
|
let matchedPlace = null;
|
||||||
|
|
||||||
|
// 1. Exact Name Match
|
||||||
|
matchedPlace = pois.find(
|
||||||
|
(p) =>
|
||||||
|
p.osmTags &&
|
||||||
|
(p.osmTags.name === targetName || p.osmTags['name:en'] === targetName)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. High Proximity Match (<= 10m) - Only if we don't have a name match
|
||||||
|
// Note: MapComponent had logic for <=20m + type match.
|
||||||
|
// We might want to pass the 'type' in queryParams if we want to be that precise.
|
||||||
|
// For now, let's stick to name or very close proximity.
|
||||||
|
if (!matchedPlace) {
|
||||||
|
const topCandidate = pois[0];
|
||||||
|
if (topCandidate._distance <= 10) {
|
||||||
|
matchedPlace = topCandidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchedPlace) {
|
||||||
|
// Direct transition!
|
||||||
|
this.router.replaceWith('place', matchedPlace);
|
||||||
|
this.mapUi.stopSearch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mapUi.setSearchResults(pois);
|
||||||
|
this.mapUi.showSidebar();
|
||||||
|
this.mapUi.stopSearch();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { service } from '@ember/service';
|
import { service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import { getDistance } from '../utils/geo';
|
|
||||||
|
|
||||||
export default class SearchRoute extends Route {
|
export default class SearchRoute extends Route {
|
||||||
@service osm;
|
|
||||||
@service photon;
|
|
||||||
@service mapUi;
|
@service mapUi;
|
||||||
@service storage;
|
|
||||||
@service router;
|
|
||||||
@service toast;
|
@service toast;
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
@@ -19,187 +14,29 @@ export default class SearchRoute extends Route {
|
|||||||
category: { refreshModel: true },
|
category: { refreshModel: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
async model(params) {
|
model(params) {
|
||||||
const lat = params.lat ? parseFloat(params.lat) : null;
|
// Just return params, doing the async fetch in the controller
|
||||||
const lon = params.lon ? parseFloat(params.lon) : null;
|
return params;
|
||||||
let pois = [];
|
|
||||||
let loadingType = null;
|
|
||||||
let loadingValue = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Case 0: Category Search (category parameter present)
|
|
||||||
if (params.category && lat && lon) {
|
|
||||||
loadingType = 'category';
|
|
||||||
loadingValue = params.category;
|
|
||||||
this.mapUi.startLoading(loadingType, loadingValue);
|
|
||||||
|
|
||||||
// We need bounds. If we have active map state, use it.
|
|
||||||
let bounds = this.mapUi.currentBounds;
|
|
||||||
|
|
||||||
// If we don't have bounds (direct URL visit), estimate them from lat/lon/zoom(16)
|
|
||||||
// or just use a fixed box around the center.
|
|
||||||
if (!bounds) {
|
|
||||||
// Approximate 0.01 degrees ~ 1km at equator. A viewport is roughly 0.02x0.01 at zoom 16?
|
|
||||||
// Let's take a safe box of ~1km radius.
|
|
||||||
const delta = 0.01;
|
|
||||||
bounds = {
|
|
||||||
minLat: lat - delta,
|
|
||||||
maxLat: lat + delta,
|
|
||||||
minLon: lon - delta,
|
|
||||||
maxLon: lon + delta,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pois = await this.osm.getCategoryPois(
|
|
||||||
bounds,
|
|
||||||
params.category,
|
|
||||||
lat,
|
|
||||||
lon
|
|
||||||
);
|
|
||||||
|
|
||||||
// Sort by distance from center
|
|
||||||
pois = pois
|
|
||||||
.map((p) => ({
|
|
||||||
...p,
|
|
||||||
_distance: getDistance(lat, lon, p.lat, p.lon),
|
|
||||||
}))
|
|
||||||
.sort((a, b) => a._distance - b._distance);
|
|
||||||
}
|
|
||||||
// Case 1: Text Search (q parameter present)
|
|
||||||
else if (params.q) {
|
|
||||||
loadingType = 'text';
|
|
||||||
loadingValue = params.q;
|
|
||||||
this.mapUi.startLoading(loadingType, loadingValue);
|
|
||||||
|
|
||||||
// Search with Photon (using lat/lon for bias if available)
|
|
||||||
pois = await this.photon.search(params.q, lat, lon);
|
|
||||||
|
|
||||||
// Search local bookmarks by name
|
|
||||||
const queryLower = params.q.toLowerCase();
|
|
||||||
const localMatches = this.storage.savedPlaces.filter((p) => {
|
|
||||||
return (
|
|
||||||
p.title?.toLowerCase().includes(queryLower) ||
|
|
||||||
p.description?.toLowerCase().includes(queryLower)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Merge local matches
|
|
||||||
localMatches.forEach((local) => {
|
|
||||||
const exists = pois.find(
|
|
||||||
(poi) =>
|
|
||||||
(local.osmId && poi.osmId === local.osmId) ||
|
|
||||||
(poi.id && poi.id === local.id)
|
|
||||||
);
|
|
||||||
if (!exists) {
|
|
||||||
pois.push(local);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case 2: Nearby Search (lat/lon present, no q)
|
|
||||||
else if (lat && lon) {
|
|
||||||
// Nearby search does NOT trigger loading state (pulse is used instead)
|
|
||||||
const searchRadius = 50; // Default radius
|
|
||||||
|
|
||||||
// Fetch POIs from Overpass
|
|
||||||
pois = await this.osm.getNearbyPois(lat, lon, searchRadius);
|
|
||||||
|
|
||||||
// Get cached/saved places in search radius
|
|
||||||
const localMatches = this.storage.savedPlaces.filter((p) => {
|
|
||||||
const dist = getDistance(lat, lon, p.lat, p.lon);
|
|
||||||
return dist <= searchRadius;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Merge local matches
|
|
||||||
localMatches.forEach((local) => {
|
|
||||||
const exists = pois.find(
|
|
||||||
(poi) =>
|
|
||||||
(local.osmId && poi.osmId === local.osmId) ||
|
|
||||||
(poi.id && poi.id === local.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!exists) {
|
|
||||||
pois.push(local);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sort by distance from click
|
|
||||||
pois = pois
|
|
||||||
.map((p) => {
|
|
||||||
return {
|
|
||||||
...p,
|
|
||||||
_distance: getDistance(lat, lon, p.lat, p.lon),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.sort((a, b) => a._distance - b._distance);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (loadingType && loadingValue) {
|
|
||||||
this.mapUi.stopLoading(loadingType, loadingValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if any of these are already bookmarked
|
|
||||||
// We resolve them to the bookmark version if they exist
|
|
||||||
pois = pois.map((p) => {
|
|
||||||
const saved = this.storage.findPlaceById(p.osmId);
|
|
||||||
return saved || p;
|
|
||||||
});
|
|
||||||
|
|
||||||
return pois;
|
|
||||||
}
|
|
||||||
|
|
||||||
afterModel(model, transition) {
|
|
||||||
const { q, selected } = transition.to.queryParams;
|
|
||||||
|
|
||||||
// Heuristic Match Logic (ported from MapComponent)
|
|
||||||
// If 'selected' is provided (from map click), try to find that specific feature.
|
|
||||||
// If 'q' is provided (from text search), try to find an exact match to auto-select.
|
|
||||||
const targetName = selected || q;
|
|
||||||
|
|
||||||
if (targetName && model.length > 0) {
|
|
||||||
let matchedPlace = null;
|
|
||||||
|
|
||||||
// 1. Exact Name Match
|
|
||||||
matchedPlace = model.find(
|
|
||||||
(p) =>
|
|
||||||
p.osmTags &&
|
|
||||||
(p.osmTags.name === targetName || p.osmTags['name:en'] === targetName)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 2. High Proximity Match (<= 10m) - Only if we don't have a name match
|
|
||||||
// Note: MapComponent had logic for <=20m + type match.
|
|
||||||
// We might want to pass the 'type' in queryParams if we want to be that precise.
|
|
||||||
// For now, let's stick to name or very close proximity.
|
|
||||||
if (!matchedPlace) {
|
|
||||||
const topCandidate = model[0];
|
|
||||||
if (topCandidate._distance <= 10) {
|
|
||||||
matchedPlace = topCandidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchedPlace) {
|
|
||||||
// Direct transition!
|
|
||||||
this.router.replaceWith('place', matchedPlace);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the pulse animation since search is done (and we are staying here)
|
|
||||||
this.mapUi.stopSearch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
super.setupController(controller, model);
|
super.setupController(controller, model);
|
||||||
// Ensure pulse is stopped if we reach here
|
|
||||||
this.mapUi.stopSearch();
|
// Trigger the background task to fetch results
|
||||||
this.mapUi.setSearchResults(model);
|
controller.fetchResultsTask.perform(model);
|
||||||
this.mapUi.showSidebar();
|
|
||||||
|
|
||||||
// Store current search params to allow "Up" navigation from place details
|
// Store current search params to allow "Up" navigation from place details
|
||||||
const { q, category, lat, lon } = this.paramsFor('search');
|
const { q, category, lat, lon } = this.paramsFor('search');
|
||||||
this.mapUi.currentSearch = { q, category, lat, lon };
|
this.mapUi.currentSearch = { q, category, lat, lon };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetController(controller, isExiting) {
|
||||||
|
if (isExiting) {
|
||||||
|
controller.fetchResultsTask.cancelAll();
|
||||||
|
this.mapUi.stopSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
error(error, transition) {
|
error(error, transition) {
|
||||||
this.mapUi.stopSearch();
|
this.mapUi.stopSearch();
|
||||||
@@ -207,6 +44,6 @@ export default class SearchRoute extends Route {
|
|||||||
if (transition) {
|
if (transition) {
|
||||||
transition.abort();
|
transition.abort();
|
||||||
}
|
}
|
||||||
return false; // Prevent bubble and stop transition
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default class SearchTemplate extends Component {
|
|||||||
<template>
|
<template>
|
||||||
{{#if this.mapUi.isSidebarVisible}}
|
{{#if this.mapUi.isSidebarVisible}}
|
||||||
<PlacesSidebar
|
<PlacesSidebar
|
||||||
@places={{@model}}
|
@places={{this.mapUi.searchResults}}
|
||||||
@onSelect={{this.selectPlace}}
|
@onSelect={{this.selectPlace}}
|
||||||
@onClose={{this.close}}
|
@onClose={{this.close}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ import toolbox from '@waysidemapping/pinhead/dist/icons/toolbox.svg?raw';
|
|||||||
import treeAndBenchWithBackrest from '@waysidemapping/pinhead/dist/icons/tree_and_bench_with_backrest.svg?raw';
|
import treeAndBenchWithBackrest from '@waysidemapping/pinhead/dist/icons/tree_and_bench_with_backrest.svg?raw';
|
||||||
import villageBuildings from '@waysidemapping/pinhead/dist/icons/village_buildings.svg?raw';
|
import villageBuildings from '@waysidemapping/pinhead/dist/icons/village_buildings.svg?raw';
|
||||||
import wallHangingWithMountainsAndSun from '@waysidemapping/pinhead/dist/icons/wall_hanging_with_mountains_and_sun.svg?raw';
|
import wallHangingWithMountainsAndSun from '@waysidemapping/pinhead/dist/icons/wall_hanging_with_mountains_and_sun.svg?raw';
|
||||||
|
import windingWayWide from '@waysidemapping/pinhead/dist/icons/winding_way_wide.svg?raw';
|
||||||
import womensAndMensRestroomSymbol from '@waysidemapping/pinhead/dist/icons/womens_and_mens_restroom_symbol.svg?raw';
|
import womensAndMensRestroomSymbol from '@waysidemapping/pinhead/dist/icons/womens_and_mens_restroom_symbol.svg?raw';
|
||||||
|
|
||||||
import loadingRing from '../icons/270-ring.svg?raw';
|
import loadingRing from '../icons/270-ring.svg?raw';
|
||||||
@@ -243,6 +244,7 @@ const ICONS = {
|
|||||||
'womens-and-mens-restroom-symbol': womensAndMensRestroomSymbol,
|
'womens-and-mens-restroom-symbol': womensAndMensRestroomSymbol,
|
||||||
whatsapp,
|
whatsapp,
|
||||||
wikipedia,
|
wikipedia,
|
||||||
|
winding_way_wide: windingWayWide,
|
||||||
parking_p: parkingP,
|
parking_p: parkingP,
|
||||||
car,
|
car,
|
||||||
x,
|
x,
|
||||||
|
|||||||
@@ -109,7 +109,9 @@ export const POI_ICON_RULES = [
|
|||||||
{ tags: { amenity: 'arts_center' }, icon: 'comedy-mask-and-tragedy-mask' },
|
{ tags: { amenity: 'arts_center' }, icon: 'comedy-mask-and-tragedy-mask' },
|
||||||
|
|
||||||
// Historic
|
// Historic
|
||||||
|
{ tags: { historic: 'canal' }, icon: 'winding_way_wide' },
|
||||||
{ tags: { historic: 'bridge' }, icon: 'bridge' },
|
{ tags: { historic: 'bridge' }, icon: 'bridge' },
|
||||||
|
{ tags: { historic: 'bridge_site' }, icon: 'bridge' },
|
||||||
{ tags: { historic: 'fort' }, icon: 'fort' },
|
{ tags: { historic: 'fort' }, icon: 'fort' },
|
||||||
{ tags: { historic: 'castle' }, icon: 'palace' },
|
{ tags: { historic: 'castle' }, icon: 'palace' },
|
||||||
{ tags: { historic: 'building' }, icon: 'classical-building-with-flag' },
|
{ tags: { historic: 'building' }, icon: 'classical-building-with-flag' },
|
||||||
|
|||||||
@@ -140,12 +140,14 @@ module('Acceptance | map search reset', function (hooks) {
|
|||||||
bubbles: true,
|
bubbles: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for transition to index
|
// Wait for transition or UI update
|
||||||
await new Promise((r) => setTimeout(r, 500));
|
await new Promise((r) => setTimeout(r, 500));
|
||||||
assert.strictEqual(
|
|
||||||
currentURL(),
|
// Sidebar should be hidden, but we should stay on the search route
|
||||||
'/',
|
assert.dom('.sidebar').doesNotExist('Sidebar should be closed');
|
||||||
'Should have transitioned to index (closed sidebar)'
|
assert.ok(
|
||||||
|
currentURL().includes('category=coffee'),
|
||||||
|
'Should have stayed on the search route with markers intact'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Second Click (Start new search)
|
// Second Click (Start new search)
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ module('Acceptance | navigation', function (hooks) {
|
|||||||
// Click the Close (X) button
|
// Click the Close (X) button
|
||||||
await click('.close-btn');
|
await click('.close-btn');
|
||||||
|
|
||||||
assert.strictEqual(currentURL(), '/', 'Returned to index');
|
assert.dom('.sidebar').doesNotExist('Sidebar should be closed');
|
||||||
assert.false(mapUi.returnToSearch, 'Flag is reset after closing sidebar');
|
assert.ok(currentURL().includes('/place/'), 'Remains on place route');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('navigating directly to place and back closes sidebar', async function (assert) {
|
test('navigating directly to place and back closes sidebar', async function (assert) {
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ module('Acceptance | search loading', function (hooks) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await searchPromise;
|
await searchPromise;
|
||||||
|
await new Promise((r) => setTimeout(r, 250));
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
mapUi.loadingState,
|
mapUi.loadingState,
|
||||||
null,
|
null,
|
||||||
@@ -84,6 +85,7 @@ module('Acceptance | search loading', function (hooks) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await catPromise;
|
await catPromise;
|
||||||
|
await new Promise((r) => setTimeout(r, 250));
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
mapUi.loadingState,
|
mapUi.loadingState,
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ module('Unit | Route | place', function (hooks) {
|
|||||||
selectPlaceCalled = true;
|
selectPlaceCalled = true;
|
||||||
}
|
}
|
||||||
stopSearch() {}
|
stopSearch() {}
|
||||||
|
showSidebar() {}
|
||||||
|
hideSidebar() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.owner.register('service:osm', OsmStub);
|
this.owner.register('service:osm', OsmStub);
|
||||||
@@ -76,6 +78,8 @@ module('Unit | Route | place', function (hooks) {
|
|||||||
class MapUiStub extends Service {
|
class MapUiStub extends Service {
|
||||||
selectPlace() {}
|
selectPlace() {}
|
||||||
stopSearch() {}
|
stopSearch() {}
|
||||||
|
showSidebar() {}
|
||||||
|
hideSidebar() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.owner.register('service:osm', OsmStub);
|
this.owner.register('service:osm', OsmStub);
|
||||||
@@ -110,6 +114,8 @@ module('Unit | Route | place', function (hooks) {
|
|||||||
class MapUiStub extends Service {
|
class MapUiStub extends Service {
|
||||||
selectPlace() {}
|
selectPlace() {}
|
||||||
stopSearch() {}
|
stopSearch() {}
|
||||||
|
showSidebar() {}
|
||||||
|
hideSidebar() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.owner.register('service:osm', OsmStub);
|
this.owner.register('service:osm', OsmStub);
|
||||||
@@ -155,6 +161,8 @@ module('Unit | Route | place', function (hooks) {
|
|||||||
assert.ok(options.preventZoom, 'Prevented zoom on update');
|
assert.ok(options.preventZoom, 'Prevented zoom on update');
|
||||||
}
|
}
|
||||||
stopSearch() {}
|
stopSearch() {}
|
||||||
|
showSidebar() {}
|
||||||
|
hideSidebar() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.owner.register('service:storage', StorageStub);
|
this.owner.register('service:storage', StorageStub);
|
||||||
|
|||||||
Reference in New Issue
Block a user