Skip to content

Commit

Permalink
- Context menu on articles and shortcuts (closes #15)
Browse files Browse the repository at this point in the history
- new template variables (closes #14)
- new command to refresh articles(closes #16)
- new setting: new file location (closes #10)
  • Loading branch information
joethei committed Oct 30, 2021
1 parent 4d975db commit 567a2fe
Show file tree
Hide file tree
Showing 22 changed files with 514 additions and 144 deletions.
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "rss-reader",
"name": "RSS Reader",
"version": "0.4.0",
"version": "0.5.0",
"minAppVersion": "0.9.12",
"description": "Read RSS Feeds from within obsidian",
"author": "Johannes Theiner",
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rss-reader",
"version": "0.4.0",
"version": "0.5.0",
"description": "Read RSS Feeds from inside obsidian",
"main": "main.js",
"scripts": {
Expand All @@ -9,9 +9,10 @@
"lint": "eslint . --ext .ts"
},
"keywords": [],
"author": "",
"author": "Johannes Theiner",
"license": "MIT",
"devDependencies": {
"@popperjs/core": "^2.10.2",
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.2.1",
Expand Down
28 changes: 0 additions & 28 deletions src/ItemView.svelte

This file was deleted.

73 changes: 73 additions & 0 deletions src/functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {RssFeedItem} from "./parser/rssParser";
import {htmlToMarkdown, MarkdownView, normalizePath, Notice, TextComponent} from "obsidian";
import {TextInputPrompt} from "./modals/TextInputPrompt";
import {FILE_NAME_REGEX} from "./consts";
import {isInVault} from "obsidian-community-lib";
import RssReaderPlugin from "./main";

export async function createNewNote(plugin: RssReaderPlugin, item: RssFeedItem) : Promise<void> {
const activeFile = plugin.app.workspace.getActiveFile();
let dir = plugin.app.fileManager.getNewFileParent(activeFile ? activeFile.path : "").name;

if(plugin.settings.saveLocation === "custom") {
dir = plugin.settings.saveLocationFolder;
}
//make sure there are no slashes in the title.
const title = item.title.replace(/[\/\\:]/g, ' ');
const content = htmlToMarkdown(item.content);

const appliedTemplate = plugin.settings.template
.replace("{{title}}", item.title)
.replace("{{link}}", item.link)
.replace("{{author}}", item.creator)
.replace("{{published}}", item.pubDate)
.replace("{{folder}}", item.folder)
.replace("{{feed}}", item.feed)
.replace("{{content}}", content);

const inputPrompt = new TextInputPrompt(plugin.app, "Please specify a file name", "cannot contain: * \" \\ / < > : | ?", title, title);

await inputPrompt
.openAndGetValue(async (text: TextComponent) => {
const value = text.getValue();
if(value.match(FILE_NAME_REGEX)) {
inputPrompt.setValidationError(text, "that filename is not valid");
return;
}
const filePath = normalizePath([dir, `${value}.md`].join('/'));

if (isInVault(plugin.app, filePath, '')) {
inputPrompt.setValidationError(text, "there is already a note with that name");
return;
}
inputPrompt.close();

const file = await plugin.app.vault.create(filePath, appliedTemplate);

await plugin.app.workspace.activeLeaf.openFile(file, {
state: {mode: 'edit'},
})
new Notice("Created note from article");
});
}

export async function pasteToNote(plugin: RssReaderPlugin, item: RssFeedItem) : Promise<void> {
const file = plugin.app.workspace.getActiveFile();
if (file === null) {
new Notice("no file active");
return;
}

const view = plugin.app.workspace.getActiveViewOfType(MarkdownView);
if (view) {
const editor = view.editor;
editor.replaceRange(htmlToMarkdown(item.content), editor.getCursor());
new Notice("inserted article into note");
}
}

export function openInBrowser(item: RssFeedItem) : void {
if (typeof item.link === "string") {
window.open(item.link, '_blank');
}
}
14 changes: 11 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Plugin, WorkspaceLeaf} from 'obsidian';
import {DEFAULT_SETTINGS, RssFeed, RssReaderSettings, RSSReaderSettingsTab} from "./settings";
import ViewLoader from "./ViewLoader";
import {DEFAULT_SETTINGS, RssFeed, RssReaderSettings, RSSReaderSettingsTab} from "./settings/settings";
import ViewLoader from "./view/ViewLoader";
import {
favoritesStore,
configuredFeedsStore,
Expand All @@ -11,7 +11,7 @@ import {
FeedItems
} from "./stores";
import {VIEW_ID} from "./consts";
import {getFeedItems, RssFeedMap} from "./rssParser";
import {getFeedItems, RssFeedMap} from "./parser/rssParser";
import {addFeatherIcon} from "obsidian-community-lib";
import groupBy from "lodash.groupby";

Expand Down Expand Up @@ -57,6 +57,14 @@ export default class RssReaderPlugin extends Plugin {
}
});*/

this.addCommand({
id: 'rss-refresh',
name: 'Refresh feeds',
callback: async () => {
await this.updateFeeds();
}
});

this.registerView(VIEW_ID, (leaf: WorkspaceLeaf) => (this.view = new ViewLoader(leaf, this)));

this.addSettingTab(new RSSReaderSettingsTab(this.app, this));
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions src/ImportModal.ts → src/modals/ImportModal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {App, Modal, Notice, Setting} from "obsidian";
import {loadFeedsFromString} from "./opmlParser";
import RssReaderPlugin from "./main";
import {loadFeedsFromString} from "../parser/opmlParser";
import RssReaderPlugin from "../main";

export class ImportModal extends Modal {
importData: string;
Expand Down
75 changes: 11 additions & 64 deletions src/ItemModal.ts → src/modals/ItemModal.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import {
ButtonComponent,
htmlToMarkdown,
MarkdownView,
Modal,
normalizePath,
Notice, TextComponent,
Notice,
} from "obsidian";
import {RssFeedItem} from "./rssParser";
import {favoritesStore, FeedItems, readStore} from "./stores";
import RssReaderPlugin from "./main";
import {RssFeedItem} from "../parser/rssParser";
import {favoritesStore, FeedItems, readStore} from "../stores";
import RssReaderPlugin from "../main";
import {get} from "svelte/store";
import {copy, isInVault} from "obsidian-community-lib";
import {FILE_NAME_REGEX, sanitizeHTMLToDom} from "./consts";
import {TextInputPrompt} from "./TextInputPrompt";
import {copy} from "obsidian-community-lib";
import {sanitizeHTMLToDom} from "../consts";
import {createNewNote, openInBrowser, pasteToNote} from "../functions";

export class ItemModal extends Modal {

Expand Down Expand Up @@ -104,66 +102,15 @@ export class ItemModal extends Modal {
});

new ButtonComponent(topButtons).setTooltip("open in browser").setIcon("open-elsewhere-glyph").onClick(() => {
if (typeof this.item.link === "string") {
window.open(this.item.link, '_blank');
}
openInBrowser(this.item);
});

new ButtonComponent(topButtons).setTooltip("Add as new note").setIcon("create-new").onClick(async () => {
const activeFile = this.app.workspace.getActiveFile();
const dir = this.app.fileManager.getNewFileParent(activeFile ? activeFile.path : "").name;
//make sure there are now slashes in the title.
const title = this.item.title.replace(/[\/\\:]/g, ' ');
const content = htmlToMarkdown(this.item.content);

const appliedTemplate = this.plugin.settings.template
.replace("{{title}}", this.item.title)
.replace("{{link}}", this.item.link)
.replace("{{author}}", this.item.creator)
.replace("{{published}}", this.item.pubDate)
.replace("{{content}}", content);

const inputPrompt = new TextInputPrompt(this.app, "Please specify a file name", "cannot contain: * \" \\ / < > : | ?", title, title);

await inputPrompt
.openAndGetValue(async (text: TextComponent) => {
const value = text.getValue();
if(value.match(FILE_NAME_REGEX)) {
inputPrompt.setValidationError(text, "that filename is not valid");
return;
}
const filePath = normalizePath([dir, `${value}.md`].join('/'));

if (isInVault(this.app, filePath, '')) {
inputPrompt.setValidationError(text, "there is already a note with that name");
return;
}

this.close();
inputPrompt.close();

const file = await this.app.vault.create(filePath, appliedTemplate);

await this.app.workspace.activeLeaf.openFile(file, {
state: {mode: 'edit'},
})
new Notice("Created note from article");
});
await createNewNote(this.plugin, this.item);
});

new ButtonComponent(topButtons).setTooltip("paste to current note").setIcon("paste").onClick(() => {
const file = this.app.workspace.getActiveFile();
if (file === null) {
new Notice("no file active");
return;
}

const view = this.app.workspace.getActiveViewOfType(MarkdownView);
if (view) {
const editor = view.editor;
editor.replaceRange(htmlToMarkdown(this.item.content), editor.getCursor());
new Notice("inserted article into note");
}
new ButtonComponent(topButtons).setTooltip("paste to current note").setIcon("paste").onClick(async() => {
await pasteToNote(this.plugin, this.item);
});

new ButtonComponent(topButtons).setTooltip("copy content to clipboard").setIcon("feather-clipboard").onClick(async () => {
Expand Down
8 changes: 4 additions & 4 deletions src/SettingsModal.ts → src/modals/SettingsModal.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Notice, Setting, TextComponent} from "obsidian";
import RssReaderPlugin from "./main";
import {RssFeed} from "./settings";
import {getFeedItems} from "./rssParser";
import {isValidHttpUrl} from "./consts";
import RssReaderPlugin from "../main";
import {RssFeed} from "../settings/settings";
import {getFeedItems} from "../parser/rssParser";
import {isValidHttpUrl} from "../consts";
import {BaseModal} from "./BaseModal";

export class SettingsModal extends BaseModal {
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion src/opmlParser.ts → src/parser/opmlParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {RssFeed} from "./settings";
import {RssFeed} from "../settings/settings";

//not used currently, parser not fully implemented.
export async function loadFeedsFromString(importData: string) : Promise<RssFeed[]> {
Expand Down
28 changes: 18 additions & 10 deletions src/rssParser.ts → src/parser/rssParser.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import {request} from "obsidian";
import {RssFeed} from "./settings";
import {RssFeed} from "../settings/settings";

/**
* parser for .rss files, build from scratch
* because I could not find a parser that works on mobile and is up to date.
*/

export interface RssFeedContent {
subtitle: string;
title: string;
link: string;
image: string;
description: string;
items: RssFeedItem[];
subtitle: string,
title: string,
link: string,
image: string,
description: string,
items: RssFeedItem[]
}

export interface RssFeedItem {
Expand All @@ -22,7 +22,9 @@ export interface RssFeedItem {
category: string,
link: string,
creator: string,
pubDate: string
pubDate: string,
folder: string,
feed: string
}

export interface RssFeedMap {
Expand Down Expand Up @@ -123,7 +125,9 @@ function buildItem(element: Element): RssFeedItem {
category: getContent(element, ["category"]),
link: getContent(element, ["link", "link#href"]),
creator: getContent(element, ["creator", "dc:creator", "author", "author.name"]),
pubDate: getContent(element, ["pubDate", "published"])
pubDate: getContent(element, ["pubDate", "published"]),
folder: null,
feed: null
}
}

Expand Down Expand Up @@ -155,8 +159,12 @@ export async function getFeedItems(feed: RssFeed): Promise<RssFeedContent> {

rawItems.forEach((rawItem) => {
const item = buildItem(rawItem);
if (item.title !== undefined)
if (item.title !== undefined) {
item.folder = feed.folder;
item.feed = feed.name;
items.push(item);
}

})

const image = getContent(data, ["image", "image.url", "icon"]);
Expand Down
33 changes: 33 additions & 0 deletions src/settings/FolderSuggestor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes

import { TAbstractFile, TFolder } from "obsidian";
import { TextInputSuggest } from "./suggest";

export class FolderSuggest extends TextInputSuggest<TFolder> {
getSuggestions(inputStr: string): TFolder[] {
const abstractFiles = this.app.vault.getAllLoadedFiles();
const folders: TFolder[] = [];
const lowerCaseInputStr = inputStr.toLowerCase();

abstractFiles.forEach((folder: TAbstractFile) => {
if (
folder instanceof TFolder &&
folder.path.toLowerCase().contains(lowerCaseInputStr)
) {
folders.push(folder);
}
});

return folders;
}

renderSuggestion(file: TFolder, el: HTMLElement): void {
el.setText(file.path);
}

selectSuggestion(file: TFolder): void {
this.inputEl.value = file.path;
this.inputEl.trigger("input");
this.close();
}
}
Loading

0 comments on commit 567a2fe

Please sign in to comment.