Strip merge-only id suffix when writing project files
This commit is contained in:
+47
-17
@@ -115,6 +115,31 @@ function retagSubtree(node, projectId) {
|
|||||||
walkTree([node], it => { it._projectId = projectId; });
|
walkTree([node], it => { it._projectId = projectId; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disk counterpart of the in-memory de-collision in buildMergedDataFromProjects:
|
||||||
|
// strip the merge-only `~<projectId>` suffix from entry ids, history itemIds and
|
||||||
|
// restore-path segments of THIS project's subset before serializing, so every
|
||||||
|
// project file stays a standard upstream backlog.md (upstream ids never contain
|
||||||
|
// `~`). Only an exact `~<projectId>` ending is stripped — other projects'
|
||||||
|
// suffixes or natural `~` pass through untouched. PURE: the in-memory tree keeps
|
||||||
|
// its suffixed ids for de-collision; deep-safe copies are returned.
|
||||||
|
function toDiskIds(entries, history, projectId) {
|
||||||
|
if (!projectId) return { entries, history };
|
||||||
|
const suffix = '~' + projectId;
|
||||||
|
const strip = id => (typeof id === 'string' && id.endsWith(suffix)) ? id.slice(0, id.length - suffix.length) : id;
|
||||||
|
const mapEntry = it => ({
|
||||||
|
...it,
|
||||||
|
id: strip(it.id),
|
||||||
|
restorePath: typeof it.restorePath === 'string' && it.restorePath
|
||||||
|
? it.restorePath.split('/').map(strip).join('/')
|
||||||
|
: it.restorePath,
|
||||||
|
children: it.children ? it.children.map(mapEntry) : it.children,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
entries: entries.map(mapEntry),
|
||||||
|
history: history.map(h => ({ ...h, itemId: strip(h.itemId) })),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [data, setData] = useStateMain(buildEmptyData);
|
const [data, setData] = useStateMain(buildEmptyData);
|
||||||
const [storageMode, setStorageMode] = useStateMain('local'); // 'api' | 'direct' | 'local'
|
const [storageMode, setStorageMode] = useStateMain('local'); // 'api' | 'direct' | 'local'
|
||||||
@@ -391,7 +416,7 @@ function App() {
|
|||||||
for (const pId of dirtyIds) {
|
for (const pId of dirtyIds) {
|
||||||
const projectEntries = d.entries.filter(e => (e._projectId ?? fallbackFirst) === pId);
|
const projectEntries = d.entries.filter(e => (e._projectId ?? fallbackFirst) === pId);
|
||||||
const projectHistory = d.history.filter(h => (h._projectId ?? fallbackFirst) === pId);
|
const projectHistory = d.history.filter(h => (h._projectId ?? fallbackFirst) === pId);
|
||||||
const content = await Parser.serialize({ entries: projectEntries, history: projectHistory });
|
const content = await Parser.serialize(toDiskIds(projectEntries, projectHistory, pId));
|
||||||
const result = await ApiBackend.saveProject(pId, content);
|
const result = await ApiBackend.saveProject(pId, content);
|
||||||
// Keep SyncPoller in sync with our own save to avoid false external-change triggers.
|
// Keep SyncPoller in sync with our own save to avoid false external-change triggers.
|
||||||
if (result?.checksum) SyncPoller.lastChecksums[pId] = result.checksum;
|
if (result?.checksum) SyncPoller.lastChecksums[pId] = result.checksum;
|
||||||
@@ -863,14 +888,16 @@ function App() {
|
|||||||
return { pid, entries: [], history: [] };
|
return { pid, entries: [], history: [] };
|
||||||
}));
|
}));
|
||||||
for (const { pid, entries: archEntries, history: archHistory } of existing) {
|
for (const { pid, entries: archEntries, history: archHistory } of existing) {
|
||||||
const backlogContent = await Parser.serialize({
|
const backlogContent = await Parser.serialize(toDiskIds(
|
||||||
entries: newEntries.filter(e => pidOf(e) === pid),
|
newEntries.filter(e => pidOf(e) === pid),
|
||||||
history: newHistory.filter(h => pidOf(h) === pid),
|
newHistory.filter(h => pidOf(h) === pid),
|
||||||
});
|
pid,
|
||||||
const archiveContent = await Parser.serialize({
|
));
|
||||||
entries: [...archEntries, ...groups.get(pid)],
|
const archiveContent = await Parser.serialize(toDiskIds(
|
||||||
history: archHistory,
|
[...archEntries, ...groups.get(pid)],
|
||||||
});
|
archHistory,
|
||||||
|
pid,
|
||||||
|
));
|
||||||
const result = await ApiBackend.saveArchive(backlogContent, archiveContent, pid);
|
const result = await ApiBackend.saveArchive(backlogContent, archiveContent, pid);
|
||||||
if (!result.ok) throw new Error('Archive failed');
|
if (!result.ok) throw new Error('Archive failed');
|
||||||
}
|
}
|
||||||
@@ -950,14 +977,16 @@ function App() {
|
|||||||
const fallbackFirst = latestProjects.current[0]?.id ?? null;
|
const fallbackFirst = latestProjects.current[0]?.id ?? null;
|
||||||
const pidOf = x => x._projectId ?? fallbackFirst;
|
const pidOf = x => x._projectId ?? fallbackFirst;
|
||||||
for (const pid of [...new Set(toRestore.map(pidOf))]) {
|
for (const pid of [...new Set(toRestore.map(pidOf))]) {
|
||||||
const backlogContent = await Parser.serialize({
|
const backlogContent = await Parser.serialize(toDiskIds(
|
||||||
entries: newEntries.filter(e => pidOf(e) === pid),
|
newEntries.filter(e => pidOf(e) === pid),
|
||||||
history: newHistory.filter(h => pidOf(h) === pid),
|
newHistory.filter(h => pidOf(h) === pid),
|
||||||
});
|
pid,
|
||||||
const archiveContent = await Parser.serialize({
|
));
|
||||||
entries: remainingArchive.filter(e => pidOf(e) === pid),
|
const archiveContent = await Parser.serialize(toDiskIds(
|
||||||
history: [],
|
remainingArchive.filter(e => pidOf(e) === pid),
|
||||||
});
|
[],
|
||||||
|
pid,
|
||||||
|
));
|
||||||
const result = await ApiBackend.restoreFromArchive(backlogContent, archiveContent, pid);
|
const result = await ApiBackend.restoreFromArchive(backlogContent, archiveContent, pid);
|
||||||
if (!result.ok) throw new Error('Restore failed');
|
if (!result.ok) throw new Error('Restore failed');
|
||||||
}
|
}
|
||||||
@@ -1507,6 +1536,7 @@ Object.assign(window, {
|
|||||||
buildMergedDataFromProjects,
|
buildMergedDataFromProjects,
|
||||||
computeDirtyProjectIds,
|
computeDirtyProjectIds,
|
||||||
retagSubtree,
|
retagSubtree,
|
||||||
|
toDiskIds,
|
||||||
});
|
});
|
||||||
|
|
||||||
window.App = App;
|
window.App = App;
|
||||||
|
|||||||
Reference in New Issue
Block a user