Skip to content

Commit

Permalink
feat: improved extended feed scan, added feed caching mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
mt190502 committed Nov 21, 2024
1 parent 108c6a5 commit d9246cf
Show file tree
Hide file tree
Showing 15 changed files with 283 additions and 87 deletions.
88 changes: 64 additions & 24 deletions chromium/src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const enableIcon = (tab_id: number, feed_urls?: string[]) => {
chrome.action.setIcon({ path: '/img/firerss_32.png', tabId: tab_id });
if (feed_urls && feed_urls.length > 0) {
chrome.action.setBadgeText({ text: feed_urls.length.toString(), tabId: tab_id });
chrome.action.setBadgeBackgroundColor({ color: '#FF6600', tabId: tab_id });
chrome.action.setTitle({ title: 'FireRSS (Found ' + feed_urls.length + ' feeds)', tabId: tab_id });
} else {
chrome.action.setTitle({ title: 'FireRSS', tabId: tab_id });
Expand Down Expand Up @@ -72,6 +73,7 @@ const getTabFromId = (tab_id: number): Promise<chrome.tabs.Tab> => {
};

const findAllFeeds = async (): Promise<string[]> => {
const settings = (await chrome.storage.local.get('firerss_settings')).firerss_settings ?? InitDefaultSettings();
const feed_urls: string[] = [];
const youtube_user_pattern = /(?<=(https:\/\/(www\.)?youtube.com\/))@\w+/gi;
let doc: Document;
Expand All @@ -97,16 +99,41 @@ const findAllFeeds = async (): Promise<string[]> => {
}
}

if (!collected_feeds.length) {
const possible_feed_files = ['index.xml', 'feed.xml', 'rss.xml', 'atom.xml', 'feed', 'rss', 'atom'];
for (const file of possible_feed_files) {
const feed = window.location.origin + '/' + file;
const response = await fetch(feed);
if (response.ok && response.headers.get('Content-Type')?.includes('xml')) {
feed_urls.push(feed);
break;
if (settings.extended_feed_scan === 0) {
return feed_urls;
} else if (settings.extended_feed_scan === 1) {
if (feed_urls.length > 0) return feed_urls;
}
const possible_feed_files = [
'.atom',
'.feed',
'.rss',
'.xml',
'/atom.xml',
'/atom',
'/feed.xml',
'/feed',
'/index.xml',
'/rss.xml',
'/rss',
];
for (const file of possible_feed_files) {
let feed;
if (window.location.pathname === '/') {
if (file.startsWith('.')) continue;
feed = window.location.href + file.slice(1);
} else {
if (file.startsWith('.')) {
feed = window.location.href.slice(0, window.location.href.lastIndexOf('/')) + file;
} else {
feed = window.location.origin + file;
}
}
const response = await fetch(feed);
if (response.ok && response.headers.get('Content-Type')?.includes('xml') && !feed_urls.includes(feed)) {
feed_urls.push('_' + feed);
}
await new Promise((resolve) => setTimeout(resolve, 700));
}

return feed_urls;
Expand All @@ -130,25 +157,38 @@ const injectScript = (tab_id: number) => {
return;
}
}
const injection = await chrome.scripting.executeScript({
target: { tabId: tab_id },
func: findAllFeeds,
});

if (!chrome.runtime.lastError) {
const feed_urls: string[] = [];
for (const result of injection) {
feed_urls.push(...result.result);
}

if (feed_urls.length > 0) {
updatePopupState(tab_id, feed_urls);
chrome.storage.session.get(`firerss_feeds:${tab_info.url.replace(/\W/gi, '')}`, async (cached_feeds) => {
const manually_finded_feeds: string[] = cached_feeds[`firerss_feeds:${tab_info.url.replace(/\W/gi, '')}`];
if (manually_finded_feeds && manually_finded_feeds.length > 0) {
updatePopupState(tab_id, manually_finded_feeds);
return;
} else {
disableIcon(tab_id, Status.NO_FEEDS);
const injection = await chrome.scripting.executeScript({
target: { tabId: tab_id },
func: findAllFeeds,
});

if (!chrome.runtime.lastError) {
const feed_urls: string[] = [];
for (const result of injection) {
feed_urls.push(...result.result);
}

if (feed_urls.length > 0) {
chrome.storage.session.set({
[`firerss_feeds:${tab_info.url.replace(/\W/gi, '')}`]: feed_urls,
});
updatePopupState(tab_id, feed_urls);
} else {
chrome.storage.session.set({ [`firerss_feeds:${tab_info.url.replace(/\W/gi, '')}`]: [] });
disableIcon(tab_id, Status.NO_FEEDS);
}
} else {
console.error(`Error: FireRSS: ${chrome.runtime.lastError.message}`);
}
}
} else {
console.error(`Error: FireRSS: ${chrome.runtime.lastError.message}`);
}
});
});
};

Expand Down
4 changes: 3 additions & 1 deletion chromium/src/lib/init_default_settings.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Settings } from '../types/settings_interface';

export const InitDefaultSettings = () => {
export const InitDefaultSettings = (): Settings => {
const settings: Settings = {
theme: 'system',
ignored_sites: ['(.*).hetzner.(.*)'],
extended_feed_scan: 0,
};
chrome.storage.local.set({ firerss_settings: settings });
return settings;
};
21 changes: 18 additions & 3 deletions chromium/src/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,25 @@ const initPopup = async () => {
chrome.runtime.openOptionsPage();
});

for (const feed_url of feed_urls) {
for (let feed_url of feed_urls) {
const tr = document.createElement('tr');
const td1 = document.createElement('td');
const td2 = document.createElement('td');
const td3 = document.createElement('td');
const a = document.createElement('a');
const info_div = document.createElement('div');
const copy_btn = document.createElement('button');

if (feed_url.startsWith('_')) {
info_div.setAttribute('class', 'help-icon');
info_div.setAttribute('aria-label', 'This feed was found by Extended Feed Scan');
info_div.innerText = '!';
feed_url = feed_url.slice(1);
}

td1.setAttribute('class', 'col1 feed_url');
td2.setAttribute('class', 'col2 copy_button_area');
td2.setAttribute('class', 'col2 info_button_area');
td3.setAttribute('class', 'col3 copy_button_area');
a.setAttribute('class', 'feed_url_link');
a.setAttribute('target', '_blank');
a.setAttribute('href', feed_url);
Expand All @@ -34,9 +44,14 @@ const initPopup = async () => {
copy_btn.innerText = 'Copy';

td1.appendChild(a);
td2.appendChild(copy_btn);
if (info_div.innerText) {
td2.appendChild(info_div);
td2.appendChild(document.createTextNode('\u00A0'));
}
td3.appendChild(copy_btn);
tr.appendChild(td1);
tr.appendChild(td2);
tr.appendChild(td3);
feed_list.appendChild(tr);
}

Expand Down
44 changes: 29 additions & 15 deletions chromium/src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ import { applyTheme } from './lib/theme';
import { Settings } from './types/settings_interface';

let settings: Settings;
let dark_mode_theme_btn: HTMLButtonElement;
let light_mode_theme_btn: HTMLButtonElement;
let system_mode_theme_btn: HTMLButtonElement;
let theme_buttons: HTMLButtonElement[];
let ignored_urls_textarea: HTMLTextAreaElement;
let extended_feed_scan_buttons: HTMLButtonElement[];

const ChangeTheme = (theme: Settings['theme'], button_id: string) => {
const ChangeTheme = (theme: Settings['theme']) => {
settings.theme = theme;
light_mode_theme_btn.classList.remove('active');
dark_mode_theme_btn.classList.remove('active');
system_mode_theme_btn.classList.remove('active');
document.getElementById(button_id).classList.add('active');
for (const button of theme_buttons) {
button.classList.remove('active');
if (button.value === theme) button.classList.add('active');
}
chrome.storage.local.set({ firerss_settings: settings });
};

Expand All @@ -22,20 +21,35 @@ const SaveIgnoredSites = () => {
chrome.storage.local.set({ firerss_settings: settings });
};

const ToggleExtendedFeedScan = (opt: 0 | 1 | 2) => {
// 0: disabled, 1: if no feeds found, 2: always
settings.extended_feed_scan = opt;
for (const button of extended_feed_scan_buttons) {
button.classList.remove('active');
if (button.value === opt.toString()) button.classList.add('active');
}
chrome.storage.local.set({ firerss_settings: settings });
};

document.addEventListener('DOMContentLoaded', () => {
dark_mode_theme_btn = document.getElementById('theme_button_dark') as HTMLButtonElement;
light_mode_theme_btn = document.getElementById('theme_button_light') as HTMLButtonElement;
system_mode_theme_btn = document.getElementById('theme_button_system') as HTMLButtonElement;
theme_buttons = Array.from(document.querySelectorAll('.theme_button')) as HTMLButtonElement[];
ignored_urls_textarea = document.getElementById('ignored_urls') as HTMLTextAreaElement;
extended_feed_scan_buttons = Array.from(
document.querySelectorAll('.extended_feed_scan_button')
) as HTMLButtonElement[];

light_mode_theme_btn.addEventListener('click', () => ChangeTheme('light', 'theme_button_light'));
dark_mode_theme_btn.addEventListener('click', () => ChangeTheme('dark', 'theme_button_dark'));
system_mode_theme_btn.addEventListener('click', () => ChangeTheme('system', 'theme_button_system'));
for (const button of theme_buttons) {
button.addEventListener('click', () => ChangeTheme(button.value as Settings['theme']));
}
for (const button of extended_feed_scan_buttons) {
button.addEventListener('click', () => ToggleExtendedFeedScan(Number(button.value) as 0 | 1 | 2));
}
ignored_urls_textarea.addEventListener('change', () => SaveIgnoredSites());

chrome.storage.local.get('firerss_settings', (setting) => {
settings = setting.firerss_settings || InitDefaultSettings();
ChangeTheme(settings.theme, `theme_button_${settings.theme}`);
ChangeTheme(settings.theme);
ToggleExtendedFeedScan(settings.extended_feed_scan);
applyTheme(settings.theme);
ignored_urls_textarea.value = settings.ignored_sites.join('\n') || '';
});
Expand Down
1 change: 1 addition & 0 deletions chromium/src/types/settings_interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface Settings {
theme: 'dark' | 'light' | 'system';
ignored_sites: (string | RegExp)[];
extended_feed_scan: 0 | 1 | 2;
}
15 changes: 15 additions & 0 deletions firefox/src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const enableIcon = (tab_id: number, feed_urls?: string[]) => {
browser.action.setIcon({ path: '/img/firerss_32.png', tabId: tab_id });
if (feed_urls && feed_urls.length > 0) {
browser.action.setBadgeText({ text: feed_urls.length.toString(), tabId: tab_id });
browser.action.setBadgeBackgroundColor({ color: '#FF6600', tabId: tab_id });
browser.action.setTitle({ title: 'FireRSS (Found ' + feed_urls.length + ' feeds)', tabId: tab_id });
} else {
browser.action.setTitle({ title: 'FireRSS', tabId: tab_id });
Expand Down Expand Up @@ -87,6 +88,18 @@ const injectScript = async (tab_id: number, tab_info?: browser.tabs.Tab) => {
return;
}
}

const cached_feeds = sessionStorage.getItem(`firerss_feeds:${tab_info.url.replace(/\W/gi, '')}`);
if (cached_feeds) {
if (JSON.parse(cached_feeds).length > 0) {
enableIcon(tab_id, JSON.parse(cached_feeds));
updatePopupState(tab_id, JSON.parse(cached_feeds));
} else {
disableIcon(tab_id, Status.NO_FEEDS);
}
return;
}

const injection = await browser.scripting.executeScript({
target: { tabId: tab_id },
func: findAllFeeds,
Expand All @@ -99,8 +112,10 @@ const injectScript = async (tab_id: number, tab_info?: browser.tabs.Tab) => {
}

if (feed_urls.length > 0) {
sessionStorage.setItem(`firerss_feeds:${tab_info.url.replace(/\W/gi, '')}`, JSON.stringify(feed_urls));
updatePopupState(tab_id, feed_urls);
} else {
sessionStorage.setItem(`firerss_feeds:${tab_info.url.replace(/\W/gi, '')}`, JSON.stringify([]));
disableIcon(tab_id, Status.NO_FEEDS);
}
} else {
Expand Down
44 changes: 36 additions & 8 deletions firefox/src/content.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { InitDefaultSettings } from './lib/init_default_settings';

export const findAllFeeds = async (): Promise<string[]> => {
const settings = (await browser.storage.local.get('firerss_settings')).firerss_settings ?? InitDefaultSettings();
const feed_urls: string[] = [];
const youtube_user_pattern = /(?<=(https:\/\/(www\.)?youtube.com\/))@\w+/gi;
let doc: Document;
Expand All @@ -24,16 +27,41 @@ export const findAllFeeds = async (): Promise<string[]> => {
}
}

if (!collected_feeds.length) {
const possible_feed_files = ['index.xml', 'feed.xml', 'rss.xml', 'atom.xml', 'feed', 'rss', 'atom'];
for (const file of possible_feed_files) {
const feed = window.location.origin + '/' + file;
const response = await fetch(feed);
if (response.ok && response.headers.get('Content-Type')?.includes('xml')) {
feed_urls.push(feed);
break;
if (settings.extended_feed_scan === 0) {
return feed_urls;
} else if (settings.extended_feed_scan === 1) {
if (feed_urls.length > 0) return feed_urls;
}
const possible_feed_files = [
'.atom',
'.feed',
'.rss',
'.xml',
'/atom.xml',
'/atom',
'/feed.xml',
'/feed',
'/index.xml',
'/rss.xml',
'/rss',
];
for (const file of possible_feed_files) {
let feed;
if (window.location.pathname === '/') {
if (file.startsWith('.')) continue;
feed = window.location.href + file.slice(1);
} else {
if (file.startsWith('.')) {
feed = window.location.href + file;
} else {
feed = window.location.origin + file;
}
}
const response = await fetch(feed);
if (response.ok && response.headers.get('Content-Type')?.includes('xml') && !feed_urls.includes(feed)) {
feed_urls.push('_' + feed);
}
await new Promise((resolve) => setTimeout(resolve, 700));
}

return feed_urls;
Expand Down
3 changes: 2 additions & 1 deletion firefox/src/lib/init_default_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ export const InitDefaultSettings = (): Settings => {
const settings: Settings = {
theme: 'system',
ignored_sites: ['(.*).hetzner.(.*)'],
extended_feed_scan: 0,
};
browser.storage.local.set({ firerss_settings: settings });
chrome.storage.local.set({ firerss_settings: settings });
return settings;
};
Loading

0 comments on commit d9246cf

Please sign in to comment.