diff --git a/website/src/lib/components/file-list/FileList.ts b/website/src/lib/components/file-list/FileList.ts index ac5f4be4..5c18a6e7 100644 --- a/website/src/lib/components/file-list/FileList.ts +++ b/website/src/lib/components/file-list/FileList.ts @@ -1,8 +1,9 @@ import { dbUtils, fileObservers } from "$lib/db"; -import { castDraft } from "immer"; +import { castDraft, freeze } from "immer"; import { Track, TrackSegment, Waypoint } from "gpx"; import { selection } from "./Selection"; import { get } from "svelte/store"; +import { newGPXFile } from "$lib/stores"; export enum ListLevel { ROOT, @@ -273,7 +274,7 @@ export function moveItems(fromParent: ListItem, toParent: ListItem, fromItems: L } }); - dbUtils.applyEachToFiles([fromParent.getFileId(), toParent.getFileId()], [ + dbUtils.applyEachToFilesAndGlobal([fromParent.getFileId(), toParent.getFileId()], [ (file, context: (Track | TrackSegment | Waypoint[] | Waypoint)[]) => { let newFile = file; fromItems.forEach((item) => { @@ -301,15 +302,27 @@ export function moveItems(fromParent: ListItem, toParent: ListItem, fromItems: L (file, context: (Track | TrackSegment | Waypoint[] | Waypoint)[]) => { let newFile = file; toItems.forEach((item, i) => { - if (item instanceof ListTrackItem && context[i] instanceof Track) { - let [result, _removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [context[i]]); - newFile = castDraft(result); + if (item instanceof ListTrackItem) { + if (context[i] instanceof Track) { + let [result, _removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [context[i]]); + newFile = castDraft(result); + } else if (context[i] instanceof TrackSegment) { + let [result, _removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [new Track({ + trkseg: [context[i]] + })]); + newFile = castDraft(result); + } } else if (item instanceof ListTrackSegmentItem && context[i] instanceof TrackSegment) { let [result, _removed] = newFile.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex() - 1, [context[i]]); newFile = castDraft(result); - } else if (item instanceof ListWaypointsItem && Array.isArray(context[i]) && context[i].length > 0 && context[i][0] instanceof Waypoint) { - let [result, _removed] = newFile.replaceWaypoints(newFile.wpt.length, newFile.wpt.length - 1, context[i]); - newFile = castDraft(result); + } else if (item instanceof ListWaypointsItem) { + if (Array.isArray(context[i]) && context[i].length > 0 && context[i][0] instanceof Waypoint) { + let [result, _removed] = newFile.replaceWaypoints(newFile.wpt.length, newFile.wpt.length - 1, context[i]); + newFile = castDraft(result); + } else if (context[i] instanceof Waypoint) { + let [result, _removed] = newFile.replaceWaypoints(newFile.wpt.length, newFile.wpt.length - 1, [context[i]]); + newFile = castDraft(result); + } } else if (item instanceof ListWaypointItem && context[i] instanceof Waypoint) { let [result, _removed] = newFile.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex() - 1, [context[i]]); newFile = castDraft(result); @@ -317,13 +330,26 @@ export function moveItems(fromParent: ListItem, toParent: ListItem, fromItems: L }); return newFile; } - ], []); - - selection.update(($selection) => { - $selection.clear(); - toItems.forEach((item) => { - $selection.set(item, true); + ], (files, context: (Track | TrackSegment | Waypoint[] | Waypoint)[]) => { + toItems.forEach((item, i) => { + if (item instanceof ListFileItem) { + if (context[i] instanceof Track) { + let newFile = newGPXFile(); + newFile._data.id = item.getFileId(); + if (context[i].name) { + newFile.metadata.name = context[i].name; + } + newFile = newFile.replaceTracks(0, 0, [context[i]])[0]; + files.set(item.getFileId(), freeze(newFile)); + } else if (context[i] instanceof TrackSegment) { + let newFile = newGPXFile(); + newFile._data.id = item.getFileId(); + newFile = newFile.replaceTracks(0, 0, [new Track({ + trkseg: [context[i]] + })])[0]; + files.set(item.getFileId(), freeze(newFile)); + } + } }); - return $selection; - }); + }, []); } diff --git a/website/src/lib/components/file-list/FileListNodeContent.svelte b/website/src/lib/components/file-list/FileListNodeContent.svelte index b2924727..d3a134bf 100644 --- a/website/src/lib/components/file-list/FileListNodeContent.svelte +++ b/website/src/lib/components/file-list/FileListNodeContent.svelte @@ -1,12 +1,25 @@ + +
{#if node instanceof Map} {#each node as [fileId, file] (fileId)} diff --git a/website/src/lib/components/file-list/Selection.ts b/website/src/lib/components/file-list/Selection.ts index 5f4e8969..0df7c316 100644 --- a/website/src/lib/components/file-list/Selection.ts +++ b/website/src/lib/components/file-list/Selection.ts @@ -1,5 +1,5 @@ import { get, writable } from "svelte/store"; -import { ListFileItem, ListItem, ListRootItem, ListTrackItem, ListTrackSegmentItem, ListWaypointItem, type ListLevel, sortItems } from "./FileList"; +import { ListFileItem, ListItem, ListRootItem, ListTrackItem, ListTrackSegmentItem, ListWaypointItem, type ListLevel, sortItems, ListWaypointsItem } from "./FileList"; import { fileObservers, settings } from "$lib/db"; export class SelectionTreeType { @@ -205,7 +205,7 @@ export function applyToOrderedSelectedItemsFromFile(callback: (fileId: string, l get(selection).forEach((item) => { if (item.getFileId() === fileId) { level = item.level; - if (item instanceof ListFileItem || item instanceof ListTrackItem || item instanceof ListTrackSegmentItem || item instanceof ListWaypointItem) { + if (item instanceof ListFileItem || item instanceof ListTrackItem || item instanceof ListTrackSegmentItem || item instanceof ListWaypointsItem || item instanceof ListWaypointItem) { items.push(item); } } diff --git a/website/src/lib/db.ts b/website/src/lib/db.ts index 6985d5df..dec679ca 100644 --- a/website/src/lib/db.ts +++ b/website/src/lib/db.ts @@ -187,17 +187,13 @@ function dexieGPXFileStore(id: string): Readable & { dest } // Add/update the files to the database -function updateDbFiles(files: (GPXFile | undefined)[], add: boolean = false) { +function updateDbFiles(files: (GPXFile | undefined)[]) { let filteredFiles = files.filter(file => file !== undefined) as GPXFile[]; let fileIds = filteredFiles.map(file => file._data.id); - if (add) { - return db.transaction('rw', db.fileids, db.files, async () => { - await db.fileids.bulkAdd(fileIds, fileIds); - await db.files.bulkAdd(filteredFiles, fileIds); - }); - } else { - return db.files.bulkPut(filteredFiles, fileIds); - } + return db.transaction('rw', db.fileids, db.files, async () => { + await db.fileids.bulkPut(fileIds, fileIds); + await db.files.bulkPut(filteredFiles, fileIds); + }); } // Delete the files with the given ids from the database @@ -210,9 +206,7 @@ function deleteDbFiles(fileIds: string[]) { // Commit the changes to the file state to the database function commitFileStateChange(newFileState: ReadonlyMap, patch: Patch[]) { - if (newFileState.size > fileState.size) { - return updateDbFiles(getChangedFileIds(patch).map((fileId) => newFileState.get(fileId)), true); - } else if (newFileState.size === fileState.size) { + if (newFileState.size >= fileState.size) { return updateDbFiles(getChangedFileIds(patch).map((fileId) => newFileState.get(fileId))); } else { return deleteDbFiles(getChangedFileIds(patch)); @@ -252,6 +246,20 @@ liveQuery(() => db.fileids.toArray()).subscribe(dbFileIds => { return $selection; }); } + settings.fileOrder.update((order) => { + newFiles.forEach((fileId) => { + if (!order.includes(fileId)) { + order.push(fileId); + } + }); + deletedFiles.forEach((fileId) => { + let index = order.indexOf(fileId); + if (index !== -1) { + order.splice(index, 1); + } + }); + return order; + }); } }); @@ -292,7 +300,7 @@ function applyToFiles(fileIds: string[], callback: (file: WritableDraft } // Helper function to apply different callbacks to multiple files -function applyEachToFiles(fileIds: string[], callbacks: ((file: WritableDraft, context?: any) => GPXFile)[], context?: any) { +function applyEachToFilesAndGlobal(fileIds: string[], callbacks: ((file: WritableDraft, context?: any) => GPXFile)[], globalCallback: (files: Map, context?: any) => void, context?: any) { const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => { fileIds.forEach((fileId, index) => { let file = draft.get(fileId); @@ -300,6 +308,7 @@ function applyEachToFiles(fileIds: string[], callbacks: ((file: WritableDraft) => GPXFile) => { applyToFiles(ids, callback); }, - applyEachToFiles: (ids: string[], callbacks: ((file: WritableDraft, context?: any) => GPXFile)[], context?: any) => { - applyEachToFiles(ids, callbacks, context); + applyEachToFilesAndGlobal: (ids: string[], callbacks: ((file: WritableDraft, context?: any) => GPXFile)[], globalCallback: (files: Map, context?: any) => void, context?: any) => { + applyEachToFilesAndGlobal(ids, callbacks, globalCallback, context); }, applyToSelection: (callback: (file: WritableDraft) => AnyGPXTreeElement) => { if (get(selection).size === 0) { diff --git a/website/src/lib/stores.ts b/website/src/lib/stores.ts index b7123f10..b3ae4e16 100644 --- a/website/src/lib/stores.ts +++ b/website/src/lib/stores.ts @@ -127,9 +127,14 @@ export enum Tool { } export const currentTool = writable(null); -export function createFile() { +export function newGPXFile() { let file = new GPXFile(); file.metadata.name = get(_)("menu.new_filename"); + return file; +} + +export function createFile() { + let file = newGPXFile(); dbUtils.add(file);