mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 06:11:45 +00:00
feat(places): unified file import modal with drag-and-drop and deduplication
- Replace separate GPX and KML/KMZ import buttons with a single "Import file" modal accepting all three formats, with a drag-and-drop drop zone - Support dragging files directly onto the Places sidebar panel; overlay appears on hover and pre-loads the file into the modal on drop - Fix [object Object] description bug in KML imports caused by fast-xml-parser returning mixed-content nodes as objects; add stopNodes config and object guard in asTrimmedString - Fix CDATA sections leaking into descriptions (e.g. "text.]]>") by unwrapping CDATA markers before tag stripping - Add import deduplication across all import paths (GPX, KML/KMZ, Google list, Naver list): reimporting skips places already in the trip by name (case-insensitive) or by coordinates (within ~11 m tolerance), with intra-batch dedup so duplicate placemarks within the same file are also collapsed - Fix KML route returning 400 "No valid Placemarks found" when all placemarks were valid but deduplicated; 400 now only fires when the file contains zero placemarks - Show a warning toast "All places were already in the trip" instead of a misleading success toast when a reimport produces zero new places (GPX, KML/KMZ, Google list, Naver list) - Add 8 new i18n keys across all 14 locales; remove 11 keys made unused by the modal consolidation
This commit is contained in:
@@ -40,6 +40,13 @@ function asArray<T>(value: T | T[] | null | undefined): T[] {
|
||||
|
||||
function asTrimmedString(value: unknown): string | null {
|
||||
if (value == null) return null;
|
||||
// Parsed objects (mixed-content XML parsed without stopNodes) must not
|
||||
// produce "[object Object]" — extract #text if present, else return null.
|
||||
if (typeof value === 'object') {
|
||||
const candidate = (value as Record<string, unknown>)['#text'];
|
||||
if (typeof candidate === 'string') return candidate.trim() || null;
|
||||
return null;
|
||||
}
|
||||
const text = String(value).trim();
|
||||
return text.length > 0 ? text : null;
|
||||
}
|
||||
@@ -73,7 +80,12 @@ export function sanitizeKmlDescription(value: unknown): string | null {
|
||||
const raw = asTrimmedString(value);
|
||||
if (!raw) return null;
|
||||
|
||||
const withLineBreaks = raw.replace(/<br\s*\/?>/gi, '\n');
|
||||
// Unwrap CDATA sections — present when fast-xml-parser returns raw node text
|
||||
// via stopNodes. Must happen before tag-stripping so the CDATA markers are
|
||||
// not mis-parsed by the <[^>]+> regex.
|
||||
const withoutCdata = raw.replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, '$1');
|
||||
|
||||
const withLineBreaks = withoutCdata.replace(/<br\s*\/?>/gi, '\n');
|
||||
const stripped = withLineBreaks.replace(/<[^>]+>/g, '');
|
||||
const decoded = decodeHtmlEntities(stripped)
|
||||
.replace(/\r\n/g, '\n')
|
||||
|
||||
Reference in New Issue
Block a user