1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-08-01 17:38:37 +00:00

Make entries sorting configurable

This commit is contained in:
Frédéric Guillot 2017-12-02 17:04:01 -08:00
parent 453ff64f29
commit 2f1367a8d4
28 changed files with 253 additions and 193 deletions

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// 2017-11-27 21:07:53.21170439 -0800 PST m=+0.005890618
// 2017-12-02 16:12:47.261744369 -0800 PST m=+0.007253685
package static

File diff suppressed because one or more lines are too long

View file

@ -10,7 +10,7 @@ body {
text-rendering: optimizeLegibility;
}
.main {
main {
padding-left: 5px;
padding-right: 5px;
}

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// 2017-12-02 14:53:55.175825378 -0800 PST m=+0.009022020
// 2017-12-02 16:12:47.268772139 -0800 PST m=+0.014281455
package static

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// 2017-12-02 14:53:55.184002045 -0800 PST m=+0.017198687
// 2017-12-02 16:12:47.286110197 -0800 PST m=+0.031619513
package template
@ -76,9 +76,9 @@ var templateCommonMap = map[string]string{
</nav>
</header>
{{ end }}
<section class="main">
<main>
{{template "content" .}}
</section>
</main>
</body>
</html>
{{ end }}`,
@ -106,6 +106,6 @@ var templateCommonMap = map[string]string{
var templateCommonMapChecksums = map[string]string{
"entry_pagination": "f1465fa70f585ae8043b200ec9de5bf437ffbb0c19fb7aefc015c3555614ee27",
"layout": "0a06f790d6caad2918c5038f7aa4a2f88ff3b31ed8f52749d45344f2be7bee53",
"layout": "d1f96640bf90eca64571cfa4fe73be55b09d1d5a49da85b1ea9f9d4f9c670a07",
"pagination": "6ff462c2b2a53bc5448b651da017f40a39f1d4f16cef4b2f09784f0797286924",
}

View file

@ -51,9 +51,9 @@
</nav>
</header>
{{ end }}
<section class="main">
<main>
{{template "content" .}}
</section>
</main>
</body>
</html>
{{ end }}

View file

@ -58,6 +58,12 @@
{{ end }}
</select>
<label for="form-entry-direction">{{ t "Entry Sorting" }}</label>
<select id="form-entry-direction" name="entry_direction">
<option value="asc" {{ if eq "asc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "Older entries first" }}</option>
<option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "Recent entries first" }}</option>
</select>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "Loading..." }}">{{ t "Update" }}</button>
</div>

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// 2017-12-02 14:53:55.176980108 -0800 PST m=+0.010176750
// 2017-12-02 16:12:47.271430439 -0800 PST m=+0.016939755
package template
@ -922,6 +922,12 @@ var templateViewsMap = map[string]string{
{{ end }}
</select>
<label for="form-entry-direction">{{ t "Entry Sorting" }}</label>
<select id="form-entry-direction" name="entry_direction">
<option value="asc" {{ if eq "asc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "Older entries first" }}</option>
<option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "Recent entries first" }}</option>
</select>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "Loading..." }}">{{ t "Update" }}</button>
</div>
@ -1070,7 +1076,7 @@ var templateViewsMapChecksums = map[string]string{
"integrations": "c485d6d9ed996635e55e73320610e6bcb01a41c1153e8e739ae2294b0b14b243",
"login": "04f3ce79bfa5753f69e0d956c2a8999c0da549c7925634a3e8134975da0b0e0f",
"sessions": "878dbe8f8ea783b44130c495814179519fa5c3aa2666ac87508f94d58dd008bf",
"settings": "1e2df11f5436eb2d05ae1fae30dd6f1362613011edbfcc79ae8b23854fa348b4",
"settings": "ea2505b9d0a6d6bb594dba87a92079de19baa6d494f0651693a7685489fb7de9",
"unread": "3d8deab9119dc11f0d74a461e1ac89dc29931ba4645a043bb5b3eccba3cba5b8",
"users": "44677e28bb5347799ed0020c90ec785aadec4b1454446d92411cfdaf6e32110b",
}

View file

@ -54,7 +54,7 @@ func (c *Controller) ShowCategoryEntries(ctx *core.Context, request *core.Reques
builder := c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithCategoryID(category.ID)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(model.DefaultSortingDirection)
builder.WithDirection(user.EntryDirection)
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage)

View file

@ -11,12 +11,12 @@ import (
"github.com/miniflux/miniflux2/model"
"github.com/miniflux/miniflux2/server/core"
"github.com/miniflux/miniflux2/server/ui/payload"
"github.com/miniflux/miniflux2/storage"
)
// ShowFeedEntry shows a single feed entry in "feed" mode.
func (c *Controller) ShowFeedEntry(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.LoggedUser()
sortingDirection := model.DefaultSortingDirection
entryID, err := request.IntegerParam("entryID")
if err != nil {
@ -46,6 +46,15 @@ func (c *Controller) ShowFeedEntry(ctx *core.Context, request *core.Request, res
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)
@ -53,26 +62,9 @@ func (c *Controller) ShowFeedEntry(ctx *core.Context, request *core.Request, res
}
builder = c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithFeedID(feedID)
builder.WithCondition("e.id", "!=", entryID)
builder.WithCondition("e.published_at", "<=", entry.Date)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(model.DefaultSortingDirection)
nextEntry, err := builder.GetEntry()
if err != nil {
response.HTML().ServerError(err)
return
}
builder = c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithFeedID(feedID)
builder.WithCondition("e.id", "!=", entryID)
builder.WithCondition("e.published_at", ">=", entry.Date)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(model.GetOppositeDirection(sortingDirection))
prevEntry, err := builder.GetEntry()
prevEntry, nextEntry, err := c.getEntryPrevNext(user, builder, entry.ID)
if err != nil {
response.HTML().ServerError(err)
return
@ -88,14 +80,6 @@ func (c *Controller) ShowFeedEntry(ctx *core.Context, request *core.Request, res
prevEntryRoute = ctx.Route("feedEntry", "feedID", feedID, "entryID", prevEntry.ID)
}
if entry.Status == model.EntryStatusUnread {
err = c.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
if err != nil {
response.HTML().ServerError(err)
return
}
}
response.HTML().Render("entry", args.Merge(tplParams{
"entry": entry,
"prevEntry": prevEntry,
@ -109,7 +93,6 @@ func (c *Controller) ShowFeedEntry(ctx *core.Context, request *core.Request, res
// ShowCategoryEntry shows a single feed entry in "category" mode.
func (c *Controller) ShowCategoryEntry(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.LoggedUser()
sortingDirection := model.DefaultSortingDirection
categoryID, err := request.IntegerParam("categoryID")
if err != nil {
@ -139,6 +122,15 @@ func (c *Controller) ShowCategoryEntry(ctx *core.Context, request *core.Request,
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)
@ -146,26 +138,9 @@ func (c *Controller) ShowCategoryEntry(ctx *core.Context, request *core.Request,
}
builder = c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithCategoryID(categoryID)
builder.WithCondition("e.id", "!=", entryID)
builder.WithCondition("e.published_at", "<=", entry.Date)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(sortingDirection)
nextEntry, err := builder.GetEntry()
if err != nil {
response.HTML().ServerError(err)
return
}
builder = c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithCategoryID(categoryID)
builder.WithCondition("e.id", "!=", entryID)
builder.WithCondition("e.published_at", ">=", entry.Date)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(model.GetOppositeDirection(sortingDirection))
prevEntry, err := builder.GetEntry()
prevEntry, nextEntry, err := c.getEntryPrevNext(user, builder, entry.ID)
if err != nil {
response.HTML().ServerError(err)
return
@ -181,15 +156,6 @@ func (c *Controller) ShowCategoryEntry(ctx *core.Context, request *core.Request,
prevEntryRoute = ctx.Route("categoryEntry", "categoryID", categoryID, "entryID", prevEntry.ID)
}
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,
@ -203,7 +169,6 @@ func (c *Controller) ShowCategoryEntry(ctx *core.Context, request *core.Request,
// ShowUnreadEntry shows a single feed entry in "unread" mode.
func (c *Controller) ShowUnreadEntry(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.LoggedUser()
sortingDirection := model.DefaultSortingDirection
entryID, err := request.IntegerParam("entryID")
if err != nil {
@ -226,6 +191,15 @@ 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)
@ -233,26 +207,9 @@ func (c *Controller) ShowUnreadEntry(ctx *core.Context, request *core.Request, r
}
builder = c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithStatus(model.EntryStatusUnread)
builder.WithCondition("e.id", "!=", entryID)
builder.WithCondition("e.published_at", "<=", entry.Date)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(sortingDirection)
nextEntry, err := builder.GetEntry()
if err != nil {
response.HTML().ServerError(err)
return
}
builder = c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithStatus(model.EntryStatusUnread)
builder.WithCondition("e.id", "!=", entryID)
builder.WithCondition("e.published_at", ">=", entry.Date)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(model.GetOppositeDirection(sortingDirection))
prevEntry, err := builder.GetEntry()
prevEntry, nextEntry, err := c.getEntryPrevNext(user, builder, entry.ID)
if err != nil {
response.HTML().ServerError(err)
return
@ -268,15 +225,6 @@ func (c *Controller) ShowUnreadEntry(ctx *core.Context, request *core.Request, r
prevEntryRoute = ctx.Route("unreadEntry", "entryID", prevEntry.ID)
}
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,
@ -290,7 +238,6 @@ func (c *Controller) ShowUnreadEntry(ctx *core.Context, request *core.Request, r
// ShowReadEntry shows a single feed entry in "history" mode.
func (c *Controller) ShowReadEntry(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.LoggedUser()
sortingDirection := model.DefaultSortingDirection
entryID, err := request.IntegerParam("entryID")
if err != nil {
@ -320,26 +267,9 @@ func (c *Controller) ShowReadEntry(ctx *core.Context, request *core.Request, res
}
builder = c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithStatus(model.EntryStatusRead)
builder.WithCondition("e.id", "!=", entryID)
builder.WithCondition("e.published_at", "<=", entry.Date)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(sortingDirection)
nextEntry, err := builder.GetEntry()
if err != nil {
response.HTML().ServerError(err)
return
}
builder = c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithStatus(model.EntryStatusRead)
builder.WithCondition("e.id", "!=", entryID)
builder.WithCondition("e.published_at", ">=", entry.Date)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(model.GetOppositeDirection(sortingDirection))
prevEntry, err := builder.GetEntry()
prevEntry, nextEntry, err := c.getEntryPrevNext(user, builder, entry.ID)
if err != nil {
response.HTML().ServerError(err)
return
@ -390,3 +320,29 @@ func (c *Controller) UpdateEntriesStatus(ctx *core.Context, request *core.Reques
response.JSON().Standard("OK")
}
func (c *Controller) getEntryPrevNext(user *model.User, builder *storage.EntryQueryBuilder, entryID int64) (prev *model.Entry, next *model.Entry, err error) {
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(user.EntryDirection)
entries, err := builder.GetEntries()
if err != nil {
return nil, nil, err
}
n := len(entries)
for i := 0; i < n; i++ {
if entries[i].ID == entryID {
if i-1 > 0 {
prev = entries[i-1]
}
if i+1 < n {
next = entries[i+1]
}
}
}
return prev, next, nil
}

View file

@ -72,7 +72,7 @@ func (c *Controller) ShowFeedEntries(ctx *core.Context, request *core.Request, r
builder.WithFeedID(feed.ID)
builder.WithoutStatus(model.EntryStatusRemoved)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(model.DefaultSortingDirection)
builder.WithDirection(user.EntryDirection)
builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage)

View file

@ -23,7 +23,7 @@ func (c *Controller) ShowHistoryPage(ctx *core.Context, request *core.Request, r
builder := c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithStatus(model.EntryStatusRead)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(model.DefaultSortingDirection)
builder.WithDirection(user.EntryDirection)
builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage)

View file

@ -74,10 +74,11 @@ func (c *Controller) getSettingsFormTemplateArgs(ctx *core.Context, user *model.
if settingsForm == nil {
args["form"] = form.SettingsForm{
Username: user.Username,
Theme: user.Theme,
Language: user.Language,
Timezone: user.Timezone,
Username: user.Username,
Theme: user.Theme,
Language: user.Language,
Timezone: user.Timezone,
EntryDirection: user.EntryDirection,
}
} else {
args["form"] = settingsForm

View file

@ -17,7 +17,7 @@ func (c *Controller) ShowUnreadPage(ctx *core.Context, request *core.Request, re
builder := c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithStatus(model.EntryStatusUnread)
builder.WithOrder(model.DefaultSortingOrder)
builder.WithDirection(model.DefaultSortingDirection)
builder.WithDirection(user.EntryDirection)
builder.WithOffset(offset)
builder.WithLimit(nbItemsPerPage)

View file

@ -13,12 +13,13 @@ import (
// SettingsForm represents the settings form.
type SettingsForm struct {
Username string
Password string
Confirmation string
Theme string
Language string
Timezone string
Username string
Password string
Confirmation string
Theme string
Language string
Timezone string
EntryDirection string
}
// Merge updates the fields of the given user.
@ -27,6 +28,7 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
user.Theme = s.Theme
user.Language = s.Language
user.Timezone = s.Timezone
user.EntryDirection = s.EntryDirection
if s.Password != "" {
user.Password = s.Password
@ -37,7 +39,7 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
// Validate makes sure the form values are valid.
func (s *SettingsForm) Validate() error {
if s.Username == "" || s.Theme == "" || s.Language == "" || s.Timezone == "" {
if s.Username == "" || s.Theme == "" || s.Language == "" || s.Timezone == "" || s.EntryDirection == "" {
return errors.NewLocalizedError("The username, theme, language and timezone fields are mandatory.")
}
@ -57,11 +59,12 @@ func (s *SettingsForm) Validate() error {
// NewSettingsForm returns a new SettingsForm.
func NewSettingsForm(r *http.Request) *SettingsForm {
return &SettingsForm{
Username: r.FormValue("username"),
Password: r.FormValue("password"),
Confirmation: r.FormValue("confirmation"),
Theme: r.FormValue("theme"),
Language: r.FormValue("language"),
Timezone: r.FormValue("timezone"),
Username: r.FormValue("username"),
Password: r.FormValue("password"),
Confirmation: r.FormValue("confirmation"),
Theme: r.FormValue("theme"),
Language: r.FormValue("language"),
Timezone: r.FormValue("timezone"),
EntryDirection: r.FormValue("entry_direction"),
}
}