Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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_polygon_dark.svg
Comment thread
Dima-1 marked this conversation as resolved.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions map/src/context/AppContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import supportedLanguages from '../resources/translations/supportedLanguages.jso
import { normalizeLang } from '../util/lang';

export const collator = new Intl.Collator(supportedLanguages.map(normalizeLang), { numeric: true });
export const searchCollator = new Intl.Collator(supportedLanguages.map(normalizeLang), {
usage: 'search',
sensitivity: 'base',
});

export const OBJECT_TYPE_LOCAL_TRACK = 'local_track'; // track in localStorage
export const OBJECT_TYPE_CLOUD_TRACK = 'cloud_track'; // track in OsmAnd Cloud
Expand Down Expand Up @@ -329,6 +333,9 @@ export const AppContextProvider = (props) => {
const [selectedCloudTrackObj, setSelectedCloudTrackObj] = useState(null);
const [selectedLocalTrackObj, setSelectedLocalTrackObj] = useState(null);
const [selectedPoiObj, setSelectedPoiObj] = useState(null);
const [selectedSearchObj, setSelectedSearchObj] = useState(null);
// Group IDs of favorites visible in current search results — their map markers must always show.
const [searchFavoriteGroupIds, setSearchFavoriteGroupIds] = useState(null);

const [processingPoiByUrl, setProcessingPoiByUrl] = useState(false);
const [processingStopByUrl, setProcessingStopByUrl] = useState(false);
Expand Down Expand Up @@ -736,6 +743,10 @@ export const AppContextProvider = (props) => {
setPoiCatMenu,
selectedPoiObj,
setSelectedPoiObj,
selectedSearchObj,
setSelectedSearchObj,
searchFavoriteGroupIds,
setSearchFavoriteGroupIds,
zoomToCoords,
setZoomToCoords,
poiByUrl,
Expand Down
13 changes: 11 additions & 2 deletions map/src/infoblock/components/InformationBlock.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
SHARE_MENU_URL,
TRACKS_URL,
} from '../../manager/GlobalManager';
import { isTrackFromSearch, navigateBackToSearchResults } from '../../manager/SearchManager';
import { isVisibleTrack } from '../../menu/visibletracks/VisibleTracks';
import WptDetails from './wpt/WptDetails';
import WptPhotoList from './wpt/WptPhotoList';
Expand Down Expand Up @@ -154,6 +155,7 @@ export default function InformationBlock({
ctx.setTrackRange(null);
setClearState(true);
if (!ctx.currentObjectType) {
setTrackName(null);
hideTrackFromMapIfNotVisible(ctx.selectedGpxFile);
if (!isEmpty(ctx.selectedGpxFile)) {
ctx.setSelectedGpxFile({});
Expand Down Expand Up @@ -388,7 +390,7 @@ export default function InformationBlock({
if (ctx.selectedGpxFile.mapObj) {
closeMapObjectMenu();
} else if (isCloudTrack(ctx)) {
closeCloudTrack();
closeCloudTrack({ fromSearch: isTrackFromSearch(ctx) });
} else if (isLocalTrack(ctx)) {
if (!isEmpty(ctx.selectedGpxFile)) {
ctx.setSelectedGpxFile({});
Expand Down Expand Up @@ -418,7 +420,7 @@ export default function InformationBlock({
ctx.setCurrentObjectType(null);
}

function closeCloudTrack() {
function closeCloudTrack({ fromSearch = false } = {}) {
hideTrackFromMapIfNotVisible(ctx.selectedGpxFile);

// If openGroups is empty (e.g. track was opened directly from Garmin last-sync menu),
Expand Down Expand Up @@ -446,6 +448,13 @@ export default function InformationBlock({
setSavePrevState(true);
ctx.setSelectedCloudTrackObj(null);

if (fromSearch) {
ctx.setSelectedSearchObj(null);
if (navigateBackToSearchResults(navigate, ctx, location)) {
return;
}
}

navigate({
pathname: MAIN_URL_WITH_SLASH + trackType,
hash: location.hash,
Expand Down
2 changes: 1 addition & 1 deletion map/src/infoblock/components/favorite/WptEditPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ export default function WptEditPanel({ setShowInfoBlock }) {
mapObj: true,
};

openFavoriteObj(ctx, newGpxFile);
openFavoriteObj({ ctx, favoriteObj: newGpxFile });

// Clear state
ctx.setAddFavorite((prev) => ({
Expand Down
21 changes: 17 additions & 4 deletions map/src/infoblock/components/wpt/WptDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ import {
import { useWindowSize } from '../../../util/hooks/useWindowSize';
import gStyles from '../../../menu/gstylesmenu.module.css';
import { buildSearchParamsFromQuery } from '../../../util/hooks/search/useSearchNav';
import { isFavoriteFromSearch, navigateBackToSearchResults } from '../../../manager/SearchManager';
import { useLocation, useNavigate } from 'react-router-dom';
import LocationInfoLine from '../common/LocationInfoLine';
import OpeningHoursInfo, { getOpeningHours } from './OpeningHoursInfo';
Expand Down Expand Up @@ -319,7 +320,7 @@ export default function WptDetails({ setOpenWptTab, setShowInfoBlock }) {

const type = getWptType(ctx.selectedWpt);

if ((type?.isFav && !ctx.selectedWpt.mapObj) || type?.isShareFav) {
if ((type?.isFav && !ctx.selectedWpt.mapObj && !isFavoriteFromSearch(ctx)) || type?.isShareFav) {
recentSaver(FAVORITES_KEY, ctx.selectedWpt);
ctx.setSelectedFavoriteObj({ ...ctx.selectedWpt });
}
Expand Down Expand Up @@ -546,7 +547,10 @@ export default function WptDetails({ setOpenWptTab, setShowInfoBlock }) {
isSearch: ctx.currentObjectType === OBJECT_SEARCH && wpt?.poi && !wpt?.wikidata,
isWikiPoi: wpt?.wikidata,
isWpt: isTrack(ctx) && wpt?.trackWpt,
isFav: ctx.currentObjectType === OBJECT_TYPE_FAVORITE && wpt?.markerCurrent,
isFav:
(ctx.currentObjectType === OBJECT_TYPE_FAVORITE ||
ctx.selectedSearchObj?.type === OBJECT_TYPE_FAVORITE) &&
wpt?.markerCurrent,
isShareFav: ctx.currentObjectType === OBJECT_TYPE_SHARE_FILE && wpt?.markerCurrent,
isStop: ctx.currentObjectType === OBJECT_TYPE_STOP && wpt?.stop,
};
Expand Down Expand Up @@ -599,13 +603,22 @@ export default function WptDetails({ setOpenWptTab, setShowInfoBlock }) {
});
}
} else if (type.isFav) {
if (!wpt.mapObj) {
if (isFavoriteFromSearch(ctx)) {
// Opened from search — always return to search results.
ctx.setSelectedSearchObj(null);
setShowInfoBlock(false);
if (!wpt.mapObj) {
ctx.setSelectedFavoriteObj(null);
}
navigateBackToSearchResults(navigate, ctx, location);
} else if (!wpt.mapObj) {
Comment thread
alisa911 marked this conversation as resolved.
ctx.setSelectedFavoriteObj(null);
closeOnlyFavDetails();
} else {
// remove the selected pin from the map
ctx.setCloseMapObj(true);
closeOnlyFavDetails();
}
closeOnlyFavDetails();
} else if (type.isShareFav) {
setShowInfoBlock(false);
ctx.setSelectedGpxFile((prev) => ({ ...prev, markerCurrent: null, favItem: false, name: null }));
Expand Down
117 changes: 103 additions & 14 deletions map/src/manager/FavoritesManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import isEmpty from 'lodash-es/isEmpty';
import { apiPost } from '../util/HttpApi';
import TracksManager from './track/TracksManager';
import { refreshGlobalFiles } from './track/SaveTrackManager';
import { OBJECT_TYPE_FAVORITE, FAVORITES_URL_PARAM_FOLDER } from '../context/AppContext';
import { OBJECT_SEARCH, OBJECT_TYPE_FAVORITE, FAVORITES_URL_PARAM_FOLDER } from '../context/AppContext';
import FavoriteHelper from '../infoblock/components/favorite/FavoriteHelper';
import { getUniqFileId, MAIN_URL_WITH_SLASH, FAVORITES_URL } from './GlobalManager';
import { getFavoriteFromDB, saveFavoriteToDB } from '../context/FavoriteStorage';
Expand Down Expand Up @@ -692,6 +692,19 @@ export function getSize(group, t) {
: 'empty';
}

/** Build icon HTML for a favorite waypoint marker in the menu list. */
export function getFavoriteMenuIconHtml({ wpt = null, icon, color, background } = {}) {
Comment thread
alisa911 marked this conversation as resolved.
const rawHtml = createPoiIcon({
point: wpt ?? {},
icon,
color,
background,
hasBackgroundLight: false,
}).options.html;

return changeIconSizeWpt(removeShadowFromIconWpt(rawHtml), 18, 30, background);
}

export function getFavMenuListByLayers({ layers, wpts, currentLoc, pointsGroups = null }) {
let markerList = [];
Object.values(layers).forEach((value) => {
Expand All @@ -704,14 +717,9 @@ export function getFavMenuListByLayers({ layers, wpts, currentLoc, pointsGroups
return;
}
const appearance = resolveWptAppearance(wpt, pointsGroups);
const icon = createPoiIcon({
point: wpt,
...appearance,
hasBackgroundLight: false,
}).options.html;
const marker = {
name: value.options.name,
icon: changeIconSizeWpt(removeShadowFromIconWpt(icon), 18, 30, appearance.background),
icon: getFavoriteMenuIconHtml({ wpt, ...appearance }),
layer: value,
color: appearance.color,
background: appearance.background,
Expand All @@ -725,6 +733,37 @@ export function getWptByTitle(title, wpts) {
return wpts.find((wpt) => wpt.name === title);
}

/** Build menu marker + group for opening a favorite from search (requires map markers loaded). */
export function resolveFavoriteMarkerForSearch(ctx, groupId, wptName) {
const group = ctx.favorites?.groups?.find((g) => g.id === groupId);
const mapObj = ctx.favorites?.mapObjs?.[groupId];
if (!group || !mapObj?.wpts || !mapObj?.markers?._layers) {
return null;
}
const wpt = getWptByTitle(wptName, mapObj.wpts);
if (!wpt) {
return null;
}
const layer = Object.values(mapObj.markers._layers).find((l) => l.options?.name === wptName);
if (!layer) {
return null;
}
const appearance = resolveWptAppearance(wpt, mapObj.pointsGroups);
const marker = {
name: wptName,
icon: getFavoriteMenuIconHtml({
wpt,
icon: appearance.icon,
color: appearance.color,
background: appearance.background,
}),
layer,
color: appearance.color,
background: appearance.background,
};
return { group, marker };
}

export function addLocDist({ location, markers = null, wpts = null }) {
let res = [];
if (location && location !== LOCATION_UNAVAILABLE) {
Expand Down Expand Up @@ -774,11 +813,63 @@ export function getFavoriteId(layer) {
return `fav:${lat}:${lng}`;
}

export function openFavoriteObj(ctx, object) {
ctx.setCurrentObjectType(OBJECT_TYPE_FAVORITE);
const selectionId = getFavoriteId(object.markerCurrent?.layer);
ctx.setSelectedWpt({ ...object, selectionId, id: selectionId, groupId: object.id });
ctx.setSelectedGpxFile({ ...object });
export function getSelectedFavoriteObj({
group,
marker,
ctx,
sharedFile = false,
mapObj = false,
openedFolder = undefined,
}) {
const favObj = {};
if (marker?.layer) {
marker.latlng = marker.layer.getLatLng();
}
favObj.markerCurrent = { ...marker, groupId: group.id };
if (!ctx.selectedGpxFile.markerPrev || ctx.selectedGpxFile.markerPrev !== ctx.selectedGpxFile.markerCurrent) {
favObj.markerPrev = ctx.selectedGpxFile.markerCurrent;
}
let trackData;
Object.keys(ctx.favorites.mapObjs).forEach((fileId) => {
if (fileId === group.id) {
favObj.nameGroup = group.name;
Object.values(ctx.favorites.mapObjs[fileId].markers._layers).forEach((m) => {
if (m.options.name === marker.name) {
trackData = ctx.favorites.mapObjs[fileId];
}
});
}
});
favObj.id = group.id;
favObj.key = `${group.id}:${marker.name}`;
favObj.trackData = trackData;
favObj.sharedWithMe = sharedFile;
favObj.file = ctx.favorites.groups.find((g) => g.name === group.name).file;
favObj.name = marker.name;
favObj.prevState = ctx.selectedGpxFile;
favObj.favItem = true;
favObj.mapObj = mapObj;
favObj.openedFolder = openedFolder;

return favObj;
}

export function addFavoriteToMap({ group, marker, ctx, sharedFile = false, mapObj = false, openedFolder = undefined }) {
const favoriteObj = getSelectedFavoriteObj({ group, marker, ctx, sharedFile, mapObj, openedFolder });
openFavoriteObj({ ctx, favoriteObj });
}

export function addFavoriteToMapFromSearch(ctx, { group, marker }) {
const favoriteObj = getSelectedFavoriteObj({ group, marker, ctx, mapObj: false });
ctx.setSelectedSearchObj({ type: OBJECT_TYPE_FAVORITE, object: favoriteObj });
openFavoriteObj({ ctx, favoriteObj, options: { fromSearch: true } });
}

export function openFavoriteObj({ ctx, favoriteObj, options = {} }) {
ctx.setCurrentObjectType(options.fromSearch === true ? OBJECT_SEARCH : OBJECT_TYPE_FAVORITE);
const selectionId = getFavoriteId(favoriteObj.markerCurrent?.layer);
ctx.setSelectedWpt({ ...favoriteObj, selectionId, id: selectionId, groupId: favoriteObj.id });
ctx.setSelectedGpxFile({ ...favoriteObj });
}

export function navigateToFavoritesMenu(navigate, ctx) {
Expand All @@ -798,11 +889,9 @@ const FavoritesManager = {
deleteFavorite,
updateFavorite,
prepareTrackData,
getShapesSvg,
orderList,
getColorGroup,
createGroup,
createDefaultWptGroup,
getGroupSize,
DEFAULT_TAB_ICONS: DEFAULT_TAB_ICONS,
DEFAULT_GROUP_NAME: DEFAULT_FAV_GROUP_NAME,
Expand Down
Loading