Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
0a6faeb
Search in tracks
Dima-1 Apr 30, 2026
83720d0
Open track from search
Dima-1 Apr 30, 2026
74a17fd
Fix search after deleting a track. Add a test.
Dima-1 May 1, 2026
d40ca15
Fix return to search result. Improve test.
Dima-1 May 1, 2026
e6fffcc
Handle new type according to existing pattern
Dima-1 May 4, 2026
5d3d775
Merge search results with results from uniqueFiles search
Dima-1 May 6, 2026
4fa726b
Remove openedFromSearch
Dima-1 May 7, 2026
786657b
Rename icon
Dima-1 May 7, 2026
8c87c30
Search in favorites
Dima-1 May 8, 2026
392a1d7
Fix favorite search result item UI
Dima-1 May 11, 2026
cb8e110
Merge branch 'main' into search-fav-or-track
Dima-1 May 12, 2026
c06ef11
Fix secondaryMarker hover icon
Dima-1 May 12, 2026
3883a27
Fix 36 test, refactoring
Dima-1 May 12, 2026
7cc0b82
Add test 37-search-favorite
Dima-1 May 12, 2026
55632ce
Use collator instead normalize('NFKD')
Dima-1 May 13, 2026
8a912b2
FIx review: replace for with flatMap + filter, getFavoriteMenuIconHtm…
Dima-1 May 13, 2026
3660872
Fix back cross and menu flashing
Dima-1 May 13, 2026
4268c5b
Remove code duplication
Dima-1 May 14, 2026
223b924
Fix hover
Dima-1 May 14, 2026
63c4337
Combine tests. Make search collator static.
Dima-1 May 14, 2026
21425f5
Merge branch 'main' into search-fav-or-track
Dima-1 May 14, 2026
9511dc5
Fix favorite state
Dima-1 May 15, 2026
901662d
Fix favorite state refactor
Dima-1 May 15, 2026
e04d8df
Fix remove returnToSearch
Dima-1 May 15, 2026
cff1341
Fix search refresh
Dima-1 May 15, 2026
31008ef
Merge SearchObjectManager in SearchManager, extract FavoriteSearchRes…
Dima-1 May 18, 2026
0fe9313
Refactoring marker use, create common component FavoriteItemContent
Dima-1 May 18, 2026
7b1eacd
Move searchIncludes to SearchLayer
Dima-1 May 18, 2026
ac6ec4c
Remove unnecessary mapObj check. Fix test.
Dima-1 May 19, 2026
73b326a
Refactoring and add comments
alisa911 May 19, 2026
1a39cae
Refactoring
alisa911 May 19, 2026
610b71b
Rallback
alisa911 May 19, 2026
07f241b
Recessary complexity in favorite search results
alisa911 May 19, 2026
b15d4b8
Fix hide markers
Dima-1 May 20, 2026
982f19c
Remove wrapper method, rename cloudFeatures, refactor getTrackInfoText
Dima-1 May 21, 2026
061216c
Fix URL
Dima-1 May 21, 2026
80772da
Format
Dima-1 May 21, 2026
95369b9
Merge branch 'main' into search-fav-or-track
Dima-1 May 21, 2026
3d4cc46
Fix search results for favorites and tracks
alisa911 May 21, 2026
9ed1f15
Remove icon
alisa911 May 21, 2026
14dfdcd
Remove unnecessary changes
alisa911 May 21, 2026
ea59cb8
Refactoring
alisa911 May 21, 2026
c33226f
Fix search results restore for tracks and favorites
alisa911 May 21, 2026
e7515cb
Remove unnecessary changes
alisa911 May 21, 2026
8d5def1
Fix track info
alisa911 May 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions map/public/images/map_icons/ic_action_polygom_dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions map/src/infoblock/components/InformationBlock.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,10 @@ export default function InformationBlock({
function closeMapObjectMenu() {
ctx.setCloseMapObj(true);
if (!isEmpty(ctx.gpxFiles) && ctx.gpxFiles[ctx.selectedGpxFile.name]) {
ctx.mutateGpxFiles((o) => (o[ctx.selectedGpxFile.name].mapObj = null));
ctx.mutateGpxFiles((o) => {
o[ctx.selectedGpxFile.name].mapObj = null;
o[ctx.selectedGpxFile.name].openedFromSearch = null;
Comment thread
Dima-1 marked this conversation as resolved.
Outdated
});
}
}

Expand Down Expand Up @@ -446,7 +449,7 @@ export default function InformationBlock({
track={ctx.selectedGpxFile}
onClose={handleCloseTrackContextMenu}
tabsObj={tabsObj}
showBackButton={!ctx.selectedGpxFile?.mapObj}
showBackButton={!ctx.selectedGpxFile?.mapObj || ctx.selectedGpxFile?.openedFromSearch}
/>
)}
</>
Expand Down
13 changes: 13 additions & 0 deletions map/src/manager/track/DeleteTrackManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ async function deleteCloudFile(name, type, ctx) {
return { ...o };
});

// delete track from search results cache
Comment thread
Dima-1 marked this conversation as resolved.
Outdated
ctx.setSearchResult((prevResult) => {
if (!prevResult?.features) {
return prevResult;
}
return {
...prevResult,
features: prevResult.features.filter((feature) => feature?.properties?.web_name !== name),
};
});

await loadSmartFolders(ctx.setTracksGroups, ctx.setSmartFoldersCache);
}
}
Expand All @@ -101,6 +112,8 @@ export async function deleteTrackFolder(folder, ctx) {
dataOnErrors: true,
});
if (res && res?.data?.status === 'ok') {
// Clear search results cache when folder is deleted
ctx.setSearchResult(null);
Comment thread
Dima-1 marked this conversation as resolved.
Outdated
refreshGlobalFiles({ ctx }).then();
} else {
ctx.setTrackErrorMsg({
Expand Down
4 changes: 3 additions & 1 deletion map/src/manager/track/TracksManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
OBJECT_TYPE_CLOUD_TRACK,
OBJECT_TYPE_LOCAL_TRACK,
OBJECT_TYPE_SHARE_FILE,
OBJECT_SEARCH,
} from '../../context/AppContext';
import { confirm } from '../../dialogs/GlobalConfirmationDialog';
import { saveTrackToLocal } from './SaveTrackManager';
Expand Down Expand Up @@ -1545,6 +1546,7 @@ export async function openTrackOnMap({
track.name = file.name;
track.key = track.name;
track.mapObj = file.mapObj;
track.openedFromSearch = file.openedFromSearch;
Object.keys(track).forEach((t) => {
oneGpxFile[t] = track[t];
});
Expand Down Expand Up @@ -1630,7 +1632,7 @@ function showInfoBlock({ hasUrl, file, ctx, smartf, recentSaver }) {
allFiles = ctx.gpxFiles;

// not set for track analyzer, because we need to keep the current object type
if (ctx.currentObjectType !== OBJECT_TRACK_ANALYZER) {
if (ctx.currentObjectType !== OBJECT_TRACK_ANALYZER && (ctx.currentObjectType !== OBJECT_SEARCH || file?.mapObj)) {
Comment thread
Dima-1 marked this conversation as resolved.
Outdated
ctx.setCurrentObjectType(OBJECT_TYPE_CLOUD_TRACK);
}
}
Expand Down
3 changes: 3 additions & 0 deletions map/src/map/layers/SearchLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const SEARCH_ICON_MAP_LOCATION = 'location';
export const SEARCH_ICON_MAP_BUILDING = 'house';
export const SEARCH_ICON_MAP_STREET = 'street';
export const SEARCH_ICON_MAP_INTERSECTION = 'intersection';
export const SEARCH_ICON_MAP_GPX_TRACK = 'gpx_track';

export const ZOOM_TO_MAP = 17;

Expand All @@ -57,13 +58,15 @@ export const searchTypeMap = {
CITY: 'CITY',
TOWN: 'TOWN',
VILLAGE: 'VILLAGE',
GPX_TRACK: 'GPX_TRACK',
};

export const typeIconMap = {
[searchTypeMap.LOCATION]: SEARCH_ICON_MAP_LOCATION,
[searchTypeMap.HOUSE]: SEARCH_ICON_MAP_BUILDING,
[searchTypeMap.STREET]: SEARCH_ICON_MAP_STREET,
[searchTypeMap.INTERSECTION]: SEARCH_ICON_MAP_INTERSECTION,
[searchTypeMap.GPX_TRACK]: SEARCH_ICON_MAP_GPX_TRACK,
};

export function getObjIdSearch(obj) {
Expand Down
1 change: 1 addition & 0 deletions map/src/map/util/MapManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const iconPathMap = {
street: '/map/images/map_icons/ic_action_street_name.svg',
intersection: '/map/images/map_icons/ic_action_intersection.svg',
ic_action_transport_bus: '/map/images/map_icons/ic_action_transport_bus.svg',
gpx_track: '/map/images/map_icons/ic_action_polygom_dark.svg',
};

export async function getIconFromMap(name) {
Expand Down
29 changes: 29 additions & 0 deletions map/src/menu/search/search/SearchResultItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import useSearchNav from '../../../util/hooks/search/useSearchNav';
import { POI_OBJECTS_KEY, useRecentDataSaver } from '../../../util/hooks/menu/useRecentDataSaver';
import i18n from 'i18next';
import { useNavigate } from 'react-router-dom';
import { getDist, getTime, openTrackOnMap, updateTracks } from '../../../manager/track/TracksManager';
import { convertMeters, getLargeLengthUnit, LARGE_UNIT } from '../../settings/units/UnitsConverter';

export function getFirstSubstring(inputString) {
if (inputString?.includes(SEPARATOR)) {
Expand Down Expand Up @@ -73,6 +75,8 @@ export function getPropsFromSearchResultItem(props, t = null, lang = null) {
type = poiType;
}
type = preparedType(type, t);
} else if (props[CATEGORY_TYPE] === searchTypeMap.GPX_TRACK) {
name = props[CATEGORY_NAME];
} else {
name = props[CATEGORY_NAME];
if (props[CATEGORY_TYPE] === searchTypeMap.POI_TYPE) {
Expand Down Expand Up @@ -161,6 +165,14 @@ export default function SearchResultItem({ item, typeItem, index }) {
const bearing = item.bearing;
const isUserLocation = item.isUserLocation;
const icon = item.icon;
if (item.properties[CATEGORY_TYPE] === searchTypeMap.GPX_TRACK) {
Comment thread
Dima-1 marked this conversation as resolved.
Outdated
const fileName = item.properties?.[CATEGORY_NAME];
const trackFile = ctx.listFiles?.uniqueFiles.find((f) => f.name === fileName);
const dist = `${convertMeters(getDist(trackFile), ctx.unitsSettings.len, LARGE_UNIT)?.toFixed(2)} ${t(getLargeLengthUnit(ctx))}`;
Comment thread
Dima-1 marked this conversation as resolved.
Outdated
const time = getTime(trackFile);
const info = `${dist}${time ? ` · ${time}` : ''}`;
return { ...res, icon, distance, bearing, isUserLocation, info };
}
return { ...res, icon, distance, bearing, isUserLocation };
}

Expand All @@ -176,6 +188,23 @@ export default function SearchResultItem({ item, typeItem, index }) {
})();

async function clickHandler() {
if (item.properties?.[CATEGORY_TYPE] === searchTypeMap.GPX_TRACK) {
const fileName = item.properties?.[CATEGORY_NAME];
const file = ctx.listFiles?.uniqueFiles?.find((f) => f?.name === fileName);
if (!file) return;
file.mapObj = true;
Comment thread
Dima-1 marked this conversation as resolved.
Outdated
file.openedFromSearch = true;
const newTracks = await openTrackOnMap({
file,
showOnMap: true,
showInfo: true,
zoomToTrack: true,
ctx,
recentSaver,
});
updateTracks(ctx, null, newTracks);
return;
}
if (item.geometry.coordinates[0] !== 0 && item.geometry.coordinates[1] !== 0) {
const type = item.properties[WEB_PREFIX + TYPE];
let options;
Expand Down
58 changes: 58 additions & 0 deletions tests/selenium/src/tests/search/36-search-track.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { By } from 'selenium-webdriver';
import { assert, clickBy, sendKeysBy, waitBy, waitByRemoved } from '../../lib.mjs';

import actionOpenMap from '../../actions/map/actionOpenMap.mjs';
import actionLogIn from '../../actions/login/actionLogIn.mjs';
import actionUploadGpx from '../../actions/actionUploadGpx.mjs';
import actionLocalToCloud from '../../actions/tracks/actionLocalToCloud.mjs';
import actionDeleteTracksByPattern from '../../actions/tracks/actionDeleteTracksByPattern.mjs';
import actionFinish from '../../actions/actionFinish.mjs';
import { deleteTrack } from '../../util.mjs';


export default async function test() {
await actionOpenMap();
await actionLogIn();

const trackName = 'test-track-wpt';

await actionDeleteTracksByPattern(trackName);

await actionUploadGpx({ mask: `${trackName}.gpx` });
await clickBy(By.id('se-show-menu-planroute'));
await actionLocalToCloud({ mask: trackName });

await searchForTrack(trackName);

await waitBy(By.id('se-search-results'));
const trackResultId = `se-${trackName}.gpx`;
await clickBy(By.id(trackResultId));
await waitBy(By.id('se-track-context-menu'));
await waitBy(By.id(`se-track-${trackName}`));

await clickBy(By.id('se-button-back'));

await waitBy(By.id('se-search-results'));
await waitBy(By.id(trackResultId));

await clickBy(By.id('se-show-menu-tracks'));
await waitBy(By.id(`se-cloud-track-${trackName}`));
await deleteTrack(trackName);

await searchForTrack(trackName);

await waitBy(By.id('se-empty-search'));
const deletedTrackResult = await waitBy(By.id(trackResultId), { optional: true, idle: true });
await assert(!deletedTrackResult, `Deleted track "${trackName}" should be absent in search results`);

await actionFinish();
}

async function searchForTrack(trackName) {
await clickBy(By.id('se-show-main-menu'), { optional: true });
await clickBy(By.id('se-show-menu-search'));
await waitBy(By.id('se-search-input'));
await clickBy(By.id('se-search-input-cancel'), { optional: true });
await sendKeysBy(By.id('se-search-input'), `${trackName}\n`);
await waitByRemoved(By.id('se-loading-page'), { optional: true });
}