Add search form
This commit is contained in:
parent
af15412954
commit
6d0dc451e4
20 changed files with 383 additions and 49 deletions
|
@ -95,6 +95,8 @@ func routes(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handle
|
|||
uiRouter.HandleFunc("/unread", uiController.ShowUnreadPage).Name("unread").Methods("GET")
|
||||
uiRouter.HandleFunc("/history", uiController.ShowHistoryPage).Name("history").Methods("GET")
|
||||
uiRouter.HandleFunc("/starred", uiController.ShowStarredPage).Name("starred").Methods("GET")
|
||||
uiRouter.HandleFunc("/search", uiController.ShowSearchEntries).Name("searchEntries").Methods("GET")
|
||||
uiRouter.HandleFunc("/search/entry/{entryID}", uiController.ShowSearchEntry).Name("searchEntry").Methods("GET")
|
||||
uiRouter.HandleFunc("/feed/{feedID}/refresh", uiController.RefreshFeed).Name("refreshFeed").Methods("GET")
|
||||
uiRouter.HandleFunc("/feed/{feedID}/edit", uiController.EditFeed).Name("editFeed").Methods("GET")
|
||||
uiRouter.HandleFunc("/feed/{feedID}/remove", uiController.RemoveFeed).Name("removeFeed").Methods("POST")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2018-07-04 14:42:27.494264089 -0700 PDT m=+0.053526807
|
||||
// 2018-07-04 22:00:22.177727933 -0700 PDT m=+0.039621734
|
||||
|
||||
package locale
|
||||
|
||||
|
@ -491,7 +491,12 @@ var translations = map[string]string{
|
|||
"Feed Password": "Mot de passe du flux",
|
||||
"You are not authorized to access this resource (invalid username/password)": "Vous n'êtes pas autorisé à accéder à cette ressource (nom d'utilisateur / mot de passe incorrect)",
|
||||
"Unable to fetch this resource (Status Code = %d)": "Impossible de récupérer cette ressource (code=%d)",
|
||||
"Resource not found (404), this feed doesn't exists anymore, check the feed URL": "Page introuvable (404), cet abonnement n'existe plus, vérifiez l'adresse du flux"
|
||||
"Resource not found (404), this feed doesn't exists anymore, check the feed URL": "Page introuvable (404), cet abonnement n'existe plus, vérifiez l'adresse du flux",
|
||||
"Search Results": "Résultats de la recherche",
|
||||
"There is no result for this search.": "Il n'y a aucun résultat pour cette recherche.",
|
||||
"Search...": "Recherche...",
|
||||
"Set focus on search form": "Mettre le focus sur le champ de recherche",
|
||||
"Search": "Recherche"
|
||||
}
|
||||
`,
|
||||
"nl_NL": `{
|
||||
|
@ -1166,7 +1171,7 @@ var translations = map[string]string{
|
|||
var translationsChecksums = map[string]string{
|
||||
"de_DE": "eddbb2c3224169a6533eed6f917af95b8d9bee58c3b3d61951260face7edd768",
|
||||
"en_US": "6fe95384260941e8a5a3c695a655a932e0a8a6a572c1e45cb2b1ae8baa01b897",
|
||||
"fr_FR": "7a451a1d09e051a847f554937e07a6af24b92f1da7f46da8c0ef2d4cc31bcec6",
|
||||
"fr_FR": "343a148eed375a593023c30597ef7280d18222756c5062e6a85e1006c7b12d14",
|
||||
"nl_NL": "05cca4936bd3b0fa44057c4dab64acdef3aed32fbb682393f254cfe2f686ef1f",
|
||||
"pl_PL": "2295f35a98c8f60cfc6bab241d26b224c06979cc9ca3740bb89c63c7596a0431",
|
||||
"zh_CN": "f5fb0a9b7336c51e74d727a2fb294bab3514e3002376da7fd904e0d7caed1a1c",
|
||||
|
|
|
@ -235,5 +235,10 @@
|
|||
"Feed Password": "Mot de passe du flux",
|
||||
"You are not authorized to access this resource (invalid username/password)": "Vous n'êtes pas autorisé à accéder à cette ressource (nom d'utilisateur / mot de passe incorrect)",
|
||||
"Unable to fetch this resource (Status Code = %d)": "Impossible de récupérer cette ressource (code=%d)",
|
||||
"Resource not found (404), this feed doesn't exists anymore, check the feed URL": "Page introuvable (404), cet abonnement n'existe plus, vérifiez l'adresse du flux"
|
||||
"Resource not found (404), this feed doesn't exists anymore, check the feed URL": "Page introuvable (404), cet abonnement n'existe plus, vérifiez l'adresse du flux",
|
||||
"Search Results": "Résultats de la recherche",
|
||||
"There is no result for this search.": "Il n'y a aucun résultat pour cette recherche.",
|
||||
"Search...": "Recherche...",
|
||||
"Set focus on search form": "Mettre le focus sur le champ de recherche",
|
||||
"Search": "Recherche"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2018-07-04 14:42:27.443758746 -0700 PDT m=+0.003021464
|
||||
// 2018-07-04 22:00:22.141197828 -0700 PDT m=+0.003091629
|
||||
|
||||
package sql
|
||||
|
||||
|
|
|
@ -23,6 +23,14 @@ type EntryPaginationBuilder struct {
|
|||
direction string
|
||||
}
|
||||
|
||||
// WithSearchQuery adds full-text search query to the condition.
|
||||
func (e *EntryPaginationBuilder) WithSearchQuery(query string) {
|
||||
if query != "" {
|
||||
e.conditions = append(e.conditions, fmt.Sprintf("e.document_vectors @@ plainto_tsquery($%d)", len(e.args)+1))
|
||||
e.args = append(e.args, query)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStarred adds starred to the condition.
|
||||
func (e *EntryPaginationBuilder) WithStarred() {
|
||||
e.conditions = append(e.conditions, "e.starred is true")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2018-06-21 15:46:05.07724268 +0200 CEST m=+0.035251547
|
||||
// 2018-07-04 22:00:22.176755806 -0700 PDT m=+0.038649607
|
||||
|
||||
package template
|
||||
|
||||
|
@ -8,7 +8,7 @@ var templateCommonMap = map[string]string{
|
|||
<div class="pagination">
|
||||
<div class="pagination-prev">
|
||||
{{ if .prevEntry }}
|
||||
<a href="{{ .prevEntryRoute }}" title="{{ .prevEntry.Title }}" data-page="previous">{{ t "Previous" }}</a>
|
||||
<a href="{{ .prevEntryRoute }}{{ if .searchQuery }}?q={{ .searchQuery }}{{ end }}" title="{{ .prevEntry.Title }}" data-page="previous">{{ t "Previous" }}</a>
|
||||
{{ else }}
|
||||
{{ t "Previous" }}
|
||||
{{ end }}
|
||||
|
@ -16,7 +16,7 @@ var templateCommonMap = map[string]string{
|
|||
|
||||
<div class="pagination-next">
|
||||
{{ if .nextEntry }}
|
||||
<a href="{{ .nextEntryRoute }}" title="{{ .nextEntry.Title }}" data-page="next">{{ t "Next" }}</a>
|
||||
<a href="{{ .nextEntryRoute }}{{ if .searchQuery }}?q={{ .searchQuery }}{{ end }}" title="{{ .nextEntry.Title }}" data-page="next">{{ t "Next" }}</a>
|
||||
{{ else }}
|
||||
{{ t "Next" }}
|
||||
{{ end }}
|
||||
|
@ -140,6 +140,14 @@ var templateCommonMap = map[string]string{
|
|||
<a href="{{ route "logout" }}" title="{{ t "Logged as %s" .user.Username }}">{{ t "Logout" }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="search">
|
||||
<div class="search-toggle-switch {{ if $.searchQuery }}has-search-query{{ end }}">
|
||||
<a href="#" data-action="search">« {{ t "Search" }}</a>
|
||||
</div>
|
||||
<form action="{{ route "searchEntries" }}" class="search-form {{ if $.searchQuery }}has-search-query{{ end }}">
|
||||
<input type="search" name="q" id="search-input" placeholder="{{ t "Search..." }}" {{ if $.searchQuery }}value="{{ .searchQuery }}"{{ end }} required>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
{{ end }}
|
||||
|
@ -190,6 +198,7 @@ var templateCommonMap = map[string]string{
|
|||
<li>{{ t "Download original content" }} = <strong>d</strong></li>
|
||||
<li>{{ t "Toggle bookmark" }} = <strong>f</strong></li>
|
||||
<li>{{ t "Save article" }} = <strong>s</strong></li>
|
||||
<li>{{ t "Set focus on search form" }} = <strong>/</strong></li>
|
||||
<li>{{ t "Close modal dialog" }} = <strong>Esc</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -203,7 +212,7 @@ var templateCommonMap = map[string]string{
|
|||
<div class="pagination">
|
||||
<div class="pagination-prev">
|
||||
{{ if .ShowPrev }}
|
||||
<a href="{{ .Route }}{{ if gt .PrevOffset 0 }}?offset={{ .PrevOffset }}{{ end }}" data-page="previous">{{ t "Previous" }}</a>
|
||||
<a href="{{ .Route }}{{ if gt .PrevOffset 0 }}?offset={{ .PrevOffset }}{{ if .SearchQuery }}&q={{ .SearchQuery }}{{ end }}{{ else }}{{ if .SearchQuery }}?q={{ .SearchQuery }}{{ end }}{{ end }}" data-page="previous">{{ t "Previous" }}</a>
|
||||
{{ else }}
|
||||
{{ t "Previous" }}
|
||||
{{ end }}
|
||||
|
@ -211,7 +220,7 @@ var templateCommonMap = map[string]string{
|
|||
|
||||
<div class="pagination-next">
|
||||
{{ if .ShowNext }}
|
||||
<a href="{{ .Route }}?offset={{ .NextOffset }}" data-page="next">{{ t "Next" }}</a>
|
||||
<a href="{{ .Route }}?offset={{ .NextOffset }}{{ if .SearchQuery }}&q={{ .SearchQuery }}{{ end }}" data-page="next">{{ t "Next" }}</a>
|
||||
{{ else }}
|
||||
{{ t "Next" }}
|
||||
{{ end }}
|
||||
|
@ -222,8 +231,8 @@ var templateCommonMap = map[string]string{
|
|||
}
|
||||
|
||||
var templateCommonMapChecksums = map[string]string{
|
||||
"entry_pagination": "f1465fa70f585ae8043b200ec9de5bf437ffbb0c19fb7aefc015c3555614ee27",
|
||||
"entry_pagination": "756ef122f3ebc73754b5fc4304bf05e59da0ab4af030b2509ff4c9b4a74096ce",
|
||||
"item_meta": "6cff8ae243f19dac936e523867d2975f70aa749b2a461ae63f6ebbca94cf7419",
|
||||
"layout": "2cc3abf4d832b8368689d17091856ccae696f8a51b8fc29641107846f5d6661a",
|
||||
"pagination": "6ff462c2b2a53bc5448b651da017f40a39f1d4f16cef4b2f09784f0797286924",
|
||||
"layout": "4738561d29c428157e83aa13601c463b5e73bd0e2a5fdee75089f3643d6d4055",
|
||||
"pagination": "b592d58ea9d6abf2dc0b158621404cbfaeea5413b1c8b8b9818725963096b196",
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="pagination">
|
||||
<div class="pagination-prev">
|
||||
{{ if .prevEntry }}
|
||||
<a href="{{ .prevEntryRoute }}" title="{{ .prevEntry.Title }}" data-page="previous">{{ t "Previous" }}</a>
|
||||
<a href="{{ .prevEntryRoute }}{{ if .searchQuery }}?q={{ .searchQuery }}{{ end }}" title="{{ .prevEntry.Title }}" data-page="previous">{{ t "Previous" }}</a>
|
||||
{{ else }}
|
||||
{{ t "Previous" }}
|
||||
{{ end }}
|
||||
|
@ -10,7 +10,7 @@
|
|||
|
||||
<div class="pagination-next">
|
||||
{{ if .nextEntry }}
|
||||
<a href="{{ .nextEntryRoute }}" title="{{ .nextEntry.Title }}" data-page="next">{{ t "Next" }}</a>
|
||||
<a href="{{ .nextEntryRoute }}{{ if .searchQuery }}?q={{ .searchQuery }}{{ end }}" title="{{ .nextEntry.Title }}" data-page="next">{{ t "Next" }}</a>
|
||||
{{ else }}
|
||||
{{ t "Next" }}
|
||||
{{ end }}
|
||||
|
|
|
@ -65,6 +65,14 @@
|
|||
<a href="{{ route "logout" }}" title="{{ t "Logged as %s" .user.Username }}">{{ t "Logout" }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="search">
|
||||
<div class="search-toggle-switch {{ if $.searchQuery }}has-search-query{{ end }}">
|
||||
<a href="#" data-action="search">« {{ t "Search" }}</a>
|
||||
</div>
|
||||
<form action="{{ route "searchEntries" }}" class="search-form {{ if $.searchQuery }}has-search-query{{ end }}">
|
||||
<input type="search" name="q" id="search-input" placeholder="{{ t "Search..." }}" {{ if $.searchQuery }}value="{{ .searchQuery }}"{{ end }} required>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
{{ end }}
|
||||
|
@ -115,6 +123,7 @@
|
|||
<li>{{ t "Download original content" }} = <strong>d</strong></li>
|
||||
<li>{{ t "Toggle bookmark" }} = <strong>f</strong></li>
|
||||
<li>{{ t "Save article" }} = <strong>s</strong></li>
|
||||
<li>{{ t "Set focus on search form" }} = <strong>/</strong></li>
|
||||
<li>{{ t "Close modal dialog" }} = <strong>Esc</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="pagination">
|
||||
<div class="pagination-prev">
|
||||
{{ if .ShowPrev }}
|
||||
<a href="{{ .Route }}{{ if gt .PrevOffset 0 }}?offset={{ .PrevOffset }}{{ end }}" data-page="previous">{{ t "Previous" }}</a>
|
||||
<a href="{{ .Route }}{{ if gt .PrevOffset 0 }}?offset={{ .PrevOffset }}{{ if .SearchQuery }}&q={{ .SearchQuery }}{{ end }}{{ else }}{{ if .SearchQuery }}?q={{ .SearchQuery }}{{ end }}{{ end }}" data-page="previous">{{ t "Previous" }}</a>
|
||||
{{ else }}
|
||||
{{ t "Previous" }}
|
||||
{{ end }}
|
||||
|
@ -10,7 +10,7 @@
|
|||
|
||||
<div class="pagination-next">
|
||||
{{ if .ShowNext }}
|
||||
<a href="{{ .Route }}?offset={{ .NextOffset }}" data-page="next">{{ t "Next" }}</a>
|
||||
<a href="{{ .Route }}?offset={{ .NextOffset }}{{ if .SearchQuery }}&q={{ .SearchQuery }}{{ end }}" data-page="next">{{ t "Next" }}</a>
|
||||
{{ else }}
|
||||
{{ t "Next" }}
|
||||
{{ end }}
|
||||
|
|
30
template/html/search_entries.html
Normal file
30
template/html/search_entries.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
{{ define "title"}}{{ t "Search Results" }} ({{ .total }}){{ end }}
|
||||
|
||||
{{ define "content"}}
|
||||
<section class="page-header">
|
||||
<h1>{{ t "Search Results" }} ({{ .total }})</h1>
|
||||
</section>
|
||||
|
||||
{{ if not .entries }}
|
||||
<p class="alert alert-info">{{ t "There is no result for this search." }}</p>
|
||||
{{ else }}
|
||||
<div class="items">
|
||||
{{ range .entries }}
|
||||
<article class="item touch-item item-status-{{ .Status }}" data-id="{{ .ID }}">
|
||||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16">
|
||||
{{ end }}
|
||||
<a href="{{ route "searchEntry" "entryID" .ID }}?q={{ $.searchQuery }}">{{ .Title }}</a>
|
||||
</span>
|
||||
<span class="category"><a href="{{ route "categoryEntries" "categoryID" .Feed.Category.ID }}">{{ .Feed.Category.Title }}</a></span>
|
||||
</div>
|
||||
{{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
|
||||
</article>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ template "pagination" .pagination }}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2018-06-30 18:00:36.547092772 -0700 PDT m=+0.023261871
|
||||
// 2018-07-04 22:00:22.166425155 -0700 PDT m=+0.028318956
|
||||
|
||||
package template
|
||||
|
||||
|
@ -1018,6 +1018,37 @@ var templateViewsMap = map[string]string{
|
|||
</div>
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ end }}
|
||||
`,
|
||||
"search_entries": `{{ define "title"}}{{ t "Search Results" }} ({{ .total }}){{ end }}
|
||||
|
||||
{{ define "content"}}
|
||||
<section class="page-header">
|
||||
<h1>{{ t "Search Results" }} ({{ .total }})</h1>
|
||||
</section>
|
||||
|
||||
{{ if not .entries }}
|
||||
<p class="alert alert-info">{{ t "There is no result for this search." }}</p>
|
||||
{{ else }}
|
||||
<div class="items">
|
||||
{{ range .entries }}
|
||||
<article class="item touch-item item-status-{{ .Status }}" data-id="{{ .ID }}">
|
||||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16">
|
||||
{{ end }}
|
||||
<a href="{{ route "searchEntry" "entryID" .ID }}?q={{ $.searchQuery }}">{{ .Title }}</a>
|
||||
</span>
|
||||
<span class="category"><a href="{{ route "categoryEntries" "categoryID" .Feed.Category.ID }}">{{ .Feed.Category.Title }}</a></span>
|
||||
</div>
|
||||
{{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
|
||||
</article>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ template "pagination" .pagination }}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
`,
|
||||
"sessions": `{{ define "title"}}{{ t "Sessions" }}{{ end }}
|
||||
|
@ -1285,6 +1316,7 @@ var templateViewsMapChecksums = map[string]string{
|
|||
"import": "73b5112e20bfd232bf73334544186ea419505936bc237d481517a8622901878f",
|
||||
"integrations": "20c1c82070b93235d189b10acccd0cda5694cc5684d0b3be23de2ba5ae83e73f",
|
||||
"login": "7d83c3067c02f1f6aafdd8816c7f97a4eb5a5a4bdaaaa4cc1e2fbb9c17ea65e8",
|
||||
"search_entries": "2ed1fa914f322ee077bf4a63d29bb2c5bb415bc3245a0d47019ff8077a5d40fc",
|
||||
"sessions": "3fa79031dd883847eba92fbafe5f535fa3a4e1614bb610f20588b6f8fc8b3624",
|
||||
"settings": "d435dc37e82896ce9a7a573b3c2aeda1db71eec62349e2472ebbf1d5c3e0bc21",
|
||||
"unread_entries": "ca3ef1547d7d170b005a2f48fabd4c0a15550884db5e481659c13ffe6a47d19d",
|
||||
|
|
95
ui/entry_search.go
Normal file
95
ui/entry_search.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ui
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/miniflux/miniflux/http/context"
|
||||
"github.com/miniflux/miniflux/http/request"
|
||||
"github.com/miniflux/miniflux/http/response/html"
|
||||
"github.com/miniflux/miniflux/http/route"
|
||||
"github.com/miniflux/miniflux/logger"
|
||||
"github.com/miniflux/miniflux/model"
|
||||
"github.com/miniflux/miniflux/storage"
|
||||
"github.com/miniflux/miniflux/ui/session"
|
||||
"github.com/miniflux/miniflux/ui/view"
|
||||
)
|
||||
|
||||
// ShowSearchEntry shows a single entry in "search" mode.
|
||||
func (c *Controller) ShowSearchEntry(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := context.New(r)
|
||||
|
||||
user, err := c.store.UserByID(ctx.UserID())
|
||||
if err != nil {
|
||||
html.ServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
entryID, err := request.IntParam(r, "entryID")
|
||||
if err != nil {
|
||||
html.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
searchQuery := request.QueryParam(r, "q", "")
|
||||
builder := c.store.NewEntryQueryBuilder(user.ID)
|
||||
builder.WithSearchQuery(searchQuery)
|
||||
builder.WithEntryID(entryID)
|
||||
builder.WithoutStatus(model.EntryStatusRemoved)
|
||||
|
||||
entry, err := builder.GetEntry()
|
||||
if err != nil {
|
||||
html.ServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if entry == nil {
|
||||
html.NotFound(w)
|
||||
return
|
||||
}
|
||||
|
||||
if entry.Status == model.EntryStatusUnread {
|
||||
err = c.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
|
||||
if err != nil {
|
||||
logger.Error("[Controller:ShowSearchEntry] %v", err)
|
||||
html.ServerError(w, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection)
|
||||
entryPaginationBuilder.WithSearchQuery(searchQuery)
|
||||
prevEntry, nextEntry, err := entryPaginationBuilder.Entries()
|
||||
if err != nil {
|
||||
html.ServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
nextEntryRoute := ""
|
||||
if nextEntry != nil {
|
||||
nextEntryRoute = route.Path(c.router, "searchEntry", "entryID", nextEntry.ID)
|
||||
}
|
||||
|
||||
prevEntryRoute := ""
|
||||
if prevEntry != nil {
|
||||
prevEntryRoute = route.Path(c.router, "searchEntry", "entryID", prevEntry.ID)
|
||||
}
|
||||
|
||||
sess := session.New(c.store, ctx)
|
||||
view := view.New(c.tpl, ctx, sess)
|
||||
view.Set("searchQuery", searchQuery)
|
||||
view.Set("entry", entry)
|
||||
view.Set("prevEntry", prevEntry)
|
||||
view.Set("nextEntry", nextEntry)
|
||||
view.Set("nextEntryRoute", nextEntryRoute)
|
||||
view.Set("prevEntryRoute", prevEntryRoute)
|
||||
view.Set("menu", "search")
|
||||
view.Set("user", user)
|
||||
view.Set("countUnread", c.store.CountUnreadEntries(user.ID))
|
||||
view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID))
|
||||
|
||||
html.OK(w, view.Render("entry"))
|
||||
}
|
|
@ -17,6 +17,7 @@ type pagination struct {
|
|||
ShowPrev bool
|
||||
NextOffset int
|
||||
PrevOffset int
|
||||
SearchQuery string
|
||||
}
|
||||
|
||||
func (c *Controller) getPagination(route string, total, offset int) pagination {
|
||||
|
|
66
ui/search_entries.go
Normal file
66
ui/search_entries.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2018 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ui
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/miniflux/miniflux/http/context"
|
||||
"github.com/miniflux/miniflux/http/request"
|
||||
"github.com/miniflux/miniflux/http/response/html"
|
||||
"github.com/miniflux/miniflux/http/route"
|
||||
"github.com/miniflux/miniflux/model"
|
||||
"github.com/miniflux/miniflux/ui/session"
|
||||
"github.com/miniflux/miniflux/ui/view"
|
||||
)
|
||||
|
||||
// ShowSearchEntries shows all entries for the given feed.
|
||||
func (c *Controller) ShowSearchEntries(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := context.New(r)
|
||||
|
||||
user, err := c.store.UserByID(ctx.UserID())
|
||||
if err != nil {
|
||||
html.ServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
searchQuery := request.QueryParam(r, "q", "")
|
||||
offset := request.QueryIntParam(r, "offset", 0)
|
||||
builder := c.store.NewEntryQueryBuilder(user.ID)
|
||||
builder.WithSearchQuery(searchQuery)
|
||||
builder.WithoutStatus(model.EntryStatusRemoved)
|
||||
builder.WithOrder(model.DefaultSortingOrder)
|
||||
builder.WithDirection(user.EntryDirection)
|
||||
builder.WithOffset(offset)
|
||||
builder.WithLimit(nbItemsPerPage)
|
||||
|
||||
entries, err := builder.GetEntries()
|
||||
if err != nil {
|
||||
html.ServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
count, err := builder.CountEntries()
|
||||
if err != nil {
|
||||
html.ServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
sess := session.New(c.store, ctx)
|
||||
view := view.New(c.tpl, ctx, sess)
|
||||
pagination := c.getPagination(route.Path(c.router, "searchEntries"), count, offset)
|
||||
pagination.SearchQuery = searchQuery
|
||||
|
||||
view.Set("searchQuery", searchQuery)
|
||||
view.Set("entries", entries)
|
||||
view.Set("total", count)
|
||||
view.Set("pagination", pagination)
|
||||
view.Set("menu", "search")
|
||||
view.Set("user", user)
|
||||
view.Set("countUnread", c.store.CountUnreadEntries(user.ID))
|
||||
view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID))
|
||||
|
||||
html.OK(w, view.Render("search_entries"))
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2018-06-19 22:56:40.300982018 -0700 PDT m=+0.022794138
|
||||
// 2018-07-04 22:00:22.158122512 -0700 PDT m=+0.020016313
|
||||
|
||||
package static
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -17,6 +17,7 @@ a:hover {
|
|||
color: #ddd;
|
||||
}
|
||||
|
||||
/* Header and main menu */
|
||||
.header li {
|
||||
border-color: #333;
|
||||
}
|
||||
|
@ -36,10 +37,12 @@ a:hover {
|
|||
color: rgba(82, 168, 236, 0.85);
|
||||
}
|
||||
|
||||
/* Page header */
|
||||
.page-header h1 {
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
.logo a:hover span {
|
||||
color: #555;
|
||||
}
|
||||
|
@ -61,6 +64,7 @@ tr:hover {
|
|||
}
|
||||
|
||||
/* Forms */
|
||||
input[type="search"],
|
||||
input[type="url"],
|
||||
input[type="password"],
|
||||
input[type="text"] {
|
||||
|
@ -69,6 +73,7 @@ input[type="text"] {
|
|||
color: #ccc;
|
||||
}
|
||||
|
||||
input[type="search"]:focus,
|
||||
input[type="url"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="text"]:focus {
|
||||
|
|
|
@ -37,9 +37,9 @@ a:hover {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Header and main menu */
|
||||
.header {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header nav ul {
|
||||
|
@ -74,6 +74,7 @@ a:hover {
|
|||
color: #888;
|
||||
}
|
||||
|
||||
/* Page header */
|
||||
.page-header {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
@ -92,6 +93,7 @@ a:hover {
|
|||
line-height: 1.8em;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
.logo {
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
|
@ -114,6 +116,16 @@ a:hover {
|
|||
color: #000;
|
||||
}
|
||||
|
||||
/* Search form */
|
||||
.search {
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-toggle-switch {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
body {
|
||||
margin: auto;
|
||||
|
@ -124,6 +136,7 @@ a:hover {
|
|||
text-align: left;
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.header nav ul {
|
||||
|
@ -147,6 +160,30 @@ a:hover {
|
|||
display: inline;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
/* Search form */
|
||||
.search {
|
||||
text-align: right;
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.search-toggle-switch {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-toggle-switch.has-search-query {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-form.has-search-query {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
|
@ -217,6 +254,7 @@ select {
|
|||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
input[type="search"],
|
||||
input[type="url"],
|
||||
input[type="password"],
|
||||
input[type="text"] {
|
||||
|
@ -230,6 +268,7 @@ input[type="text"] {
|
|||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type="search"]:focus,
|
||||
input[type="url"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="text"]:focus {
|
||||
|
@ -377,7 +416,7 @@ a.button {
|
|||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 350px;
|
||||
width: 360px;
|
||||
overflow: auto;
|
||||
background: #f0f0f0;
|
||||
box-shadow: 2px 0 5px 0 #ccc;
|
||||
|
@ -387,6 +426,7 @@ a.button {
|
|||
|
||||
#modal-left h3 {
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-close-modal {
|
||||
|
@ -577,7 +617,7 @@ article.feed-parsing-error {
|
|||
.entry header h1 {
|
||||
font-size: 2.0em;
|
||||
line-height: 1.25em;
|
||||
margin: 30px 0;
|
||||
margin: 5px 0 30px 0;
|
||||
}
|
||||
|
||||
.entry header h1 a {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2018-06-29 20:26:34.577543639 -0700 PDT m=+0.034529605
|
||||
// 2018-07-04 22:00:22.164970025 -0700 PDT m=+0.026863826
|
||||
|
||||
package static
|
||||
|
||||
|
@ -29,8 +29,8 @@ listen(){let elements=document.querySelectorAll(".touch-item");elements.forEach(
|
|||
class KeyboardHandler{constructor(){this.queue=[];this.shortcuts={};}
|
||||
on(combination,callback){this.shortcuts[combination]=callback;}
|
||||
listen(){document.onkeydown=(event)=>{if(this.isEventIgnored(event)){return;}
|
||||
let key=this.getKey(event);this.queue.push(key);for(let combination in this.shortcuts){let keys=combination.split(" ");if(keys.every((value,index)=>value===this.queue[index])){this.queue=[];this.shortcuts[combination]();return;}
|
||||
if(keys.length===1&&key===keys[0]){this.queue=[];this.shortcuts[combination]();return;}}
|
||||
let key=this.getKey(event);this.queue.push(key);for(let combination in this.shortcuts){let keys=combination.split(" ");if(keys.every((value,index)=>value===this.queue[index])){this.queue=[];this.shortcuts[combination](event);return;}
|
||||
if(keys.length===1&&key===keys[0]){this.queue=[];this.shortcuts[combination](event);return;}}
|
||||
if(this.queue.length>=2){this.queue=[];}};}
|
||||
isEventIgnored(event){return event.target.tagName==="INPUT"||event.target.tagName==="TEXTAREA";}
|
||||
getKey(event){const mapping={'Esc':'Escape','Up':'ArrowUp','Down':'ArrowDown','Left':'ArrowLeft','Right':'ArrowRight'};for(let key in mapping){if(mapping.hasOwnProperty(key)&&key===event.key){return mapping[key];}}
|
||||
|
@ -59,12 +59,16 @@ element.innerHTML=element.dataset.labelLoading;let request=new RequestBuilder(el
|
|||
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";}}}
|
||||
toggleMainMenu(){let menu=document.querySelector(".header nav ul");if(DomHelper.isVisible(menu)){menu.style.display="none";}else{menu.style.display="block";}
|
||||
let searchElement=document.querySelector(".header .search");if(DomHelper.isVisible(searchElement)){searchElement.style.display="none";}else{searchElement.style.display="block";}}}
|
||||
class ModalHandler{static exists(){return document.getElementById("modal-container")!==null;}
|
||||
static open(fragment){if(ModalHandler.exists()){return;}
|
||||
let container=document.createElement("div");container.id="modal-container";container.appendChild(document.importNode(fragment,true));document.body.appendChild(container);let closeButton=document.querySelector("a.btn-close-modal");if(closeButton!==null){closeButton.onclick=(event)=>{event.preventDefault();ModalHandler.close();};}}
|
||||
static close(){let container=document.getElementById("modal-container");if(container!==null){container.parentNode.removeChild(container);}}}
|
||||
class NavHandler{showKeyboardShortcuts(){let template=document.getElementById("keyboard-shortcuts");if(template!==null){ModalHandler.open(template.content);}}
|
||||
class NavHandler{setFocusToSearchInput(event){event.preventDefault();event.stopPropagation();let toggleSwitchElement=document.querySelector(".search-toggle-switch");if(toggleSwitchElement){toggleSwitchElement.style.display="none";}
|
||||
let searchFormElement=document.querySelector(".search-form");if(searchFormElement){searchFormElement.style.display="block";}
|
||||
let searchInputElement=document.getElementById("search-input");if(searchInputElement){searchInputElement.focus();searchInputElement.value="";}}
|
||||
showKeyboardShortcuts(){let template=document.getElementById("keyboard-shortcuts");if(template!==null){ModalHandler.open(template.content);}}
|
||||
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);}}}
|
||||
|
@ -87,9 +91,9 @@ 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("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);});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,"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));}});})();`,
|
||||
}
|
||||
|
||||
var JavascriptChecksums = map[string]string{
|
||||
"app": "1cbc164e7e61cb058564c41d63530cfb8936c63b061b0381f7672f8010be70dd",
|
||||
"app": "717f6c6431128b6263dc1f54edf6fd0c6efc3bcbc8c9baf23768c8f23ce53675",
|
||||
}
|
||||
|
|
|
@ -168,13 +168,13 @@ class KeyboardHandler {
|
|||
|
||||
if (keys.every((value, index) => value === this.queue[index])) {
|
||||
this.queue = [];
|
||||
this.shortcuts[combination]();
|
||||
this.shortcuts[combination](event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (keys.length === 1 && key === keys[0]) {
|
||||
this.queue = [];
|
||||
this.shortcuts[combination]();
|
||||
this.shortcuts[combination](event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -299,20 +299,11 @@ class UnreadCounterHandler {
|
|||
let oldValue = parseInt(element.textContent, 10);
|
||||
element.innerHTML = callback(oldValue);
|
||||
});
|
||||
// The titlebar must be updated only on the "Unread" page.
|
||||
|
||||
if (window.location.href.endsWith('/unread')) {
|
||||
// The following 3 lines ensure that the unread count in the titlebar
|
||||
// is updated correctly when users presses "v".
|
||||
let oldValue = parseInt(document.title.split('(')[1], 10);
|
||||
let newValue = callback(oldValue);
|
||||
// Notes:
|
||||
// - This will only be executed in the /unread page. Therefore, it
|
||||
// will not affect titles on other pages.
|
||||
// - When there are no unread items, user cannot press "v".
|
||||
// Therefore, we need not handle the case where title is
|
||||
// "Unread Items - Miniflux". This applies to other cases as well.
|
||||
// i.e.: if there are no unread items, user cannot decrement or
|
||||
// increment anything.
|
||||
|
||||
document.title = document.title.replace(
|
||||
/(.*?)\(\d+\)(.*?)/,
|
||||
function (match, prefix, suffix, offset, string) {
|
||||
|
@ -330,8 +321,7 @@ class EntryHandler {
|
|||
request.withBody({entry_ids: entryIDs, status: status});
|
||||
request.withCallback(callback);
|
||||
request.execute();
|
||||
// The following 5 lines ensure that the unread count in the menu is
|
||||
// updated correctly when users presses "v".
|
||||
|
||||
if (status === "read") {
|
||||
UnreadCounterHandler.decrement(1);
|
||||
} else {
|
||||
|
@ -501,6 +491,13 @@ class MenuHandler {
|
|||
} else {
|
||||
menu.style.display = "block";
|
||||
}
|
||||
|
||||
let searchElement = document.querySelector(".header .search");
|
||||
if (DomHelper.isVisible(searchElement)) {
|
||||
searchElement.style.display = "none";
|
||||
} else {
|
||||
searchElement.style.display = "block";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -537,6 +534,27 @@ class ModalHandler {
|
|||
}
|
||||
|
||||
class NavHandler {
|
||||
setFocusToSearchInput(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let toggleSwitchElement = document.querySelector(".search-toggle-switch");
|
||||
if (toggleSwitchElement) {
|
||||
toggleSwitchElement.style.display = "none";
|
||||
}
|
||||
|
||||
let searchFormElement = document.querySelector(".search-form");
|
||||
if (searchFormElement) {
|
||||
searchFormElement.style.display = "block";
|
||||
}
|
||||
|
||||
let searchInputElement = document.getElementById("search-input");
|
||||
if (searchInputElement) {
|
||||
searchInputElement.focus();
|
||||
searchInputElement.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
showKeyboardShortcuts() {
|
||||
let template = document.getElementById("keyboard-shortcuts");
|
||||
if (template !== null) {
|
||||
|
@ -757,6 +775,7 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||
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();
|
||||
|
||||
|
@ -790,6 +809,10 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||
(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());
|
||||
|
|
Loading…
Add table
Reference in a new issue