Add toggle status button to entry page

This commit is contained in:
Dave Z 2018-08-28 23:44:34 -04:00 committed by Frédéric Guillot
parent 4f62a704e2
commit 5341bbcbe2
11 changed files with 57 additions and 30 deletions

View File

@ -1,13 +1,22 @@
{{ define "title"}}{{ .entry.Title }}{{ end }}
{{ define "content"}}
<section class="entry">
<section class="entry" data-id="{{ .entry.ID }}">
<header class="entry-header">
<h1>
<a href="{{ .entry.URL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .entry.Title }}</a>
</h1>
<div class="entry-actions">
<ul>
<li>
<a href="#"
title="{{ t "Change entry status" }}"
data-toggle-status="true"
data-label-read="✔ {{ t "Read" }}"
data-label-unread="✘ {{ t "Unread" }}"
data-value="{{ if eq .entry.Status "read" }}read{{ else }}unread{{ end }}"
>{{ if eq .entry.Status "read" }}✘ {{ t "Unread" }}{{ else }}✔ {{ t "Read" }}{{ end }}</a>
</li>
<li>
<a href="#"
data-toggle-bookmark="true"

View File

@ -539,13 +539,22 @@ var templateViewsMap = map[string]string{
"entry": `{{ define "title"}}{{ .entry.Title }}{{ end }}
{{ define "content"}}
<section class="entry">
<section class="entry" data-id="{{ .entry.ID }}">
<header class="entry-header">
<h1>
<a href="{{ .entry.URL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .entry.Title }}</a>
</h1>
<div class="entry-actions">
<ul>
<li>
<a href="#"
title="{{ t "Change entry status" }}"
data-toggle-status="true"
data-label-read="✔ {{ t "Read" }}"
data-label-unread="✘ {{ t "Unread" }}"
data-value="{{ if eq .entry.Status "read" }}read{{ else }}unread{{ end }}"
>{{ if eq .entry.Status "read" }} {{ t "Unread" }}{{ else }} {{ t "Read" }}{{ end }}</a>
</li>
<li>
<a href="#"
data-toggle-bookmark="true"
@ -1333,7 +1342,7 @@ var templateViewsMapChecksums = map[string]string{
"edit_category": "cee720faadcec58289b707ad30af623d2ee66c1ce23a732965463250d7ff41c5",
"edit_feed": "1a8e342e4fac80e8b9c73537c7fe8aaf7f9e3e7af22f411927010897dd37e9c3",
"edit_user": "7373e09f805e6c017167001519b9feb04226be6c81c2875cbacd5ce94f2c24bf",
"entry": "c838ae7f19d775d473ee92c4999eb28a5063869411c834d26a3f4b686ce88dbf",
"entry": "26d377a72b9c4f001b769d8a5971a6a9dfff66c87dcfa32c7ab64a5ae138ffd7",
"feed_entries": "76e7e32cdd6552304e38931459fe12cefdb5354f65a7ac3bbed52a2979896d3e",
"feeds": "1006698abfe0962b656f27794bc44568515392da72b6fac0c03316de06024237",
"history_entries": "f94e15d37d7604500cede7b583e03bf79c06be81c6597a4a43693f5712af2e13",

View File

@ -56,6 +56,8 @@ func (c *Controller) ShowStarredEntry(w http.ResponseWriter, r *http.Request) {
html.ServerError(w, nil)
return
}
entry.Status = model.EntryStatusRead
}
entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection)

View File

@ -63,6 +63,8 @@ func (c *Controller) ShowCategoryEntry(w http.ResponseWriter, r *http.Request) {
html.ServerError(w, nil)
return
}
entry.Status = model.EntryStatusRead
}
entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection)

View File

@ -63,6 +63,8 @@ func (c *Controller) ShowFeedEntry(w http.ResponseWriter, r *http.Request) {
html.ServerError(w, nil)
return
}
entry.Status = model.EntryStatusRead
}
entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection)

View File

@ -58,6 +58,8 @@ func (c *Controller) ShowSearchEntry(w http.ResponseWriter, r *http.Request) {
html.ServerError(w, nil)
return
}
entry.Status = model.EntryStatusRead
}
entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection)

View File

@ -84,6 +84,7 @@ func (c *Controller) ShowUnreadEntry(w http.ResponseWriter, r *http.Request) {
html.ServerError(w, nil)
return
}
entry.Status = model.EntryStatusRead
sess := session.New(c.store, ctx)
view := view.New(c.tpl, ctx, sess)

View File

@ -47,9 +47,8 @@ class UnreadCounterHandler{static decrement(n){this.updateValue((current)=>{retu
static increment(n){this.updateValue((current)=>{return current+n;});}
static updateValue(callback){let counterElements=document.querySelectorAll("span.unread-counter");counterElements.forEach((element)=>{let oldValue=parseInt(element.textContent,10);element.innerHTML=callback(oldValue);});if(window.location.href.endsWith('/unread')){let oldValue=parseInt(document.title.split('(')[1],10);let newValue=callback(oldValue);document.title=document.title.replace(/(.*?)\(\d+\)(.*?)/,function(match,prefix,suffix,offset,string){return prefix+'('+newValue+')'+suffix;});}}}
class EntryHandler{static updateEntriesStatus(entryIDs,status,callback){let url=document.body.dataset.entriesStatusUrl;let request=new RequestBuilder(url);request.withBody({entry_ids:entryIDs,status:status});request.withCallback(callback);request.execute();if(status==="read"){UnreadCounterHandler.decrement(1);}else{UnreadCounterHandler.increment(1);}}
static toggleEntryStatus(element){let entryID=parseInt(element.dataset.id,10);let statuses={read:"unread",unread:"read"};for(let currentStatus in statuses){let newStatus=statuses[currentStatus];if(element.classList.contains("item-status-"+currentStatus)){element.classList.remove("item-status-"+currentStatus);element.classList.add("item-status-"+newStatus);this.updateEntriesStatus([entryID],newStatus);let link=element.querySelector("a[data-toggle-status]");if(link){this.toggleLinkStatus(link);}
break;}}}
static toggleLinkStatus(link){if(link.dataset.value==="read"){link.innerHTML=link.dataset.labelRead;link.dataset.value="unread";}else{link.innerHTML=link.dataset.labelUnread;link.dataset.value="read";}}
static toggleEntryStatus(element){let entryID=parseInt(element.dataset.id,10);let link=element.querySelector("a[data-toggle-status]");let currentStatus=link.dataset.value;let newStatus=currentStatus==="read"?"unread":"read";this.updateEntriesStatus([entryID],newStatus);if(currentStatus==="read"){link.innerHTML=link.dataset.labelRead;link.dataset.value="unread";}else{link.innerHTML=link.dataset.labelUnread;link.dataset.value="read";}
if(element.classList.contains("item-status-"+currentStatus)){element.classList.remove("item-status-"+currentStatus);element.classList.add("item-status-"+newStatus);}}
static toggleBookmark(element){element.innerHTML=element.dataset.labelLoading;let request=new RequestBuilder(element.dataset.bookmarkUrl);request.withCallback(()=>{if(element.dataset.value==="star"){element.innerHTML=element.dataset.labelStar;element.dataset.value="unstar";}else{element.innerHTML=element.dataset.labelUnstar;element.dataset.value="star";}});request.execute();}
static markEntryAsRead(element){if(element.classList.contains("item-status-unread")){element.classList.remove("item-status-unread");element.classList.add("item-status-read");let entryID=parseInt(element.dataset.id,10);this.updateEntriesStatus([entryID],"read");}}
static saveEntry(element){if(element.dataset.completed){return;}
@ -72,7 +71,8 @@ showKeyboardShortcuts(){let template=document.getElementById("keyboard-shortcuts
markPageAsRead(){let items=DomHelper.getVisibleElements(".items .item");let entryIDs=[];items.forEach((element)=>{element.classList.add("item-status-read");entryIDs.push(parseInt(element.dataset.id,10));});if(entryIDs.length>0){EntryHandler.updateEntriesStatus(entryIDs,"read",()=>{this.goToPage("next",true);});}}
saveEntry(){if(this.isListView()){let currentItem=document.querySelector(".current-item");if(currentItem!==null){let saveLink=currentItem.querySelector("a[data-save-entry]");if(saveLink){EntryHandler.saveEntry(saveLink);}}}else{let saveLink=document.querySelector("a[data-save-entry]");if(saveLink){EntryHandler.saveEntry(saveLink);}}}
fetchOriginalContent(){if(!this.isListView()){let link=document.querySelector("a[data-fetch-content-entry]");if(link){EntryHandler.fetchOriginalContent(link);}}}
toggleEntryStatus(){let currentItem=document.querySelector(".current-item");if(currentItem!==null){this.goToNextListItem();EntryHandler.toggleEntryStatus(currentItem);}}
toggleEntryStatus(){if(!this.isListView()){EntryHandler.toggleEntryStatus(document.querySelector(".entry"));return;}
let currentItem=document.querySelector(".current-item");if(currentItem!==null){this.goToNextListItem();EntryHandler.toggleEntryStatus(currentItem);}}
toggleBookmark(){if(!this.isListView()){this.toggleBookmarkLink(document.querySelector(".entry"));return;}
let currentItem=document.querySelector(".current-item");if(currentItem!==null){this.toggleBookmarkLink(currentItem);}}
toggleBookmarkLink(parent){let bookmarkLink=parent.querySelector("a[data-toggle-bookmark]");if(bookmarkLink){EntryHandler.toggleBookmark(bookmarkLink);}}
@ -91,12 +91,13 @@ if(currentItem===null){items[0].classList.add("current-item");return;}
for(let i=0;i<items.length;i++){if(items[i].classList.contains("current-item")){items[i].classList.remove("current-item");if(i+1<items.length){items[i+1].classList.add("current-item");DomHelper.scrollPageTo(items[i+1]);}
break;}}}
isListView(){return document.querySelector(".items")!==null;}}
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)=>{event.preventDefault();EntryHandler.saveEntry(event.target);});mouseHandler.onClick("a[data-toggle-bookmark]",(event)=>{event.preventDefault();EntryHandler.toggleBookmark(event.target);});mouseHandler.onClick("a[data-toggle-status]",(event)=>{event.preventDefault();let currentItem=DomHelper.findParent(event.target,"item");if(currentItem){EntryHandler.toggleEntryStatus(currentItem);}});mouseHandler.onClick("a[data-fetch-content-entry]",(event)=>{event.preventDefault();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);});if(document.documentElement.clientWidth<600){let menuHandler=new MenuHandler();mouseHandler.onClick(".logo",()=>menuHandler.toggleMainMenu());mouseHandler.onClick(".header nav li",(event)=>menuHandler.clickMenuListItem(event));}
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)=>{event.preventDefault();EntryHandler.saveEntry(event.target);});mouseHandler.onClick("a[data-toggle-bookmark]",(event)=>{event.preventDefault();EntryHandler.toggleBookmark(event.target);});mouseHandler.onClick("a[data-toggle-status]",(event)=>{event.preventDefault();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)=>{event.preventDefault();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);});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": "4c73b3157f06d6b6689daaa8d0ecb7ea4e12945fff16d28bdf6da02ec4aca72c",
"app": "8a00c50f9b0f5dea54272d03638a18cec2a1815c0ff05492c7f9961138b1166a",
"sw": "55fffa223919cc18572788fb9c62fccf92166c0eb5d3a1d6f91c31f24d020be9",
}

View File

@ -46,7 +46,11 @@ document.addEventListener("DOMContentLoaded", function() {
mouseHandler.onClick("a[data-toggle-status]", (event) => {
event.preventDefault();
let currentItem = DomHelper.findParent(event.target, "item");
let currentItem = DomHelper.findParent(event.target, "entry");
if (! currentItem) {
currentItem = DomHelper.findParent(event.target, "item");
}
if (currentItem) {
EntryHandler.toggleEntryStatus(currentItem);
}

View File

@ -15,35 +15,25 @@ class EntryHandler {
static toggleEntryStatus(element) {
let entryID = parseInt(element.dataset.id, 10);
let statuses = {read: "unread", unread: "read"};
let link = element.querySelector("a[data-toggle-status]");
for (let currentStatus in statuses) {
let newStatus = statuses[currentStatus];
let currentStatus = link.dataset.value;
let newStatus = currentStatus === "read" ? "unread" : "read";
if (element.classList.contains("item-status-" + currentStatus)) {
element.classList.remove("item-status-" + currentStatus);
element.classList.add("item-status-" + newStatus);
this.updateEntriesStatus([entryID], newStatus);
this.updateEntriesStatus([entryID], newStatus);
let link = element.querySelector("a[data-toggle-status]");
if (link) {
this.toggleLinkStatus(link);
}
break;
}
}
}
static toggleLinkStatus(link) {
if (link.dataset.value === "read") {
if (currentStatus === "read") {
link.innerHTML = link.dataset.labelRead;
link.dataset.value = "unread";
} else {
link.innerHTML = link.dataset.labelUnread;
link.dataset.value = "read";
}
if (element.classList.contains("item-status-" + currentStatus)) {
element.classList.remove("item-status-" + currentStatus);
element.classList.add("item-status-" + newStatus);
}
}
static toggleBookmark(element) {

View File

@ -71,6 +71,11 @@ class NavHandler {
}
toggleEntryStatus() {
if (! this.isListView()) {
EntryHandler.toggleEntryStatus(document.querySelector(".entry"));
return;
}
let currentItem = document.querySelector(".current-item");
if (currentItem !== null) {
// The order is important here,