Skip to content

Commit

Permalink
+ Adding a counter for unread articles(fixes #77)
Browse files Browse the repository at this point in the history
+ Adding a option to define a custom folder when importing OPML(fixes #81)
~ Fixing parsing of wallabag feeds(fixes #79)
~ Adding a option to disable rendering of media(fixes #81)
~ hiding metadata block when reading a article(references #88)
  • Loading branch information
joethei committed Mar 31, 2022
1 parent 1d1a89b commit 72ea4bd
Show file tree
Hide file tree
Showing 31 changed files with 223 additions and 71 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -12,3 +12,5 @@ main.js

# obsidian
data.json
.nyc_output
coverage
7 changes: 4 additions & 3 deletions .mocharc.json
@@ -1,5 +1,6 @@
{
"extension": ["ts"],
"spec": "src/**/*.spec.ts",
"require": "ts-node/register"
"require": [
"./registerTests.js"
],
"reporter": "dot"
}
14 changes: 14 additions & 0 deletions .nycrc.json
@@ -0,0 +1,14 @@
{
"extends": "@istanbuljs/nyc-config-typescript",
"include": [
"src/**/*.ts"
],
"reporter": [
"text-summary",
"html",
"lcov",
"cobertura"
],
"report-dir": "./coverage",
"all": true
}
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -41,6 +41,7 @@ After installing the plugin:
- get fulltext content for some truncated RSS feeds with [morss.it](https://morss.it/)
- get feeds from some social media sites with [RSS Box](https://rssbox.herokuapp.com/)
- Filter content from feeds with [SiftRSS](https://siftrss.com/)
- Get an RSS feed for a site that does not support RSS with [RSS-proxy](https://github.com/damoeb/rss-proxy/) or [RSS Hub](https://github.com/DIYgod/RSSHub)

## Template variables
- `{{title}}` title of article
Expand Down
24 changes: 24 additions & 0 deletions __mocks__/obsidian.ts
@@ -0,0 +1,24 @@
import fetch from "node-fetch";


export interface RequestParam {
/** @public */
url: string;
/** @public */
method?: string;
/** @public */
contentType?: string;
/** @public */
body?: string;
/** @public */
headers?: Record<string, string>;
}

export async function request(request: RequestParam) : Promise<string> {
const result = await fetch(request.url,{
headers: request.headers,
method: request.method,
body: request.body
});
return result.text();
}
3 changes: 3 additions & 0 deletions babel.config.js
@@ -0,0 +1,3 @@
module.exports = {
presets: ['@babel/preset-env']
}
7 changes: 7 additions & 0 deletions jest.config.js
@@ -0,0 +1,7 @@
module.exports = {
transform: {"\\.ts$": ['ts-jest']},
collectCoverage: true,
testEnvironment: "jsdom",
moduleDirectories: ["node_modules", "src", "test"],

};
4 changes: 2 additions & 2 deletions manifest.json
@@ -1,8 +1,8 @@
{
"id": "rss-reader",
"name": "RSS Reader",
"version": "1.1.0",
"minAppVersion": "0.12.17",
"version": "1.2.0",
"minAppVersion": "0.13.33",
"description": "Read RSS Feeds from within obsidian",
"author": "Johannes Theiner",
"authorUrl": "https://github.com/joethei",
Expand Down
23 changes: 20 additions & 3 deletions package.json
@@ -1,19 +1,26 @@
{
"name": "rss-reader",
"version": "1.1.0",
"version": "1.2.0",
"description": "Read RSS Feeds from inside obsidian",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "node esbuild.config.mjs production",
"lint": "eslint . --ext .ts",
"lint-css": "stylelint styles.css",
"test": "mocha"
"test": "jest --coverage"
},
"keywords": [],
"author": "Johannes Theiner",
"license": "GPL-3.0",
"devDependencies": {
"jest": "27.5.1",
"@types/jest": "27.4.1",
"nyc": "15.1.0",
"sinon": "13.0.1",
"@types/sinon": "10.0.11",
"ts-jest": "27.1.4",
"@istanbuljs/nyc-config-typescript": "1.0.2",
"@popperjs/core": "^2.10.2",
"@types/chai": "^4.3.0",
"@types/lodash.groupby": "^4.6.6",
Expand Down Expand Up @@ -48,6 +55,16 @@
"ts-node": "^10.4.0",
"tslint": "^6.1.3",
"typescript": "^4.2.4",
"tslib": "^2.3.1"
"tslib": "^2.3.1",
"node-fetch": "3.2.3",
"@types/node-fetch": "2.6.1",
"@babel/core": "7.17.8",
"babel-jest": "27.5.1",
"@babel/preset-env": "7.16.11",
"@babel/preset-typescript": "7.16.7",
"@babel/plugin-transform-modules-commonjs": "7.17.7"
},
"dependencies": {

}
}
2 changes: 1 addition & 1 deletion src/actions/Action.ts
Expand Up @@ -40,7 +40,7 @@ export default class Action {
return Promise.resolve();
})));

static READ = new Action(t("mark_as_read_unread"), "feather-eye", ((async (plugin, item) : Promise<void> => {
static READ = new Action(t("mark_as_read_unread"), "eye", ((async (plugin, item) : Promise<void> => {
if (item.read) {
item.read = false;
new Notice(t("marked_as_unread"));
Expand Down
4 changes: 4 additions & 0 deletions src/functions.ts
Expand Up @@ -245,6 +245,10 @@ export function rssToMd(plugin: RssReaderPlugin, content: string): string {
markdown = markdown.replace(/^```.*\n([\s\S]*?)```$/gm, "<pre>$&</pre>");
}

if(!plugin.settings.displayMedia) {
//remove any embeds, but keep alias
markdown = markdown.replace(/!?\[(.*)\]\(.+\)/gm, "$1");
}
return markdown;
}

Expand Down
5 changes: 4 additions & 1 deletion src/l10n/locales/en.ts
Expand Up @@ -209,5 +209,8 @@ export default {
feed_title: "Title of feed",
highlights: "Highlights",
note_created: "Note creation date",
filename: "Filename"
filename: "Filename",

display_media: "Include Media",
base_folder: "Base folder"
}
17 changes: 2 additions & 15 deletions src/main.ts
Expand Up @@ -15,7 +15,6 @@ import {
} from "./stores";
import {VIEW_ID} from "./consts";
import {getFeedItems, RssFeedContent, RssFeedItem} from "./parser/rssParser";
import {addFeatherIcon} from "obsidian-community-lib";
import groupBy from "lodash.groupby";
import mergeWith from "lodash.mergewith";
import keyBy from "lodash.keyby";
Expand All @@ -35,19 +34,7 @@ export default class RssReaderPlugin extends Plugin {
async onload(): Promise<void> {
console.log('loading plugin rss reader');

addFeatherIcon("rss");
addFeatherIcon("eye");
addFeatherIcon("eye-off");
addFeatherIcon("star");
addFeatherIcon("headphones");
addFeatherIcon("upload");
addFeatherIcon("trash");
addFeatherIcon("plus");
addFeatherIcon("edit");
addFeatherIcon("download");
addFeatherIcon("archive");

//update settings whenever store contents change.
//update settings object whenever store contents change.
this.register(
settingsStore.subscribe((value: RssReaderSettings) => {
this.settings = value;
Expand Down Expand Up @@ -472,7 +459,7 @@ export default class RssReaderPlugin extends Plugin {
try {
JSON.parse(file);
} catch (e) {
console.log("RSS Reader: could not parse json, check if the plugins data.json is valid.");
console.log(t("RSS_Reader") + " could not parse json, check if the plugins data.json is valid.");
console.error(e);
new Notice(t("RSS_Reader") + " could not parse plugin data. If this message keeps showing up, check the console");
return Promise.resolve();
Expand Down
14 changes: 13 additions & 1 deletion src/modals/ImportModal.ts
Expand Up @@ -2,6 +2,7 @@ import {Modal, Notice, Setting} from "obsidian";
import {loadFeedsFromString} from "../parser/opmlParser";
import RssReaderPlugin from "../main";
import t from "../l10n/locale";
import {FeedFolderSuggest} from "../view/FeedFolderSuggest";

//adapted from javalent's code here: https://discord.com/channels/686053708261228577/840286264964022302/918146537220112455
export class ImportModal extends Modal {
Expand All @@ -14,6 +15,7 @@ export class ImportModal extends Modal {
}

importData = "";
defaultFolder = "";

async onOpen() : Promise<void> {
const setting = new Setting(this.contentEl).setName(t("choose_file")).setDesc(t("choose_file_help"));
Expand All @@ -37,13 +39,23 @@ export class ImportModal extends Modal {
}
}

new Setting(this.contentEl)
.setName(t("base_folder"))
.addSearch(search => {
new FeedFolderSuggest(this.app, search.inputEl);
search.setValue(this.defaultFolder)
.onChange(value => {
this.defaultFolder = value;
})
});

new Setting(this.contentEl).addButton((button) => {
button
.setIcon("import-glyph")
.setTooltip(t("import"))
.onClick(async () => {
if (this.importData) {
const feeds = await loadFeedsFromString(this.importData);
const feeds = await loadFeedsFromString(this.importData, this.defaultFolder);
await this.plugin.writeFeeds(() => (this.plugin.settings.feeds.concat(feeds)));
new Notice(t("imported_x_feeds", String(feeds.length)));
this.close();
Expand Down
12 changes: 6 additions & 6 deletions src/modals/ItemModal.ts
Expand Up @@ -149,7 +149,7 @@ export class ItemModal extends Modal {

async markAsRead(): Promise<void> {
await Action.READ.processor(this.plugin, this.item);
this.readButton.setIcon((this.item.read) ? 'feather-eye-off' : 'feather-eye');
this.readButton.setIcon((this.item.read) ? 'eye-off' : 'eye');
this.readButton.setTooltip((this.item.read) ? t("mark_as_unread") : t("mark_as_unread"));
}

Expand All @@ -168,7 +168,7 @@ export class ItemModal extends Modal {

if (this.save) {
this.readButton = new ButtonComponent(topButtons)
.setIcon(this.item.read ? 'feather-eye-off' : 'feather-eye')
.setIcon(this.item.read ? 'eye-off' : 'eye')
.setTooltip(this.item.read ? t("mark_as_unread") : t("mark_as_read"))
.onClick(async () => {
await this.markAsRead();
Expand Down Expand Up @@ -202,7 +202,7 @@ export class ItemModal extends Modal {
//@ts-ignore
if (this.app.plugins.plugins["obsidian-tts"]) {
const ttsButton = new ButtonComponent(topButtons)
.setIcon("feather-headphones")
.setIcon("headphones")
.setTooltip(t("read_article_tts"))
.onClick(async () => {
const content = htmlToMarkdown(this.item.content);
Expand Down Expand Up @@ -250,7 +250,7 @@ export class ItemModal extends Modal {
const content = contentEl.createDiv('rss-content');
content.addClass("rss-scrollable-content", "rss-selectable");

if (this.item.enclosure) {
if (this.item.enclosure && this.plugin.settings.displayMedia) {
if (this.item.enclosureType.toLowerCase().contains("audio")) {
const audio = content.createEl("audio", {attr: {controls: "controls"}});
audio.createEl("source", {attr: {src: this.item.enclosure, type: this.item.enclosureType}});
Expand All @@ -276,7 +276,7 @@ export class ItemModal extends Modal {

if (this.item.content) {
//prepend empty yaml to fix rendering errors
const markdown = "---\n----" + rssToMd(this.plugin, this.item.content);
const markdown = "---\n---" + rssToMd(this.plugin, this.item.content);

await MarkdownRenderer.renderMarkdown(markdown, content, "", this.plugin);

Expand Down Expand Up @@ -370,7 +370,7 @@ export class ItemModal extends Modal {
if (this.app.plugins.plugins["obsidian-tts"]) {
menu.addItem(item => {
item
.setIcon("feather-headphones")
.setIcon("headphones")
.setTitle(t("read_article_tts"))
.onClick(() => {
//@ts-ignore
Expand Down
6 changes: 3 additions & 3 deletions src/parser/opmlParser.ts
@@ -1,6 +1,6 @@
import {RssFeed} from "../settings/settings";

export async function loadFeedsFromString(importData: string): Promise<RssFeed[]> {
export async function loadFeedsFromString(importData: string, defaultFolder: string): Promise<RssFeed[]> {
const rawData = new window.DOMParser().parseFromString(importData, "text/xml");
const feeds: RssFeed[] = [];

Expand All @@ -19,13 +19,13 @@ export async function loadFeedsFromString(importData: string): Promise<RssFeed[]
feeds.push({
name: title,
url: xmlUrl,
folder: current.parentElement.getAttribute("title"),
folder: defaultFolder + ((defaultFolder) ? "/" : "") + current.parentElement.getAttribute("title"),
});
}else {
feeds.push({
name: title,
url: xmlUrl,
folder: ""
folder: defaultFolder + ""
});
}
}
Expand Down
24 changes: 18 additions & 6 deletions src/parser/rssParser.ts
Expand Up @@ -60,7 +60,7 @@ function getElementByName(element: Element | Document, name: string): ChildNode
return;
}

if (name.contains(":")) {
if (name.includes(":")) {
const [namespace, tag] = name.split(":");
const namespaceUri = element.lookupNamespaceURI(namespace);
const byNamespace = element.getElementsByTagNameNS(namespaceUri, tag);
Expand All @@ -81,7 +81,7 @@ function getElementByName(element: Element | Document, name: string): ChildNode
}
}

} else if (name.contains(".")) {
} else if (name.includes(".")) {
const [prefix, tag] = name.split(".");
if (element.getElementsByTagName(prefix).length > 0) {
const nodes = Array.from(element.getElementsByTagName(prefix)[0].childNodes);
Expand All @@ -101,6 +101,8 @@ function getElementByName(element: Element | Document, name: string): ChildNode
value = node;
}
}
//if(name === "content") console.log(value);

return value;
}

Expand All @@ -113,7 +115,7 @@ function getElementByName(element: Element | Document, name: string): ChildNode
function getContent(element: Element | Document, names: string[]): string {
let value: string;
for (const name of names) {
if (name.contains("#")) {
if (name.includes("#")) {
const [elementName, attr] = name.split("#");
const data = getElementByName(element, elementName);
if (data) {
Expand All @@ -129,11 +131,17 @@ function getContent(element: Element | Document, names: string[]): string {
const data = getElementByName(element, name);
if (data) {
//@ts-ignore
if (data.nodeValue && data.nodeValue.length > 0) {
if(data.wholeText && data.wholeText.length > 0) {
//@ts-ignore
value = data.wholeText;
}

//@ts-ignore
if (!value && data.nodeValue && data.nodeValue.length > 0) {
value = data.nodeValue;
}
//@ts-ignore
else if (data.innerHTML && data.innerHTML.length > 0) {
if (!value && data.innerHTML && data.innerHTML.length > 0) {
//@ts-ignore
value = data.innerHTML;
}
Expand Down Expand Up @@ -190,10 +198,14 @@ function getAllItems(doc: Document): Element[] {
return items;
}

async function requestFeed(feed: RssFeed) : Promise<string> {
return await request({url: feed.url});
}

export async function getFeedItems(feed: RssFeed): Promise<RssFeedContent> {
let data;
try {
const rawData = await request({url: feed.url});
const rawData = await requestFeed(feed);
data = new window.DOMParser().parseFromString(rawData, "text/xml");
} catch (e) {
console.error(e);
Expand Down

0 comments on commit 72ea4bd

Please sign in to comment.