From 0e6fc2db1ec04d82154517202a5cc1339dcce250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Guillot?= Date: Mon, 4 Dec 2017 20:40:23 -0800 Subject: [PATCH] Improve naviguation --- server/static/js.go | 20 ++++++++--------- server/static/js/app.js | 42 +++++++++++++++++++++++++++-------- server/ui/controller/entry.go | 19 ++++++++-------- 3 files changed, 53 insertions(+), 28 deletions(-) diff --git a/server/static/js.go b/server/static/js.go index 15f83a37..a328b050 100644 --- a/server/static/js.go +++ b/server/static/js.go @@ -1,5 +1,5 @@ // Code generated by go generate; DO NOT EDIT. -// 2017-12-03 17:25:29.409871548 -0800 PST m=+0.022898473 +// 2017-12-04 20:40:04.511740583 -0800 PST m=+0.012182340 package static @@ -41,30 +41,30 @@ withCallback(callback){this.callback=callback;return this;} getCsrfToken(){let element=document.querySelector("meta[name=X-CSRF-Token]");if(element!==null){return element.getAttribute("value");} return "";} execute(){fetch(new Request(this.url,this.options)).then((response)=>{if(this.callback){this.callback(response);}});}} -class EntryHandler{static updateEntriesStatus(entryIDs,status){let url=document.body.dataset.entriesStatusUrl;let request=new RequestBuilder(url);request.withBody({entry_ids:entryIDs,status:status});request.execute();} +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();} 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);break;}}} +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;} 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();}} class ConfirmHandler{remove(url){let request=new RequestBuilder(url);request.withCallback(()=>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.remove(linkElement.dataset.url);};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");}} toggleMainMenu(){let menu=document.querySelector(".header nav ul");if(DomHelper.isVisible(menu)){menu.style.display="none";}else{menu.style.display="block";}}} -class NavHandler{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");} +class NavHandler{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);}}} -toggleEntryStatus(){let currentItem=document.querySelector(".current-item");if(currentItem!==null){EntryHandler.toggleEntryStatus(currentItem);this.goToNextListItem();}} +toggleEntryStatus(){let currentItem=document.querySelector(".current-item");if(currentItem!==null){this.goToNextListItem();EntryHandler.toggleEntryStatus(currentItem);}} 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 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");}} -goToPage(page){let element=document.querySelector("a[data-page="+page+"]");if(element){document.location.href=element.href;}} +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");}} goToPreviousListItem(){let items=DomHelper.getVisibleElements(".items .item");if(items.length===0){return;} if(document.querySelector(".current-item")===null){items[0].classList.add("current-item");return;} for(let i=0;i=0){items[i-1].classList.add("current-item");DomHelper.scrollPageTo(items[i-1]);} break;}}} -goToNextListItem(){let items=DomHelper.getVisibleElements(".items .item");if(items.length===0){return;} -if(document.querySelector(".current-item")===null){items[0].classList.add("current-item");return;} +goToNextListItem(){let currentItem=document.querySelector(".current-item");let items=DomHelper.getVisibleElements(".items .item");if(items.length===0){return;} +if(currentItem===null){items[0].classList.add("current-item");return;} for(let i=0;i 0) { - EntryHandler.updateEntriesStatus(entryIDs, "read"); + EntryHandler.updateEntriesStatus(entryIDs, "read", () => { + // This callback make sure the Ajax request reach the server before we reload the page. + this.goToPage("next", true); + }); } - - this.goToPage("next"); } saveEntry() { @@ -421,8 +433,10 @@ class NavHandler { toggleEntryStatus() { let currentItem = document.querySelector(".current-item"); if (currentItem !== null) { - EntryHandler.toggleEntryStatus(currentItem); + // The order is important here, + // On the unread page, the read item will be hidden. this.goToNextListItem(); + EntryHandler.toggleEntryStatus(currentItem); } } @@ -436,6 +450,11 @@ class NavHandler { let currentItemOriginalLink = document.querySelector(".current-item a[data-original-link]"); if (currentItemOriginalLink !== null) { DomHelper.openNewTab(currentItemOriginalLink.getAttribute("href")); + + // Move to the next item and if we are on the unread page mark this item as read. + let currentItem = document.querySelector(".current-item"); + this.goToNextListItem(); + EntryHandler.markEntryAsRead(currentItem); } } @@ -446,11 +465,17 @@ class NavHandler { } } - goToPage(page) { + /** + * @param {string} page Page to redirect to. + * @param {boolean} fallbackSelf Refresh actual page if the page is not found. + */ + goToPage(page, fallbackSelf) { let element = document.querySelector("a[data-page=" + page + "]"); if (element) { document.location.href = element.href; + } else if (fallbackSelf) { + window.location.reload(); } } @@ -472,7 +497,6 @@ class NavHandler { goToPreviousListItem() { let items = DomHelper.getVisibleElements(".items .item"); - if (items.length === 0) { return; } @@ -497,13 +521,13 @@ class NavHandler { } goToNextListItem() { + let currentItem = document.querySelector(".current-item"); let items = DomHelper.getVisibleElements(".items .item"); - if (items.length === 0) { return; } - if (document.querySelector(".current-item") === null) { + if (currentItem === null) { items[0].classList.add("current-item"); return; } diff --git a/server/ui/controller/entry.go b/server/ui/controller/entry.go index 933f0c63..309fa09e 100644 --- a/server/ui/controller/entry.go +++ b/server/ui/controller/entry.go @@ -191,15 +191,6 @@ func (c *Controller) ShowUnreadEntry(ctx *core.Context, request *core.Request, r return } - if entry.Status == model.EntryStatusUnread { - err = c.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) - if err != nil { - log.Println(err) - response.HTML().ServerError(nil) - return - } - } - args, err := c.getCommonTemplateArgs(ctx) if err != nil { response.HTML().ServerError(err) @@ -225,6 +216,16 @@ func (c *Controller) ShowUnreadEntry(ctx *core.Context, request *core.Request, r prevEntryRoute = ctx.Route("unreadEntry", "entryID", prevEntry.ID) } + // We change the status here, otherwise we cannot get the pagination for unread items. + if entry.Status == model.EntryStatusUnread { + err = c.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) + if err != nil { + log.Println(err) + response.HTML().ServerError(nil) + return + } + } + response.HTML().Render("entry", args.Merge(tplParams{ "entry": entry, "prevEntry": prevEntry,