mirror of
https://github.com/ckelsoe/obsidian-rss-importer.git
synced 2026-07-22 07:48:56 +00:00
Correctness: resolver classifies /p/ as Substack only on a substack.com host; redirect cap aligned to 5; extractFeedItemId unescapes in a single pass (round trips literal backslash escapes). Feature: podcast/media notes now record media-url frontmatter and append an [Episode audio]/[Media] body link. Safety: the import command guards synchronous NoteWriter construction (a bad destination folder now surfaces a Notice, not a silent throw); loadSettings validates per-feed literal fields from data.json. Diagnosability: empty-body imports and skipped image downloads are logged. Structure: FeedPickerModal and DuplicatePromptModal moved to their own files; id non-empty guard at the mapping boundary; doc + type-dedup cleanups. 244 tests; build + lint green. Deferred (low/cosmetic): shared header helper, Atom updated-vs-published date precedence, the sanctioned settings-tab cast.
66 lines
1.6 KiB
TypeScript
66 lines
1.6 KiB
TypeScript
// Asks the user whether to overwrite, skip, or cancel when a same-item note
|
|
// already exists and the duplicate policy is "prompt".
|
|
|
|
import { App, Modal, Setting } from "obsidian";
|
|
import type {
|
|
DuplicatePromptContext,
|
|
DuplicatePromptDecision,
|
|
} from "./note-writer";
|
|
|
|
export class DuplicatePromptModal extends Modal {
|
|
private readonly context: DuplicatePromptContext;
|
|
private readonly resolve: (decision: DuplicatePromptDecision) => void;
|
|
private decided = false;
|
|
|
|
constructor(
|
|
app: App,
|
|
context: DuplicatePromptContext,
|
|
resolve: (decision: DuplicatePromptDecision) => void,
|
|
) {
|
|
super(app);
|
|
this.context = context;
|
|
this.resolve = resolve;
|
|
}
|
|
|
|
onOpen(): void {
|
|
this.setTitle("Note already exists");
|
|
const { contentEl } = this;
|
|
contentEl.createEl("p", {
|
|
text: `A note for "${this.context.itemTitle}" already exists at ${this.context.targetPath}. Overwrite it?`,
|
|
});
|
|
new Setting(contentEl)
|
|
.addButton((btn) =>
|
|
btn
|
|
.setButtonText("Overwrite")
|
|
.setDestructive()
|
|
.onClick(() => {
|
|
this.settle("overwrite");
|
|
}),
|
|
)
|
|
.addButton((btn) =>
|
|
btn.setButtonText("Skip").onClick(() => {
|
|
this.settle("skip");
|
|
}),
|
|
)
|
|
.addButton((btn) =>
|
|
btn.setButtonText("Cancel import").onClick(() => {
|
|
this.settle("cancel");
|
|
}),
|
|
);
|
|
}
|
|
|
|
onClose(): void {
|
|
// Dismissing the modal without a choice cancels the import.
|
|
this.settle("cancel");
|
|
this.contentEl.empty();
|
|
}
|
|
|
|
private settle(decision: DuplicatePromptDecision): void {
|
|
if (this.decided) {
|
|
return;
|
|
}
|
|
this.decided = true;
|
|
this.resolve(decision);
|
|
this.close();
|
|
}
|
|
}
|