Skip to content

Commit

Permalink
+ Highlights in article view, with extraction(closes #60)
Browse files Browse the repository at this point in the history
+ a new style for the article list(references #54)
~ ignoring of folders, feeds and tags in filters(closes #58)
~ fixing an issue where filters would not work correctly(closes #63)
  • Loading branch information
joethei committed Jan 5, 2022
1 parent e1af436 commit d93b7f4
Show file tree
Hide file tree
Showing 19 changed files with 503 additions and 151 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -50,7 +50,8 @@ Plugin for [Obsidian](https://obsidian.md)
- `{{tags}}` - tags, seperated by comma, you can also specify a seperator like this: `{{tags:;}}`
- `{{#tags}}` - tags with #, seperated by comma, with #, you can also specify a seperator like this: `{{#tags:;}}`
- `{{media}}` link to media
- `{{highlights}}` - list of highlights, specify a style with the following syntax `{{hightlights:-$\n}}`
- `{{highlights}}` - list of highlights, you can also specify a custom style, this example creates a [admonition](https://github.com/valentine195/obsidian-admonition) for each highlight:
![](https://i.joethei.space/obsidian-rss-highlight-syntax.png)

## Styling
If you want to style the plugin differently you can use the following css classes
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
@@ -1,7 +1,7 @@
{
"id": "rss-reader",
"name": "RSS Reader",
"version": "0.9.3",
"version": "1.0.0",
"minAppVersion": "0.12.17",
"description": "Read RSS Feeds from within obsidian",
"author": "Johannes Theiner",
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "rss-reader",
"version": "0.9.3",
"version": "1.0.0",
"description": "Read RSS Feeds from inside obsidian",
"main": "main.js",
"scripts": {
Expand Down
53 changes: 45 additions & 8 deletions src/functions.ts
Expand Up @@ -129,23 +129,38 @@ function applyTemplate(item: RssFeedItem, template: string, settings: RssReaderS
return item.tags.map(i => '#' + i).join(separator);
});

result = result.replace(/{{tags}}/, item.tags.join(", "));
result = result.replace(/{{#tags}}/, item.tags.map(i => '#' + i).join(", "));
result = result.replace(/{{tags}}/g, item.tags.join(", "));
result = result.replace(/{{#tags}}/g, item.tags.map(i => '#' + i).join(", "));

result = result.replace(/{{highlights}}/, item.highlights.map(value => "- " + htmlToMarkdown(value)).join("\n"));
result = result.replace(/({{highlights:).*(}})/g, function (k) {
const value = k.split(":")[1];
const separator = value.substring(0, value.indexOf("}"));
return item.highlights.map(i => '' + i).join(separator);
});


result = result.replace(/{{highlights}}/g, item.highlights.map(value => {
//remove all - from the start of a highlight
return "- " + htmlToMarkdown(removeFormatting(value).replace(/^(-+)/, ""))
}).join("\n"));

result = result.replace(/({{highlights:)[\s\S][^}]*(}})/g, function (k) {
const value = k.split(/(:[\s\S]?)/);
const tmp = value.slice(1).join("");
const template = tmp.substring(1, tmp.indexOf("}"));
return item.highlights.map(i => {
return template.replace(/%%highlight%%/g, htmlToMarkdown(removeFormatting(i)).replace(/^(-+)/, ""));
}).join("");
});

if(filename) {
result = result.replace(/{{filename}}/g, filename);
}

let content = htmlToMarkdown(item.content);


item.highlights.forEach(highlight => {
const mdHighlight = htmlToMarkdown(highlight);
content = content.replace(mdHighlight, "==" + mdHighlight + "==");
});


/*
fixes #48
replacing $ with $$$, because that is a special regex character:
Expand All @@ -154,11 +169,33 @@ function applyTemplate(item: RssFeedItem, template: string, settings: RssReaderS
*/
content = content.replace(/\$/g, "$$$");


result = result.replace(/{{content}}/g, content);

return result;
}

function removeFormatting(html: string) : string {
const doc = new DOMParser().parseFromString(html, 'text/html');
const elements = doc.querySelectorAll("html body a");
for (let i = 0; i < elements.length; i++) {
const el = elements.item(i) as HTMLAnchorElement;
if(el.dataset) {
Object.keys(el.dataset).forEach(key => {
delete el.dataset[key];
});
}
}

const objects = doc.querySelectorAll("object");
for (let i = 0; i <objects.length; i++) {
const object = objects.item(i) as HTMLObjectElement;
object.remove();
}

return doc.documentElement.innerHTML;
}

export function openInBrowser(item: RssFeedItem) : void {
if (typeof item.link === "string") {
window.open(item.link, '_blank');
Expand Down
13 changes: 12 additions & 1 deletion src/l10n/locales/de.ts
Expand Up @@ -182,5 +182,16 @@ export default {
scanning_duplicates: "Entferne Duplikate",
do_not_close: "Bitte dieses Fenster nicht schliesen",

display_style: "Anzeige"
display_style: "Anzeige",
list: "List",
cards: "Karten",

customize_terms: "Begriffe anpassen",
content: "Inhalt",
highlight: "Markieren",
highlight_remove: "Markierung entfernen",

filter_folder_ignore_help: "diese Ordner ignorieren",
filter_feed_ignore_help: "diese Feeds ignorieren",
filter_tags_ignore_help: "diese Tags ignorieren"
}
7 changes: 6 additions & 1 deletion src/l10n/locales/en.ts
Expand Up @@ -186,6 +186,11 @@ export default {
cards: "Cards",

customize_terms: "Customize Terms",
content: "Content"
content: "Content",
highlight: "Highlight",
highlight_remove: "remove highlight",

filter_folder_ignore_help: "ignore the following folders",
filter_feed_ignore_help: "ignore the following feeds",
filter_tags_ignore_help: "ignore the following tags"
}
143 changes: 98 additions & 45 deletions src/main.ts
Expand Up @@ -85,20 +85,20 @@ export default class RssReaderPlugin extends Plugin {
});

this.addCommand({
id: 'rss-open-feed',
id: 'rss-open-feed',
name: "Open Feed from URL",
callback: async () => {
const input = new TextInputPrompt(this.app, "URL", "URL", "", "", t("open"));
await input.openAndGetValue(async (text) => {
const items = await getFeedItems({name: "", folder: "", url: text.getValue()});
if(!items || items.items.length === 0) {
input.setValidationError(text, t("invalid_feed"));
return;
}

input.close();
new ArticleSuggestModal(this, items.items).open();
});
const input = new TextInputPrompt(this.app, "URL", "URL", "", "", t("open"));
await input.openAndGetValue(async (text) => {
const items = await getFeedItems({name: "", folder: "", url: text.getValue()});
if (!items || items.items.length === 0) {
input.setValidationError(text, t("invalid_feed"));
return;
}

input.close();
new ArticleSuggestModal(this, items.items).open();
});
}
});

Expand Down Expand Up @@ -141,7 +141,6 @@ export default class RssReaderPlugin extends Plugin {
await this.updateFeeds();



feedsStore.subscribe((feeds: RssFeedContent[]) => {
//keep sorted store sorted when the items change.
const sorted = groupBy(feeds, "folder");
Expand All @@ -157,21 +156,21 @@ export default class RssReaderPlugin extends Plugin {
//collect all tags for auto completion
const tags: string[] = [];
for (const item of items) {
if(item !== undefined)
if (item !== undefined)
tags.push(...item.tags);
}

//@ts-ignore
const fileTags = this.app.metadataCache.getTags();
for(const tag of Object.keys(fileTags)) {
for (const tag of Object.keys(fileTags)) {
tags.push(tag.replace('#', ''));
}
tagsStore.update(() => new Set<string>(tags.filter(tag => tag.length > 0)));

//collect all folders for auto-completion
const folders: string[] = [];
for (const item of items) {
if(item !== undefined)
if (item !== undefined)
folders.push(item.folder);
}
folderStore.update(() => new Set<string>(folders.filter(folder => folder.length > 0)));
Expand All @@ -187,28 +186,62 @@ export default class RssReaderPlugin extends Plugin {
// @ts-ignore
const sortOrder = SortOrder[filter.sortOrder];
let filteredItems: RssFeedItem[];
filteredItems = items.filter((item) => {
return item.read === filter.read || item.read !== filter.unread;
});
if(filter.favorites) {
filteredItems = filteredItems.filter((item) => {

if (filter.read && filter.unread) {
filteredItems = items.filter((item) => {
return item.read === filter.read || item.read !== filter.unread;
});
} else if (filter.read) {
filteredItems = items.filter((item) => {
return item.read;
});
} else if (filter.unread) {
filteredItems = items.filter((item) => {
return !item.read;
});
}


if (filter.favorites) {
filteredItems = filteredItems.filter((item) => {
return item.favorite === filter.favorites;
});
}
if(filter.filterFolders.length > 0) {

if (filter.filterFolders.length > 0) {
filteredItems = filteredItems.filter((item) => {
return filter.filterFolders.includes(item.folder);
});
}
if (filter.ignoreFolders.length > 0) {
filteredItems = filteredItems.filter((item) => {
filter.filterFolders.includes(item.folder);
return !filter.ignoreFolders.includes(item.folder);
});
}
if(filter.filterFeeds.length > 0) {

if (filter.filterFeeds.length > 0) {
filteredItems = filteredItems.filter((item) => {
filter.filterFeeds.includes(item.feed);
return filter.filterFeeds.includes(item.feed);
});
}
if(filter.filterTags.length > 0) {
if (filter.ignoreFeeds.length > 0) {
filteredItems = filteredItems.filter((item) => {
return !filter.ignoreFeeds.includes(item.feed);
});
}

if (filter.filterTags.length > 0) {
filteredItems = filteredItems.filter((item) => {
for (const tag of filter.filterTags) {
if(!item.tags.contains(tag)) return false;
if (!item.tags.contains(tag)) return false;
}
return true;
});
}
if (filter.ignoreTags.length > 0) {
filteredItems = filteredItems.filter((item) => {
for (const tag of filter.ignoreTags) {
if (item.tags.contains(tag)) return false;
}
return true;
});
Expand All @@ -221,7 +254,7 @@ export default class RssReaderPlugin extends Plugin {
}

sortItems(items: RssFeedItem[], sortOrder: SortOrder): RssFeedItem[] {
if(!items) return items;
if (!items) return items;
if (sortOrder === SortOrder.ALPHABET_NORMAL) {
return items.sort((a, b) => a.title.localeCompare(b.title));
}
Expand Down Expand Up @@ -275,27 +308,27 @@ export default class RssReaderPlugin extends Plugin {
let result: RssFeedContent[] = [];
for (const feed of this.settings.feeds) {
const items = await getFeedItems(feed);
if(items)
if (items)
result.push(items);
}

const items = this.settings.items;
for (const feed of items) {
if(feed.hash === undefined || feed.hash === "") {
if (feed.hash === undefined || feed.hash === "") {
feed.hash = <string>new Md5().appendStr(feed.name).appendStr(feed.folder).end();
}
for (const item of feed.items) {
if(item.folder !== feed.folder || item.feed !== feed.name) {
if (item.folder !== feed.folder || item.feed !== feed.name) {
feed.items.remove(item);
}
if(item.hash === undefined) {
if (item.hash === undefined) {
item.hash = <string>new Md5().appendStr(item.title).appendStr(item.folder).appendStr(item.link).end();
}
}
}

result = mergeWith(result, items, customizer);
new Notice(t("refreshed_feeds"));
new Notice(t("refreshed_feeds"));
await this.writeFeedContent(() => result);
}

Expand All @@ -319,8 +352,25 @@ export default class RssReaderPlugin extends Plugin {
const configPath = this.app.vault.configDir + "/plugins/rss-reader/data.json";
const config = JSON.parse(await this.app.vault.adapter.read(configPath));

if(config.filtered.length === 0) return;
if(config.filtered[0].filterType === undefined) return;
if (config.filtered.length === 0) return;

if (config.filtered[0].ignoreFolders === undefined) {
new Notice("RSS Reader: migrating data");
console.log("RSS Reader: adding ignored fields to filters");

for (const filter of config.filtered) {
filter.ignoreTags = [];
filter.ignoreFolders = [];
filter.ignoreFeeds = [];

}
await this.app.vault.adapter.write(configPath, JSON.stringify(config));
await this.loadSettings();
new Notice("RSS Reader: data has been migrated");
}

if (config.filtered[0].filterType === undefined) return;


new Notice("RSS Reader: migrating data");

Expand All @@ -333,18 +383,21 @@ export default class RssReaderPlugin extends Plugin {
read: false,
unread: false,
sortOrder: filter.sortOrder,
name: filter.name
name: filter.name,
ignoreFolders: [],
ignoreFeeds: [],
ignoreTags: [],
};

if(filter.filterType === "FAVORITES") newFilter.favorites = true;
if(filter.filterType === "READ") newFilter.read = true;
if(filter.filterType === "UNREAD") newFilter.unread = true;
if(filter.filterType === "TAGS") {
if(filter.filterContent !== "") {
if (filter.filterType === "FAVORITES") newFilter.favorites = true;
if (filter.filterType === "READ") newFilter.read = true;
if (filter.filterType === "UNREAD") newFilter.unread = true;
if (filter.filterType === "TAGS") {
if (filter.filterContent !== "") {
newFilter.filterTags = filter.filterContent.split(",");
}
}else {
if(filter.filterContent !== "") {
} else {
if (filter.filterContent !== "") {
newFilter.filterFolders = filter.filterContent.split(",");
}
}
Expand Down Expand Up @@ -402,7 +455,7 @@ export default class RssReaderPlugin extends Plugin {
console.error(e);
}

if(file !== undefined) {
if (file !== undefined) {
try {
JSON.parse(file);
} catch (e) {
Expand All @@ -415,7 +468,7 @@ export default class RssReaderPlugin extends Plugin {

const data = await this.loadData();
this.settings = Object.assign({}, DEFAULT_SETTINGS, data);
if(data !== undefined && data !== null) {
if (data !== undefined && data !== null) {
this.settings.hotkeys = Object.assign({}, DEFAULT_SETTINGS.hotkeys, data.hotkeys);
}
settingsStore.set(this.settings);
Expand Down

0 comments on commit d93b7f4

Please sign in to comment.