fix(import): say where a document was already imported, and to what

"Already imported" left the reader to guess what had been compared —
names, paths, contents — when the answer is the source keys, the same
overlap that decides whether a re-import merges. The title now names the
workspace holding it and the detail says what importing here would do.

The destination row also stopped wedging the kind and the name into one
value: the row's label carries the kind, so the value is just the name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Gregory Schier
2026-09-10 09:31:50 -07:00
co-authored by Claude Opus 5
parent 0f939a5a03
commit 3084882829
2 changed files with 20 additions and 13 deletions
@@ -11,7 +11,7 @@ import { platform } from "@yaakapp-internal/platform";
import classNames from "classnames"; import classNames from "classnames";
import { formatDistanceToNowStrict } from "date-fns"; import { formatDistanceToNowStrict } from "date-fns";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { pluralize, pluralizeCount } from "../lib/pluralize"; import { pluralize } from "../lib/pluralize";
import { CommercialUseBanner } from "./CommercialUseBanner"; import { CommercialUseBanner } from "./CommercialUseBanner";
import { Button } from "./core/Button"; import { Button } from "./core/Button";
import { Checkbox } from "./core/Checkbox"; import { Checkbox } from "./core/Checkbox";
@@ -308,17 +308,21 @@ function LoadedImportDataDialog({
return item.selected; return item.selected;
}).length; }).length;
const destinationLabel = (() => { // The row's label carries what kind of destination it is, so the value can just be its name
const [destinationLabel, destinationValue] = ((): [string, string] => {
if (plan.destination.type === "new_workspace") { if (plan.destination.type === "new_workspace") {
const names = plan.resources.workspaces.map((w) => w.name).filter((n) => n !== ""); const names = plan.resources.workspaces.map((w) => w.name).filter((n) => n !== "");
if (names.length > 1) return pluralizeCount("new workspace", names.length); if (names.length === 0) return ["New workspace", "Untitled"];
return names[0] == null ? "New workspace" : `New workspace · ${names[0]}`; return [pluralize("New workspace", names.length), names.join(", ")];
} }
const { workspaceId, folderId } = plan.destination; const { workspaceId, folderId } = plan.destination;
const name = workspaces.find((w) => w.id === workspaceId)?.name ?? "Unknown workspace"; const name = workspaces.find((w) => w.id === workspaceId)?.name ?? "Unknown workspace";
return folderId != null && folderId === selectedFolder?.id return [
"Destination",
folderId != null && folderId === selectedFolder?.id
? `${name} / ${selectedFolder.name}` ? `${name} / ${selectedFolder.name}`
: name; : name,
];
})(); })();
// The destination workspace roots the tree. It is not a plan item — commit always applies // The destination workspace roots the tree. It is not a plan item — commit always applies
@@ -345,7 +349,7 @@ function LoadedImportDataDialog({
<VStack space={4} className="pb-4"> <VStack space={4} className="pb-4">
<div className="rounded-lg border border-border-subtle divide-y divide-border-subtle"> <div className="rounded-lg border border-border-subtle divide-y divide-border-subtle">
<PreviewRow label="Detected format" value={plan.importer} /> <PreviewRow label="Detected format" value={plan.importer} />
<PreviewRow label="Destination" value={destinationLabel} /> <PreviewRow label={destinationLabel} value={destinationValue} />
</div> </div>
{plan.warnings.map((warning) => ( {plan.warnings.map((warning) => (
+8 -5
View File
@@ -331,8 +331,8 @@ fn warn_if_imported_elsewhere(db: &ClientDb, plan: &mut ImportPlan) -> Result<()
} }
let names = names.iter().map(String::as_str).collect::<BTreeSet<_>>(); let names = names.iter().map(String::as_str).collect::<BTreeSet<_>>();
plan.warnings.push(ImportPlanWarning::warning( plan.warnings.push(ImportPlanWarning::warning(
"Already imported", format!("This document is already imported into {}", display_list(&names)),
format!("{} · importing here makes a second copy", display_list(&names)), "Importing it here makes a second copy instead of updating that one",
)); ));
Ok(()) Ok(())
} }
@@ -1758,14 +1758,17 @@ mod tests {
let warning = elsewhere let warning = elsewhere
.warnings .warnings
.iter() .iter()
.find(|w| w.title == "Already imported") .find(|w| w.title == "This document is already imported into Imported")
.expect("copying a document that already landed somewhere is called out"); .expect("copying a document that already landed somewhere is called out");
assert_eq!(warning.detail, "Imported · importing here makes a second copy"); assert_eq!(
warning.detail,
"Importing it here makes a second copy instead of updating that one"
);
// Merging back into the workspace it created is the whole point, so it says nothing. // Merging back into the workspace it created is the whole point, so it says nothing.
let merging = replan(&query_manager, &workspace_id, imported_resources()); let merging = replan(&query_manager, &workspace_id, imported_resources());
assert!( assert!(
merging.warnings.iter().all(|w| w.title != "Already imported"), merging.warnings.iter().all(|w| !w.title.starts_with("This document is already")),
"{:?}", "{:?}",
merging.warnings merging.warnings
); );