Unsubscribe from feed through link or "#"

After importing old OPML files, you may discover that many feeds are
obsolete or uninteresting. You list the feeds entries and determine that
you want to unsubscribe. This needs three clicks (edit feed, delete,
confirm) and requires moving the mouse to hit the different targets.

This quickly becomes tiring, if you are up to possibly deleting hundreds
of feeds. One mediation, introduced in this commit, is to add an
unsubscribe link to each feed's entry listing view, and also adding a
keyboard shortcut.

The keyboard shortcut "#" is:
* longer than one keystroke (requires shift)
* hard to type by accident
* used in Google products (thanks for the hint @fguillot)

In an effort to try to reduce the number of accidental feed
unsubscriptions.
This commit is contained in:
Carl Helmertz 2018-10-14 00:43:09 +02:00 committed by Frédéric Guillot
parent b8f874a37d
commit 15a11c3da9
18 changed files with 80 additions and 14 deletions

View file

@ -194,6 +194,7 @@ func main() {
"ui/static/js/request_builder.js",
"ui/static/js/unread_counter_handler.js",
"ui/static/js/entry_handler.js",
"ui/static/js/feed_handler.js",
"ui/static/js/confirm_handler.js",
"ui/static/js/menu_handler.js",
"ui/static/js/modal_handler.js",

View file

@ -128,6 +128,7 @@ var translations = map[string]string{
"page.keyboard_shortcuts.download_content": "Vollständigen Inhalt herunterladen",
"page.keyboard_shortcuts.toggle_bookmark_status": "Lesezeichen hinzufügen/entfernen",
"page.keyboard_shortcuts.save_article": "Artikel speichern",
"page.keyboard_shortcuts.remove_feed": "Dieses Abonnement entfernen",
"page.keyboard_shortcuts.go_to_search": "Fokus auf das Suchformular setzen",
"page.keyboard_shortcuts.close_modal": "Liste der Tastenkürzel schließen",
"page.users.title": "Benutzer",
@ -417,6 +418,7 @@ var translations = map[string]string{
"page.keyboard_shortcuts.download_content": "Download original content",
"page.keyboard_shortcuts.toggle_bookmark_status": "Toggle bookmark",
"page.keyboard_shortcuts.save_article": "Save article",
"page.keyboard_shortcuts.remove_feed": "Remove this feed",
"page.keyboard_shortcuts.go_to_search": "Set focus on search form",
"page.keyboard_shortcuts.close_modal": "Close modal dialog",
"page.users.title": "Users",
@ -686,6 +688,7 @@ var translations = map[string]string{
"page.keyboard_shortcuts.download_content": "Télécharger le contenu original",
"page.keyboard_shortcuts.toggle_bookmark_status": "Ajouter/Enlever favoris",
"page.keyboard_shortcuts.save_article": "Sauvegarder l'article",
"page.keyboard_shortcuts.remove_feed": "Supprimer ce flux",
"page.keyboard_shortcuts.go_to_search": "Mettre le focus sur le champ de recherche",
"page.keyboard_shortcuts.close_modal": "Fermer la boite de dialogue",
"page.users.title": "Utilisateurs",
@ -976,6 +979,7 @@ var translations = map[string]string{
"page.keyboard_shortcuts.download_content": "Download originele content",
"page.keyboard_shortcuts.toggle_bookmark_status": "Ster toevoegen/weghalen",
"page.keyboard_shortcuts.save_article": "Artikel opslaan",
"page.keyboard_shortcuts.remove_feed": "Verwijder deze feed",
"page.keyboard_shortcuts.go_to_search": "Focus instellen op zoekformulier",
"page.keyboard_shortcuts.close_modal": "Sluit dialoogscherm",
"page.users.title": "Gebruikers",
@ -1264,6 +1268,7 @@ var translations = map[string]string{
"page.keyboard_shortcuts.download_content": "Pobierz oryginalną zawartość",
"page.keyboard_shortcuts.toggle_bookmark_status": "Dodaj/usuń zakładki",
"page.keyboard_shortcuts.save_article": "Zapisz artykuł",
"page.keyboard_shortcuts.remove_feed": "Usuń ten kanał",
"page.keyboard_shortcuts.go_to_search": "Ustaw fokus na formularzu wyszukiwania",
"page.keyboard_shortcuts.close_modal": "Zamknij listę skrótów klawiszowych",
"page.users.title": "Użytkownicy",
@ -1559,6 +1564,7 @@ var translations = map[string]string{
"page.keyboard_shortcuts.download_content": "Загрузить оригинальное содержимое",
"page.keyboard_shortcuts.toggle_bookmark_status": "Переключатель избранного",
"page.keyboard_shortcuts.save_article": "Сохранить статью",
"page.keyboard_shortcuts.remove_feed": "Удалить эту подписку",
"page.keyboard_shortcuts.go_to_search": "Установить фокус в поисковой форме",
"page.keyboard_shortcuts.close_modal": "Закрыть модальный диалог",
"page.users.title": "Пользователи",
@ -1832,6 +1838,7 @@ var translations = map[string]string{
"page.keyboard_shortcuts.download_content": "下载原始内容",
"page.keyboard_shortcuts.toggle_bookmark_status": "切换收藏状态",
"page.keyboard_shortcuts.save_article": "保存文章",
"page.keyboard_shortcuts.remove_feed": "删除此Feed",
"page.keyboard_shortcuts.go_to_search": "将重点放在搜索表单上",
"page.keyboard_shortcuts.close_modal": "关闭模态对话窗口",
"page.users.title": "用户",
@ -1991,11 +1998,11 @@ var translations = map[string]string{
}
var translationsChecksums = map[string]string{
"de_DE": "099dea24a10c4f842674db2ae44f99e99b9c880a6f83e3a42502603fa228fd32",
"en_US": "c23d1f16d1dbea72c1e1ba558c7a9c25e0ee8ffda420d50c998efe2fb4d9aa55",
"fr_FR": "a5afa30bb63cba48fe0c2114a5e0bcb62dee7f1df0eb5748524decd280c80970",
"nl_NL": "b1e548c2b21f013b1b54a07df7df7c06c776cbd7d26fc1fed288bd6970e99c3c",
"pl_PL": "8cb856dede8b4f75e4c6aeb0a45f09507c5010f782692e887aae357e99674218",
"ru_RU": "0544db0800811fc678521b2e9a7141380919b9ae259b3158524619bf120600ab",
"zh_CN": "92687fecfaaf74489714c52903987e7027d8c1cda45b60aa081f7b2165ccaed5",
"de_DE": "67d3a4bb4e3985ff62882ad0dc73dd86137e474d3e33d41162701a7228913b0a",
"en_US": "db7298b54554207287e2ba15de03646164774368dad54e00197f0162d541643f",
"fr_FR": "fb08492db1984800e5e095f0f784b9b430caa8172c7e0ecbbbc7de08f4adfa60",
"nl_NL": "3bdd3e0150878bc9c196300c7cacb30efd01b7b5df3926950c443d01084b9cee",
"pl_PL": "b81ddb5c5955b043c571701f84d9f3f0a75574e69240c458c7a42f3a20a5aa7e",
"ru_RU": "343393224e21437009e047deb728e4ed036646ed3e4df6101a6bc3cd084b9996",
"zh_CN": "74aefbf6be418ea198fa70d6ed5c7dd8bb3b7b112d9c3f90ebff41fba8c314a3",
}

View file

@ -123,6 +123,7 @@
"page.keyboard_shortcuts.download_content": "Vollständigen Inhalt herunterladen",
"page.keyboard_shortcuts.toggle_bookmark_status": "Lesezeichen hinzufügen/entfernen",
"page.keyboard_shortcuts.save_article": "Artikel speichern",
"page.keyboard_shortcuts.remove_feed": "Dieses Abonnement entfernen",
"page.keyboard_shortcuts.go_to_search": "Fokus auf das Suchformular setzen",
"page.keyboard_shortcuts.close_modal": "Liste der Tastenkürzel schließen",
"page.users.title": "Benutzer",

View file

@ -123,6 +123,7 @@
"page.keyboard_shortcuts.download_content": "Download original content",
"page.keyboard_shortcuts.toggle_bookmark_status": "Toggle bookmark",
"page.keyboard_shortcuts.save_article": "Save article",
"page.keyboard_shortcuts.remove_feed": "Remove this feed",
"page.keyboard_shortcuts.go_to_search": "Set focus on search form",
"page.keyboard_shortcuts.close_modal": "Close modal dialog",
"page.users.title": "Users",

View file

@ -123,6 +123,7 @@
"page.keyboard_shortcuts.download_content": "Télécharger le contenu original",
"page.keyboard_shortcuts.toggle_bookmark_status": "Ajouter/Enlever favoris",
"page.keyboard_shortcuts.save_article": "Sauvegarder l'article",
"page.keyboard_shortcuts.remove_feed": "Supprimer ce flux",
"page.keyboard_shortcuts.go_to_search": "Mettre le focus sur le champ de recherche",
"page.keyboard_shortcuts.close_modal": "Fermer la boite de dialogue",
"page.users.title": "Utilisateurs",

View file

@ -124,6 +124,7 @@
"page.keyboard_shortcuts.download_content": "Download originele content",
"page.keyboard_shortcuts.toggle_bookmark_status": "Ster toevoegen/weghalen",
"page.keyboard_shortcuts.save_article": "Artikel opslaan",
"page.keyboard_shortcuts.remove_feed": "Verwijder deze feed",
"page.keyboard_shortcuts.go_to_search": "Focus instellen op zoekformulier",
"page.keyboard_shortcuts.close_modal": "Sluit dialoogscherm",
"page.users.title": "Gebruikers",

View file

@ -125,6 +125,7 @@
"page.keyboard_shortcuts.download_content": "Pobierz oryginalną zawartość",
"page.keyboard_shortcuts.toggle_bookmark_status": "Dodaj/usuń zakładki",
"page.keyboard_shortcuts.save_article": "Zapisz artykuł",
"page.keyboard_shortcuts.remove_feed": "Usuń ten kanał",
"page.keyboard_shortcuts.go_to_search": "Ustaw fokus na formularzu wyszukiwania",
"page.keyboard_shortcuts.close_modal": "Zamknij listę skrótów klawiszowych",
"page.users.title": "Użytkownicy",

View file

@ -125,6 +125,7 @@
"page.keyboard_shortcuts.download_content": "Загрузить оригинальное содержимое",
"page.keyboard_shortcuts.toggle_bookmark_status": "Переключатель избранного",
"page.keyboard_shortcuts.save_article": "Сохранить статью",
"page.keyboard_shortcuts.remove_feed": "Удалить эту подписку",
"page.keyboard_shortcuts.go_to_search": "Установить фокус в поисковой форме",
"page.keyboard_shortcuts.close_modal": "Закрыть модальный диалог",
"page.users.title": "Пользователи",

View file

@ -121,6 +121,7 @@
"page.keyboard_shortcuts.download_content": "下载原始内容",
"page.keyboard_shortcuts.toggle_bookmark_status": "切换收藏状态",
"page.keyboard_shortcuts.save_article": "保存文章",
"page.keyboard_shortcuts.remove_feed": "删除此Feed",
"page.keyboard_shortcuts.go_to_search": "将重点放在搜索表单上",
"page.keyboard_shortcuts.close_modal": "关闭模态对话窗口",
"page.users.title": "用户",

View file

@ -208,6 +208,7 @@ var templateCommonMap = map[string]string{
<li>{{ t "page.keyboard_shortcuts.download_content" }} = <strong>d</strong></li>
<li>{{ t "page.keyboard_shortcuts.toggle_bookmark_status" }} = <strong>f</strong></li>
<li>{{ t "page.keyboard_shortcuts.save_article" }} = <strong>s</strong></li>
<li>{{ t "page.keyboard_shortcuts.remove_feed" }} = <strong>#</strong></li>
<li>{{ t "page.keyboard_shortcuts.go_to_search" }} = <strong>/</strong></li>
<li>{{ t "page.keyboard_shortcuts.close_modal" }} = <strong>Esc</strong></li>
</ul>
@ -243,6 +244,6 @@ var templateCommonMap = map[string]string{
var templateCommonMapChecksums = map[string]string{
"entry_pagination": "4faa91e2eae150c5e4eab4d258e039dfdd413bab7602f0009360e6d52898e353",
"item_meta": "34deb081a054f2948ad808bdb2c8603d6ab00c58f2f50c4ead0b47ae092888eb",
"layout": "d1795cedbbc0fc6ec7a5e31039e10b8361b7a74bcca74d860f127ac159036ab6",
"layout": "69ba8db45fde768cf71bab7dba60cece39968a185cec58850582dec11668efec",
"pagination": "3386e90c6e1230311459e9a484629bc5d5bf39514a75ef2e73bbbc61142f7abb",
}

View file

@ -134,6 +134,7 @@
<li>{{ t "page.keyboard_shortcuts.download_content" }} = <strong>d</strong></li>
<li>{{ t "page.keyboard_shortcuts.toggle_bookmark_status" }} = <strong>f</strong></li>
<li>{{ t "page.keyboard_shortcuts.save_article" }} = <strong>s</strong></li>
<li>{{ t "page.keyboard_shortcuts.remove_feed" }} = <strong>#</strong></li>
<li>{{ t "page.keyboard_shortcuts.go_to_search" }} = <strong>/</strong></li>
<li>{{ t "page.keyboard_shortcuts.close_modal" }} = <strong>Esc</strong></li>
</ul>

View file

@ -92,6 +92,7 @@
<div class="alert alert-error">
<a href="#"
data-confirm="true"
data-action="remove-feed"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
@ -101,4 +102,4 @@
</div>
{{ end }}
{{ end }}
{{ end }}

View file

@ -15,6 +15,17 @@
<li>
<a href="{{ route "editFeed" "feedID" .feed.ID }}">{{ t "menu.edit_feed" }}</a>
</li>
<li>
<a href="#"
data-confirm="true"
data-action="remove-feed"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "removeFeed" "feedID" .feed.ID }}"
data-redirect-url="{{ route "feeds" }}">{{ t "action.remove_feed" }}</a>
</li>
</ul>
</section>

View file

@ -492,6 +492,7 @@ var templateViewsMap = map[string]string{
<div class="alert alert-error">
<a href="#"
data-confirm="true"
data-action="remove-feed"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
@ -501,7 +502,8 @@ var templateViewsMap = map[string]string{
</div>
{{ end }}
{{ end }}`,
{{ end }}
`,
"edit_user": `{{ define "title"}}{{ t "page.edit_user.title" .selected_user.Username }}{{ end }}
{{ define "content"}}
@ -696,6 +698,17 @@ var templateViewsMap = map[string]string{
<li>
<a href="{{ route "editFeed" "feedID" .feed.ID }}">{{ t "menu.edit_feed" }}</a>
</li>
<li>
<a href="#"
data-confirm="true"
data-action="remove-feed"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "removeFeed" "feedID" .feed.ID }}"
data-redirect-url="{{ route "feeds" }}">{{ t "action.remove_feed" }}</a>
</li>
</ul>
</section>
@ -1377,10 +1390,10 @@ var templateViewsMapChecksums = map[string]string{
"create_category": "6b22b5ce51abf4e225e23a79f81be09a7fb90acb265e93a8faf9446dff74018d",
"create_user": "1e940be3afefc0a5c6273bbadcddc1e29811e9548e5227ac2adfe697ca5ce081",
"edit_category": "daf073d2944a180ce5aaeb80b597eb69597a50dff55a9a1d6cf7938b48d768cb",
"edit_feed": "191c4c1f73e9f8d16938ec8cbd29afc7d7d1d17777394094bcf414ab198d9e51",
"edit_feed": "ab30c31a4385a7b16c54baa78bdcb93a57181ed1c5018ce097d7eb50673bb995",
"edit_user": "f4f99412ba771cfca2a2a42778b023b413c5494e9a287053ba8cf380c2865c5f",
"entry": "2ea9fee1ae5513ef1abb5923221c2ef1212e26d3bb651da66069ce8a336cbb7c",
"feed_entries": "814b58b106313d53f2929e5257c79e47a7e09d715e493f150d9dc3a8c97eaa4e",
"feed_entries": "0f3d02d820475a3b0e165e10be2bf39fa5ab6a9dc5b0945c9ad886434e64e6ca",
"feeds": "31acc253c547a6cce5710d72a6f6b3b396162ecd5e5af295b2cf47c1ff55bd06",
"history_entries": "b65ca1d85615caa7c314a33f1cb997aa3477a79e66b9894b2fd387271ad467d2",
"import": "8349e47a783bb40d8e9248b4771656e5f006185e11079e1c4680dd52633420ed",

View file

@ -56,6 +56,7 @@ static saveEntry(element){if(element.dataset.completed){return;}
element.innerHTML=element.dataset.labelLoading;let request=new RequestBuilder(element.dataset.saveUrl);request.withCallback(()=>{element.innerHTML=element.dataset.labelDone;element.dataset.completed=true;});request.execute();}
static fetchOriginalContent(element){if(element.dataset.completed){return;}
element.innerHTML=element.dataset.labelLoading;let request=new RequestBuilder(element.dataset.fetchContentUrl);request.withCallback((response)=>{element.innerHTML=element.dataset.labelDone;element.dataset.completed=true;response.json().then((data)=>{if(data.hasOwnProperty("content")){document.querySelector(".entry-content").innerHTML=data.content;}});});request.execute();}}
class FeedHandler{static unsubscribe(feedUrl,callback){let request=new RequestBuilder(feedUrl);request.withCallback(callback);request.execute();}}
class ConfirmHandler{executeRequest(url,redirectURL){let request=new RequestBuilder(url);request.withCallback(()=>{if(redirectURL){window.location.href=redirectURL;}else{window.location.reload();}});request.execute();}
handle(event){let questionElement=document.createElement("span");let linkElement=event.target;let containerElement=linkElement.parentNode;linkElement.style.display="none";let yesElement=document.createElement("a");yesElement.href="#";yesElement.appendChild(document.createTextNode(linkElement.dataset.labelYes));yesElement.onclick=(event)=>{event.preventDefault();let loadingElement=document.createElement("span");loadingElement.className="loading";loadingElement.appendChild(document.createTextNode(linkElement.dataset.labelLoading));questionElement.remove();containerElement.appendChild(loadingElement);this.executeRequest(linkElement.dataset.url,linkElement.dataset.redirectUrl);};let noElement=document.createElement("a");noElement.href="#";noElement.appendChild(document.createTextNode(linkElement.dataset.labelNo));noElement.onclick=(event)=>{event.preventDefault();linkElement.style.display="inline";questionElement.remove();};questionElement.className="confirm";questionElement.appendChild(document.createTextNode(linkElement.dataset.labelQuestion+" "));questionElement.appendChild(yesElement);questionElement.appendChild(document.createTextNode(", "));questionElement.appendChild(noElement);containerElement.appendChild(questionElement);}}
class MenuHandler{clickMenuListItem(event){let element=event.target;if(element.tagName==="A"){window.location.href=element.getAttribute("href");}else{window.location.href=element.querySelector("a").getAttribute("href");}}
@ -80,6 +81,7 @@ toggleBookmarkLink(parent){let bookmarkLink=parent.querySelector("a[data-toggle-
openOriginalLink(){let entryLink=document.querySelector(".entry h1 a");if(entryLink!==null){DomHelper.openNewTab(entryLink.getAttribute("href"));return;}
let currentItemOriginalLink=document.querySelector(".current-item a[data-original-link]");if(currentItemOriginalLink!==null){DomHelper.openNewTab(currentItemOriginalLink.getAttribute("href"));let currentItem=document.querySelector(".current-item");this.goToNextListItem();EntryHandler.markEntryAsRead(currentItem);}}
openSelectedItem(){let currentItemLink=document.querySelector(".current-item .item-title a");if(currentItemLink!==null){window.location.href=currentItemLink.getAttribute("href");}}
unsubscribeFromFeed(){let unsubscribeLinks=document.querySelectorAll("[data-action=remove-feed]");if(unsubscribeLinks.length===1){let unsubscribeLink=unsubscribeLinks[0];FeedHandler.unsubscribe(unsubscribeLink.dataset.url,()=>{if(unsubscribeLink.dataset.redirectUrl){window.location.href=unsubscribeLink.dataset.redirectUrl;}else{window.location.reload();}});}}
goToPage(page,fallbackSelf){let element=document.querySelector("a[data-page="+page+"]");if(element){document.location.href=element.href;}else if(fallbackSelf){window.location.reload();}}
goToPrevious(){if(this.isListView()){this.goToPreviousListItem();}else{this.goToPage("previous");}}
goToNext(){if(this.isListView()){this.goToNextListItem();}else{this.goToPage("next");}}
@ -93,13 +95,13 @@ for(let i=0;i<items.length;i++){if(items[i].classList.contains("current-item")){
break;}}}
isListView(){return document.querySelector(".items")!==null;}}
class LinkStateHandler{static flip(element){let labelElement=document.createElement("span");labelElement.className="link-flipped-state";labelElement.appendChild(document.createTextNode(element.dataset.labelNewState));element.parentNode.appendChild(labelElement);element.parentNode.removeChild(element);}}
document.addEventListener("DOMContentLoaded",function(){FormHandler.handleSubmitButtons();let touchHandler=new TouchHandler();touchHandler.listen();let navHandler=new NavHandler();let keyboardHandler=new KeyboardHandler();keyboardHandler.on("g u",()=>navHandler.goToPage("unread"));keyboardHandler.on("g b",()=>navHandler.goToPage("starred"));keyboardHandler.on("g h",()=>navHandler.goToPage("history"));keyboardHandler.on("g f",()=>navHandler.goToPage("feeds"));keyboardHandler.on("g c",()=>navHandler.goToPage("categories"));keyboardHandler.on("g s",()=>navHandler.goToPage("settings"));keyboardHandler.on("ArrowLeft",()=>navHandler.goToPrevious());keyboardHandler.on("ArrowRight",()=>navHandler.goToNext());keyboardHandler.on("j",()=>navHandler.goToPrevious());keyboardHandler.on("p",()=>navHandler.goToPrevious());keyboardHandler.on("k",()=>navHandler.goToNext());keyboardHandler.on("n",()=>navHandler.goToNext());keyboardHandler.on("h",()=>navHandler.goToPage("previous"));keyboardHandler.on("l",()=>navHandler.goToPage("next"));keyboardHandler.on("o",()=>navHandler.openSelectedItem());keyboardHandler.on("v",()=>navHandler.openOriginalLink());keyboardHandler.on("m",()=>navHandler.toggleEntryStatus());keyboardHandler.on("A",()=>navHandler.markPageAsRead());keyboardHandler.on("s",()=>navHandler.saveEntry());keyboardHandler.on("d",()=>navHandler.fetchOriginalContent());keyboardHandler.on("f",()=>navHandler.toggleBookmark());keyboardHandler.on("?",()=>navHandler.showKeyboardShortcuts());keyboardHandler.on("/",(e)=>navHandler.setFocusToSearchInput(e));keyboardHandler.on("Escape",()=>ModalHandler.close());keyboardHandler.listen();let mouseHandler=new MouseHandler();mouseHandler.onClick("a[data-save-entry]",(event)=>{EntryHandler.saveEntry(event.target);});mouseHandler.onClick("a[data-toggle-bookmark]",(event)=>{EntryHandler.toggleBookmark(event.target);});mouseHandler.onClick("a[data-toggle-status]",(event)=>{let currentItem=DomHelper.findParent(event.target,"entry");if(!currentItem){currentItem=DomHelper.findParent(event.target,"item");}
document.addEventListener("DOMContentLoaded",function(){FormHandler.handleSubmitButtons();let touchHandler=new TouchHandler();touchHandler.listen();let navHandler=new NavHandler();let keyboardHandler=new KeyboardHandler();keyboardHandler.on("g u",()=>navHandler.goToPage("unread"));keyboardHandler.on("g b",()=>navHandler.goToPage("starred"));keyboardHandler.on("g h",()=>navHandler.goToPage("history"));keyboardHandler.on("g f",()=>navHandler.goToPage("feeds"));keyboardHandler.on("g c",()=>navHandler.goToPage("categories"));keyboardHandler.on("g s",()=>navHandler.goToPage("settings"));keyboardHandler.on("ArrowLeft",()=>navHandler.goToPrevious());keyboardHandler.on("ArrowRight",()=>navHandler.goToNext());keyboardHandler.on("j",()=>navHandler.goToPrevious());keyboardHandler.on("p",()=>navHandler.goToPrevious());keyboardHandler.on("k",()=>navHandler.goToNext());keyboardHandler.on("n",()=>navHandler.goToNext());keyboardHandler.on("h",()=>navHandler.goToPage("previous"));keyboardHandler.on("l",()=>navHandler.goToPage("next"));keyboardHandler.on("o",()=>navHandler.openSelectedItem());keyboardHandler.on("v",()=>navHandler.openOriginalLink());keyboardHandler.on("m",()=>navHandler.toggleEntryStatus());keyboardHandler.on("A",()=>navHandler.markPageAsRead());keyboardHandler.on("s",()=>navHandler.saveEntry());keyboardHandler.on("d",()=>navHandler.fetchOriginalContent());keyboardHandler.on("f",()=>navHandler.toggleBookmark());keyboardHandler.on("?",()=>navHandler.showKeyboardShortcuts());keyboardHandler.on("#",()=>navHandler.unsubscribeFromFeed());keyboardHandler.on("/",(e)=>navHandler.setFocusToSearchInput(e));keyboardHandler.on("Escape",()=>ModalHandler.close());keyboardHandler.listen();let mouseHandler=new MouseHandler();mouseHandler.onClick("a[data-save-entry]",(event)=>{EntryHandler.saveEntry(event.target);});mouseHandler.onClick("a[data-toggle-bookmark]",(event)=>{EntryHandler.toggleBookmark(event.target);});mouseHandler.onClick("a[data-toggle-status]",(event)=>{let currentItem=DomHelper.findParent(event.target,"entry");if(!currentItem){currentItem=DomHelper.findParent(event.target,"item");}
if(currentItem){EntryHandler.toggleEntryStatus(currentItem);}});mouseHandler.onClick("a[data-fetch-content-entry]",(event)=>{EntryHandler.fetchOriginalContent(event.target);});mouseHandler.onClick("a[data-on-click=markPageAsRead]",()=>navHandler.markPageAsRead());mouseHandler.onClick("a[data-confirm]",(event)=>{(new ConfirmHandler()).handle(event);});mouseHandler.onClick("a[data-action=search]",(event)=>{navHandler.setFocusToSearchInput(event);});mouseHandler.onClick("a[data-link-state=flip]",(event)=>{LinkStateHandler.flip(event.target);},true);if(document.documentElement.clientWidth<600){let menuHandler=new MenuHandler();mouseHandler.onClick(".logo",()=>menuHandler.toggleMainMenu());mouseHandler.onClick(".header nav li",(event)=>menuHandler.clickMenuListItem(event));}
if("serviceWorker"in navigator){let scriptElement=document.getElementById("service-worker-script");if(scriptElement){navigator.serviceWorker.register(scriptElement.src);}}});})();`,
"sw": `'use strict';self.addEventListener("fetch",(event)=>{if(event.request.url.includes("/feed/icon/")){event.respondWith(caches.open("feed_icons").then((cache)=>{return cache.match(event.request).then((response)=>{return response||fetch(event.request).then((response)=>{cache.put(event.request,response.clone());return response;});});}));}});`,
}
var JavascriptsChecksums = map[string]string{
"app": "205a1e308450a89d71ecf7d278718fd030a13d97cd1b49f75855029cc5ff613c",
"app": "6d1dc775cab31cdb7275e38c32c4ae714e330df897c7515e84e021878359e3d5",
"sw": "55fffa223919cc18572788fb9c62fccf92166c0eb5d3a1d6f91c31f24d020be9",
}

View file

@ -28,6 +28,7 @@ document.addEventListener("DOMContentLoaded", function() {
keyboardHandler.on("d", () => navHandler.fetchOriginalContent());
keyboardHandler.on("f", () => navHandler.toggleBookmark());
keyboardHandler.on("?", () => navHandler.showKeyboardShortcuts());
keyboardHandler.on("#", () => navHandler.unsubscribeFromFeed());
keyboardHandler.on("/", (e) => navHandler.setFocusToSearchInput(e));
keyboardHandler.on("Escape", () => ModalHandler.close());
keyboardHandler.listen();

View file

@ -0,0 +1,7 @@
class FeedHandler {
static unsubscribe(feedUrl, callback) {
let request = new RequestBuilder(feedUrl);
request.withCallback(callback);
request.execute();
}
}

View file

@ -129,6 +129,20 @@ class NavHandler {
}
}
unsubscribeFromFeed() {
let unsubscribeLinks = document.querySelectorAll("[data-action=remove-feed]");
if (unsubscribeLinks.length === 1) {
let unsubscribeLink = unsubscribeLinks[0];
FeedHandler.unsubscribe(unsubscribeLink.dataset.url, () => {
if (unsubscribeLink.dataset.redirectUrl) {
window.location.href = unsubscribeLink.dataset.redirectUrl;
} else {
window.location.reload();
}
});
}
}
/**
* @param {string} page Page to redirect to.
* @param {boolean} fallbackSelf Refresh actual page if the page is not found.