diff --git a/README.md b/README.md
index 15de874..8cd0d3d 100644
--- a/README.md
+++ b/README.md
@@ -18,12 +18,11 @@ This plugin imports your saved [Omnivore](https://omnivore.app/) articles and hi
## Usage
-1. The plugin will automatically sync with Omnivore every time you open the plugin and every time you change the settings
-2. You can also manually sync with Omnivore by clicking the Omnivore icon on the ribbon
-3. You can also change the API key, the search filter, and how often the plugin syncs with Omnivore by updating the settings
-4. The plugin creates a new page for each saved article including metadata, labels. Content you have highlighted in Omnivore, and any notes you added, will be nested in the article page
-5. Clicking on the article will open the Omnivore article in a new tab
-6. We also create an internal link to each label in the article so you can group articles by label
+1. The plugin will sync with Omnivore when you click on Omnivore ribbon icon or use the palette command
+2. You can also change the API key, the search filter, and how often the plugin syncs with Omnivore by updating the settings
+3. The plugin creates a new page for each saved article including metadata, labels. Content you have highlighted in Omnivore, and any notes you added, will be nested in the article page
+4. Clicking on the article will open the Omnivore article in a new tab
+5. We also create an internal link to each label in the article so you can group articles by label
## Contacts
diff --git a/src/main.ts b/src/main.ts
index 94565c5..c1c296b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -8,6 +8,8 @@ import {
Plugin,
PluginSettingTab,
Setting,
+ TFile,
+ TFolder,
} from "obsidian";
import {
Article,
@@ -21,7 +23,6 @@ import {
} from "./util";
import { FolderSuggest } from "./settings/file-suggest";
-// Remember to rename these classes and interfaces!
enum Filter {
ALL = "import all my articles",
HIGHLIGHTS = "import just highlights",
@@ -102,7 +103,7 @@ export default class OmnivorePlugin extends Plugin {
await this.loadSettings();
this.addCommand({
- id: "omnivore-sync",
+ id: "sync",
name: "Sync",
callback: () => {
this.fetchOmnivore();
@@ -110,7 +111,7 @@ export default class OmnivorePlugin extends Plugin {
});
this.addCommand({
- id: "omnivore-resync",
+ id: "resync",
name: "Resync all articles",
callback: () => {
this.settings.syncAt = "";
@@ -201,9 +202,10 @@ export default class OmnivorePlugin extends Plugin {
this.settings.dateFormat
);
const folderName = `${folder}/${dateSaved}`;
- if (
- !(await this.app.vault.adapter.exists(normalizePath(folderName)))
- ) {
+ const omnivoreFolder = app.vault.getAbstractFileByPath(
+ normalizePath(folderName)
+ );
+ if (!(omnivoreFolder instanceof TFolder)) {
await this.app.vault.createFolder(folderName);
}
@@ -245,16 +247,6 @@ export default class OmnivorePlugin extends Plugin {
};
});
- // // use template from file if specified
- // let templateToUse = template;
- // if (templateFileLocation) {
- // const templateFile =
- // this.app.vault.getAbstractFileByPath(templateFileLocation);
- // if (templateFile) {
- // templateToUse = await this.app.vault.read(templateFile as TFile);
- // }
- // }
-
// Build content string based on template
const content = Mustache.render(template, {
title: article.title,
@@ -272,7 +264,16 @@ export default class OmnivorePlugin extends Plugin {
content: article.content,
});
- await this.app.vault.adapter.write(normalizePath(pageName), content);
+ const normalizedPath = normalizePath(pageName);
+ const omnivoreFile = app.vault.getAbstractFileByPath(normalizedPath);
+ if (omnivoreFile instanceof TFile) {
+ const existingContent = await this.app.vault.read(omnivoreFile);
+ if (existingContent !== content) {
+ await this.app.vault.modify(omnivoreFile, content);
+ }
+ } else {
+ await this.app.vault.create(normalizedPath, content);
+ }
}
}
@@ -317,17 +318,12 @@ class OmnivoreSettingTab extends PluginSettingTab {
this.plugin = plugin;
}
- private static createFragmentWithHTML = (html: string) =>
- createFragment(
- (documentFragment) => (documentFragment.createDiv().innerHTML = html)
- );
-
display(): void {
const { containerEl } = this;
containerEl.empty();
- containerEl.createEl("h2", { text: "Settings for omnivore plugin" });
+ containerEl.createEl("h2", { text: "Settings for Omnivore plugin" });
// create a group of general settings
containerEl.createEl("h3", {
@@ -342,16 +338,21 @@ class OmnivoreSettingTab extends PluginSettingTab {
new Setting(generalSettings)
.setName("API Key")
.setDesc(
- OmnivoreSettingTab.createFragmentWithHTML(
- "You can create an API key at https://omnivore.app/settings/api"
- )
+ createFragment((fragment) => {
+ fragment.append(
+ "You can create an API key at ",
+ fragment.createEl("a", {
+ text: "https://omnivore.app/settings/api",
+ href: "https://omnivore.app/settings/api",
+ })
+ );
+ })
)
.addText((text) =>
text
.setPlaceholder("Enter your Omnivore Api Key")
.setValue(this.plugin.settings.apiKey)
.onChange(async (value) => {
- console.log("apiKey: " + value);
this.plugin.settings.apiKey = value;
await this.plugin.saveSettings();
})
@@ -365,7 +366,6 @@ class OmnivoreSettingTab extends PluginSettingTab {
dropdown
.setValue(this.plugin.settings.filter)
.onChange(async (value) => {
- console.log("filter: " + value);
this.plugin.settings.filter = value;
await this.plugin.saveSettings();
});
@@ -374,9 +374,16 @@ class OmnivoreSettingTab extends PluginSettingTab {
new Setting(generalSettings)
.setName("Custom query")
.setDesc(
- OmnivoreSettingTab.createFragmentWithHTML(
- "See https://omnivore.app/help/search for more info on search query syntax"
- )
+ createFragment((fragment) => {
+ fragment.append(
+ "See ",
+ fragment.createEl("a", {
+ text: "https://omnivore.app/help/search",
+ href: "https://omnivore.app/help/search",
+ }),
+ " for more info on search query syntax"
+ );
+ })
)
.addText((text) =>
text
@@ -385,7 +392,6 @@ class OmnivoreSettingTab extends PluginSettingTab {
)
.setValue(this.plugin.settings.customQuery)
.onChange(async (value) => {
- console.log("query: " + value);
this.plugin.settings.customQuery = value;
await this.plugin.saveSettings();
})
@@ -400,7 +406,6 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.syncAt)
.setDefaultFormat("yyyy-MM-dd'T'HH:mm:ss")
.onChange(async (value) => {
- console.log("syncAt: " + value);
this.plugin.settings.syncAt = value;
await this.plugin.saveSettings();
})
@@ -414,7 +419,6 @@ class OmnivoreSettingTab extends PluginSettingTab {
dropdown
.setValue(this.plugin.settings.highlightOrder)
.onChange(async (value) => {
- console.log("highlightOrder: " + value);
this.plugin.settings.highlightOrder = value;
await this.plugin.saveSettings();
});
@@ -423,35 +427,26 @@ class OmnivoreSettingTab extends PluginSettingTab {
new Setting(generalSettings)
.setName("Template")
.setDesc(
- OmnivoreSettingTab.createFragmentWithHTML(
- `Enter template to render articles with. Reference`
- )
+ createFragment((fragment) => {
+ fragment.append(
+ "Enter template to render articles with ",
+ fragment.createEl("a", {
+ text: "Reference",
+ href: "https://github.com/janl/mustache.js/#templates",
+ })
+ );
+ })
)
.addTextArea((text) =>
text
.setPlaceholder("Enter the template")
.setValue(this.plugin.settings.template)
.onChange(async (value) => {
- console.log("template: " + value);
this.plugin.settings.template = value;
await this.plugin.saveSettings();
})
);
- // new Setting(generalSettings)
- // .setName("Template file location")
- // .setDesc("Choose the file to use as the template")
- // .addSearch((search) => {
- // new FileSuggest(this.app, search.inputEl);
- // search
- // .setPlaceholder("Enter the file path")
- // .setValue(this.plugin.settings.templateFileLocation)
- // .onChange(async (value) => {
- // this.plugin.settings.templateFileLocation = value;
- // await this.plugin.saveSettings();
- // });
- // });
-
new Setting(generalSettings)
.setName("Folder")
.setDesc("Enter the folder where the data will be stored")
@@ -478,9 +473,15 @@ class OmnivoreSettingTab extends PluginSettingTab {
new Setting(generalSettings)
.setName("Date Format")
.setDesc(
- OmnivoreSettingTab.createFragmentWithHTML(
- 'Enter the format date for use in rendered template.\nFormat reference.'
- )
+ createFragment((fragment) => {
+ fragment.append(
+ "Enter the format date for use in rendered template.\nFormat ",
+ fragment.createEl("a", {
+ text: "reference",
+ href: "https://moment.github.io/luxon/#/formatting?id=table-of-tokens",
+ })
+ );
+ })
)
.addText((text) =>
text
diff --git a/src/settings/file-suggest.ts b/src/settings/file-suggest.ts
index e071b9f..b5b5463 100644
--- a/src/settings/file-suggest.ts
+++ b/src/settings/file-suggest.ts
@@ -1,3 +1,5 @@
+// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
+
import { TAbstractFile, TFile, TFolder } from "obsidian";
import { TextInputSuggest } from "./suggest";
diff --git a/src/settings/suggest.ts b/src/settings/suggest.ts
index a487ac4..fc0f29d 100644
--- a/src/settings/suggest.ts
+++ b/src/settings/suggest.ts
@@ -1,3 +1,5 @@
+// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
+
import { createPopper, type Instance as PopperInstance } from "@popperjs/core";
import { App, type ISuggestOwner, Scope } from "obsidian";
import { wrapAround } from "../util";
diff --git a/src/util.ts b/src/util.ts
index 7f2e446..cec47aa 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -1,6 +1,7 @@
import { diff_match_patch } from "diff-match-patch";
import { DateTime } from "luxon";
import escape from "markdown-escape";
+import { requestUrl } from "obsidian";
export const DATE_FORMAT_W_OUT_SECONDS = "yyyy-MM-dd'T'HH:mm";
export const DATE_FORMAT = `${DATE_FORMAT_W_OUT_SECONDS}:ss`;
@@ -94,12 +95,13 @@ export const loadArticle = async (
slug: string,
apiKey: string
): Promise => {
- const res = await fetch(ENDPOINT, {
+ const res = await requestUrl({
+ url: ENDPOINT,
headers: requestHeaders(apiKey),
body: `{"query":"\\n query GetArticle(\\n $username: String!\\n $slug: String!\\n ) {\\n article(username: $username, slug: $slug) {\\n ... on ArticleSuccess {\\n article {\\n ...ArticleFields\\n highlights {\\n ...HighlightFields\\n }\\n labels {\\n ...LabelFields\\n }\\n }\\n }\\n ... on ArticleError {\\n errorCodes\\n }\\n }\\n }\\n \\n fragment ArticleFields on Article {\\n savedAt\\n }\\n\\n \\n fragment HighlightFields on Highlight {\\n id\\n quote\\n annotation\\n }\\n\\n \\n fragment LabelFields on Label {\\n name\\n }\\n\\n","variables":{"username":"me","slug":"${slug}"}}`,
method: "POST",
});
- const response = (await res.json()) as GetArticleResponse;
+ const response = res.json as GetArticleResponse;
return response.data.article.article;
};
@@ -114,7 +116,8 @@ export const loadArticles = async (
includeContent = false,
format = "html"
): Promise<[Article[], boolean]> => {
- const res = await fetch(endpoint, {
+ const res = await requestUrl({
+ url: endpoint,
headers: requestHeaders(apiKey),
body: `{"query":"\\n query Search($after: String, $first: Int, $query: String, $includeContent: Boolean, $format: String) {\\n search(first: $first, after: $after, query: $query, includeContent: $includeContent, format: $format) {\\n ... on SearchSuccess {\\n edges {\\n node {\\n title\\n slug\\n siteName\\n originalArticleUrl\\n url\\n author\\n updatedAt\\n description\\n savedAt\\n pageType\\n content\\n highlights {\\n id\\n quote\\n annotation\\n patch\\n updatedAt\\n }\\n labels {\\n name\\n }\\n }\\n }\\n pageInfo {\\n hasNextPage\\n }\\n }\\n ... on SearchError {\\n errorCodes\\n }\\n }\\n }\\n ","variables":{"after":"${after}","first":${first}, "query":"${
updatedAt ? "updated:" + updatedAt : ""
@@ -122,7 +125,7 @@ export const loadArticles = async (
method: "POST",
});
- const jsonRes = (await res.json()) as SearchResponse;
+ const jsonRes = res.json as SearchResponse;
const articles = jsonRes.data.search.edges.map((e) => e.node);
return [articles, jsonRes.data.search.pageInfo.hasNextPage];
@@ -135,7 +138,8 @@ export const loadDeletedArticleSlugs = async (
first = 10,
updatedAt = ""
): Promise<[string[], boolean]> => {
- const res = await fetch(endpoint, {
+ const res = await requestUrl({
+ url: endpoint,
headers: requestHeaders(apiKey),
body: `{"query":"\\n query UpdatesSince($after: String, $first: Int, $since: Date!) {\\n updatesSince(first: $first, after: $after, since: $since) {\\n ... on UpdatesSinceSuccess {\\n edges {\\n updateReason\\n node {\\n slug\\n }\\n }\\n pageInfo {\\n hasNextPage\\n }\\n }\\n ... on UpdatesSinceError {\\n errorCodes\\n }\\n }\\n }\\n ","variables":{"after":"${after}","first":${first}, "since":"${
updatedAt || "2021-01-01"
@@ -143,7 +147,7 @@ export const loadDeletedArticleSlugs = async (
method: "POST",
});
- const jsonRes = (await res.json()) as UpdatesSinceResponse;
+ const jsonRes = res.json as UpdatesSinceResponse;
const deletedArticleSlugs = jsonRes.data.updatesSince.edges
.filter((edge) => edge.updateReason === UpdateReason.DELETED)
.map((edge) => edge.node.slug);