1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-05 02:29:54 +00:00

History: Isolate recent changes

This commit is contained in:
Timur Ismagilov 2022-05-18 19:12:00 +03:00
parent fdba598c57
commit 1a98beccb4
6 changed files with 138 additions and 442 deletions

View File

@ -12,15 +12,24 @@ import (
"github.com/gorilla/mux"
"log"
"net/http"
"strconv"
"strings"
"text/template"
)
func InitHandlers(rtr *mux.Router) {
rtr.PathPrefix("/primitive-diff/").HandlerFunc(handlerPrimitiveDiff)
rtr.HandleFunc("/recent-changes/{count:[0-9]+}", handlerRecentChanges)
rtr.HandleFunc("/recent-changes/", func(w http.ResponseWriter, rq *http.Request) {
http.Redirect(w, rq, "/recent-changes/20", http.StatusSeeOther)
})
chainPrimitiveDiff = viewutil.
En(viewutil.CopyEnWith(fs, "view_primitive_diff.html")).
Ru(template.Must(viewutil.CopyRuWith(fs, "view_primitive_diff.html").Parse(ruTranslation)))
chainRecentChanges = viewutil.
En(viewutil.CopyEnWith(fs, "view_recent_changes.html")).
Ru(template.Must(viewutil.CopyRuWith(fs, "view_recent_changes.html").Parse(ruTranslation)))
}
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
@ -52,6 +61,16 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
}
}
// handlerRecentChanges displays the /recent-changes/ page.
func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
// Error ignored: filtered by regex
editCount, _ := strconv.Atoi(mux.Vars(rq)["count"])
if editCount > 100 {
return
}
recentChanges(viewutil.MetaFrom(w, rq), editCount, history.RecentChanges(editCount))
}
var (
//go:embed *.html
fs embed.FS
@ -61,8 +80,34 @@ var (
`
chainPrimitiveDiff viewutil.Chain
chainRecentChanges viewutil.Chain
)
type recentChangesData struct {
viewutil.BaseData
EditCount int
Changes []history.Revision
UserHypha string
Stops []int
}
func recentChanges(meta viewutil.Meta, editCount int, changes []history.Revision) {
if err := chainRecentChanges.Get(meta).ExecuteTemplate(meta.W, "page", recentChangesData{
BaseData: viewutil.BaseData{
Meta: meta,
Addr: "/recent-changes/" + strconv.Itoa(editCount),
HeaderLinks: cfg.HeaderLinks,
CommonScripts: cfg.CommonScripts,
},
EditCount: editCount,
Changes: changes,
UserHypha: cfg.UserHypha,
Stops: []int{20, 50, 100},
}); err != nil {
log.Println(err)
}
}
type primitiveDiffData struct {
viewutil.BaseData
HyphaName string

View File

@ -0,0 +1,70 @@
{{define "subscribe via"}}{{end}}
{{define "recent changes"}}Recent changes{{end}}
{{define "n recent changes"}}{{.}} recent change{{if ne . 1}}s{{end}}{{end}}
{{define "title"}}{{template "n recent changes" .EditCount}}{{end}}
{{define "body"}}
<main class="main-width recent-changes">
<h1>{{template "recent changes"}}</h1>
<p class="recent-changes__count">
{{block "count pre" .}}See{{end}}
{{ $editCount := .EditCount }}
{{range $i, $m := .Stops }}
{{if $i | gt 0}}
<span aria-hidden="true">|</span>
{{end}}
{{if $m | eq $editCount}}
<b>{{$m}}</b>
{{else}}
<a href="/recent-changes/{{$m}}">{{$m}}</a>
{{end}}
{{end}}
{{block "count post" .}}recent changes{{end}}
</p>
<p>
<img class="icon" width="20" height="20" src="/static/icon/feed.svg" aria-hidden="true" alt="RSS icon">
{{block "subscribe via" .}}Subscribe via <a href="/recent-changes-rss">RSS</a>, <a href="/recent-changes-atom">Atom</a> or <a href="/recent-changes-json">JSON feed</a>.{{end}}
</p>
{{$userHypha := .UserHypha}}
{{$year := 0}}{{$month := 0}}{{$day := 0}}
<section class="recent-changes__list" role="feed">
{{range $i, $entry := .Changes}}
{{$time := $entry.Time.UTC}}
{{$y := $time.Year}}{{$m := $time.Month}}{{$d := $time.Day}}
{{if or (ne $d $day) (ne $m $month) (ne $y $year)}}
<h2 class="recent-changes__heading">
{{printf "%04d-%02d-%02d" $y $m $d}}
</h2>
{{$year = $y}}{{$month = $m}}{{$day = $d}}
{{end}}
<div class="recent-changes__entry">
<div>
<time class="recent-changes__entry__time">
{{ $time.Format "15:04 UTC" }}
</time>
<span class="recent-changes__entry__message">{{$entry.Hash}}</span>
{{ if $entry.Username | ne "anon" }}
<span class="recent-changes__entry__author">
&mdash; <a href="/hypha/{{$userHypha}}/{{$entry.Username}}" rel="author">{{$entry.Username}}</a>
</span>
{{end}}
</div>
<div>
<span class="recent-changes__entry__links">
{{$entry.HyphaeLinksHTML}}
</span>
<span class="recent-changes__entry__message">
{{$entry.Message}}
</span>
</div>
</div>
{{else}}
<p>{{block "recent empty" .}}No recent changes found.{{end}}</p>
{{end}}
</section>
</main>
{{end}}

View File

@ -59,16 +59,6 @@
"history_title": "History of %s",
"recent_title": "{{.n}} recent change%s",
"recent_title+one": "",
"recent_title+other": "s",
"recent_heading": "Recent Changes",
"recent_count_pre": "See",
"recent_count_post": "recent changes",
"recent_subscribe": "Subscribe via {{.rss}}, {{.atom}} or {{.json}}",
"recent_subscribe_json": "JSON feed",
"recent_empty": "Could not find any recent changes.",
"diff_title": "Diff of {{.name}} at {{.rev}}",
"revision_title": "{{.name}} at {{.rev}}",

View File

@ -1,92 +1,7 @@
{% import "fmt" %}
{% import "net/http" %}
{% import "time" %}
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% import "github.com/bouncepaw/mycorrhiza/history" %}
{% func RecentChanges(n int, lc *l18n.Localizer) %}
<main class="main-width recent-changes">
<h1>{%s lc.Get("ui.recent_heading") %}</h1>
<nav class="recent-changes__count">
{%s lc.Get("ui.recent_count_pre") %}
{% for i, m := range []int{20, 50, 100} %}
{% if i > 0 %}
<span aria-hidden="true">|</span>
{% endif %}
{% if m == n %}
<b>{%d m %}</b>
{% else %}
<a href="/recent-changes/{%d m %}">{%d m %}</a>
{% endif %}
{% endfor %}
{%s lc.Get("ui.recent_count_post") %}
</nav>
<p><img class="icon" width="20" height="20" src="/static/icon/feed.svg">{%s= lc.Get("ui.recent_subscribe", &l18n.Replacements{"rss": "<a href=\"/recent-changes-rss\">RSS</a>", "atom": "<a href=\"/recent-changes-atom\">Atom</a>", "json": fmt.Sprintf("<a href=\"/recent-changes-json\">%s</a>", lc.Get("ui.recent_subscribe_json"))}) %}</p>
{% comment %}
Here I am, willing to add some accessibility using ARIA. Turns out,
role="feed" is not supported in any screen reader as of September
2020. At least web search says so. Even JAWS doesn't support it!
How come? I'll add the role anyway. -- bouncepaw
{% endcomment %}
{% code
changes := history.RecentChanges(n)
var year, day int
var month time.Month
%}
<section class="recent-changes__list" role="feed">
{% if len(changes) == 0 %}
<p>{%s lc.Get("ui.recent_empty") %}</p>
{% else %}
{% for i, entry := range changes %}
{% code y, m, d := entry.Time.UTC().Date() %}
{% if d != day || m != month || y != year %}
<h2 class="recent-changes__heading">
{%s fmt.Sprintf("%04d-%02d-%02d", y, m, d) %}
</h2>
{% code year, month, day = y, m, d %}
{% endif %}
<div class="recent-changes__entry" role="article"
aria-setsize="{%d n %}" aria-posinset="{%d i %}">
{%s= recentChanges(entry) %}
</div>
{% endfor %}
{% endif %}
</section>
</main>
{% endfunc %}
{% func recentChanges(rev history.Revision) %}
<div>
<time class="recent-changes__entry__time">
{%s rev.Time.UTC().Format("15:04 UTC") %}
</time>
<span class="recent-changes__entry__message">{%s rev.Hash %}</span>
{% if rev.Username != "anon" %}
<span class="recent-changes__entry__author">
&mdash; <a href="/hypha/{%s cfg.UserHypha %}/{%s rev.Username %}" rel="author">{%s rev.Username %}</a>
</span>
{% endif %}
</div>
<div>
<span class="recent-changes__entry__links">
{%s= rev.HyphaeLinksHTML() %}
</span>
<span class="recent-changes__entry__message">
{%s rev.Message %}
</span>
</div>
{% endfunc %}
{% func History(rq *http.Request, hyphaName, list string, lc *l18n.Localizer) %}
<main class="main-width">

View File

@ -10,368 +10,66 @@ import "fmt"
//line views/history.qtpl:2
import "net/http"
//line views/history.qtpl:3
import "time"
//line views/history.qtpl:5
import "github.com/bouncepaw/mycorrhiza/cfg"
//line views/history.qtpl:6
//line views/history.qtpl:4
import "github.com/bouncepaw/mycorrhiza/l18n"
//line views/history.qtpl:7
import "github.com/bouncepaw/mycorrhiza/history"
//line views/history.qtpl:10
//line views/history.qtpl:6
import (
qtio422016 "io"
qt422016 "github.com/valyala/quicktemplate"
)
//line views/history.qtpl:10
//line views/history.qtpl:6
var (
_ = qtio422016.Copy
_ = qt422016.AcquireByteBuffer
)
//line views/history.qtpl:10
func StreamRecentChanges(qw422016 *qt422016.Writer, n int, lc *l18n.Localizer) {
//line views/history.qtpl:10
qw422016.N().S(`
<main class="main-width recent-changes">
<h1>`)
//line views/history.qtpl:12
qw422016.E().S(lc.Get("ui.recent_heading"))
//line views/history.qtpl:12
qw422016.N().S(`</h1>
<nav class="recent-changes__count">
`)
//line views/history.qtpl:15
qw422016.E().S(lc.Get("ui.recent_count_pre"))
//line views/history.qtpl:15
qw422016.N().S(`
`)
//line views/history.qtpl:16
for i, m := range []int{20, 50, 100} {
//line views/history.qtpl:16
qw422016.N().S(`
`)
//line views/history.qtpl:17
if i > 0 {
//line views/history.qtpl:17
qw422016.N().S(`
<span aria-hidden="true">|</span>
`)
//line views/history.qtpl:19
}
//line views/history.qtpl:19
qw422016.N().S(`
`)
//line views/history.qtpl:20
if m == n {
//line views/history.qtpl:20
qw422016.N().S(`
<b>`)
//line views/history.qtpl:21
qw422016.N().D(m)
//line views/history.qtpl:21
qw422016.N().S(`</b>
`)
//line views/history.qtpl:22
} else {
//line views/history.qtpl:22
qw422016.N().S(`
<a href="/recent-changes/`)
//line views/history.qtpl:23
qw422016.N().D(m)
//line views/history.qtpl:23
qw422016.N().S(`">`)
//line views/history.qtpl:23
qw422016.N().D(m)
//line views/history.qtpl:23
qw422016.N().S(`</a>
`)
//line views/history.qtpl:24
}
//line views/history.qtpl:24
qw422016.N().S(`
`)
//line views/history.qtpl:25
}
//line views/history.qtpl:25
qw422016.N().S(`
`)
//line views/history.qtpl:26
qw422016.E().S(lc.Get("ui.recent_count_post"))
//line views/history.qtpl:26
qw422016.N().S(`
</nav>
<p><img class="icon" width="20" height="20" src="/static/icon/feed.svg">`)
//line views/history.qtpl:29
qw422016.N().S(lc.Get("ui.recent_subscribe", &l18n.Replacements{"rss": "<a href=\"/recent-changes-rss\">RSS</a>", "atom": "<a href=\"/recent-changes-atom\">Atom</a>", "json": fmt.Sprintf("<a href=\"/recent-changes-json\">%s</a>", lc.Get("ui.recent_subscribe_json"))}))
//line views/history.qtpl:29
qw422016.N().S(`</p>
`)
//line views/history.qtpl:36
qw422016.N().S(`
`)
//line views/history.qtpl:39
changes := history.RecentChanges(n)
var year, day int
var month time.Month
//line views/history.qtpl:42
qw422016.N().S(`
<section class="recent-changes__list" role="feed">
`)
//line views/history.qtpl:44
if len(changes) == 0 {
//line views/history.qtpl:44
qw422016.N().S(`
<p>`)
//line views/history.qtpl:45
qw422016.E().S(lc.Get("ui.recent_empty"))
//line views/history.qtpl:45
qw422016.N().S(`</p>
`)
//line views/history.qtpl:46
} else {
//line views/history.qtpl:46
qw422016.N().S(`
`)
//line views/history.qtpl:47
for i, entry := range changes {
//line views/history.qtpl:47
qw422016.N().S(`
`)
//line views/history.qtpl:49
y, m, d := entry.Time.UTC().Date()
//line views/history.qtpl:49
qw422016.N().S(`
`)
//line views/history.qtpl:50
if d != day || m != month || y != year {
//line views/history.qtpl:50
qw422016.N().S(`
<h2 class="recent-changes__heading">
`)
//line views/history.qtpl:52
qw422016.E().S(fmt.Sprintf("%04d-%02d-%02d", y, m, d))
//line views/history.qtpl:52
qw422016.N().S(`
</h2>
`)
//line views/history.qtpl:54
year, month, day = y, m, d
//line views/history.qtpl:54
qw422016.N().S(`
`)
//line views/history.qtpl:55
}
//line views/history.qtpl:55
qw422016.N().S(`
<div class="recent-changes__entry" role="article"
aria-setsize="`)
//line views/history.qtpl:58
qw422016.N().D(n)
//line views/history.qtpl:58
qw422016.N().S(`" aria-posinset="`)
//line views/history.qtpl:58
qw422016.N().D(i)
//line views/history.qtpl:58
qw422016.N().S(`">
`)
//line views/history.qtpl:59
qw422016.N().S(recentChanges(entry))
//line views/history.qtpl:59
qw422016.N().S(`
</div>
`)
//line views/history.qtpl:62
}
//line views/history.qtpl:62
qw422016.N().S(`
`)
//line views/history.qtpl:63
}
//line views/history.qtpl:63
qw422016.N().S(`
</section>
</main>
`)
//line views/history.qtpl:66
}
//line views/history.qtpl:66
func WriteRecentChanges(qq422016 qtio422016.Writer, n int, lc *l18n.Localizer) {
//line views/history.qtpl:66
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/history.qtpl:66
StreamRecentChanges(qw422016, n, lc)
//line views/history.qtpl:66
qt422016.ReleaseWriter(qw422016)
//line views/history.qtpl:66
}
//line views/history.qtpl:66
func RecentChanges(n int, lc *l18n.Localizer) string {
//line views/history.qtpl:66
qb422016 := qt422016.AcquireByteBuffer()
//line views/history.qtpl:66
WriteRecentChanges(qb422016, n, lc)
//line views/history.qtpl:66
qs422016 := string(qb422016.B)
//line views/history.qtpl:66
qt422016.ReleaseByteBuffer(qb422016)
//line views/history.qtpl:66
return qs422016
//line views/history.qtpl:66
}
//line views/history.qtpl:68
func streamrecentChanges(qw422016 *qt422016.Writer, rev history.Revision) {
//line views/history.qtpl:68
qw422016.N().S(`
<div>
<time class="recent-changes__entry__time">
`)
//line views/history.qtpl:71
qw422016.E().S(rev.Time.UTC().Format("15:04 UTC"))
//line views/history.qtpl:71
qw422016.N().S(`
</time>
<span class="recent-changes__entry__message">`)
//line views/history.qtpl:73
qw422016.E().S(rev.Hash)
//line views/history.qtpl:73
qw422016.N().S(`</span>
`)
//line views/history.qtpl:75
if rev.Username != "anon" {
//line views/history.qtpl:75
qw422016.N().S(`
<span class="recent-changes__entry__author">
&mdash; <a href="/hypha/`)
//line views/history.qtpl:77
qw422016.E().S(cfg.UserHypha)
//line views/history.qtpl:77
qw422016.N().S(`/`)
//line views/history.qtpl:77
qw422016.E().S(rev.Username)
//line views/history.qtpl:77
qw422016.N().S(`" rel="author">`)
//line views/history.qtpl:77
qw422016.E().S(rev.Username)
//line views/history.qtpl:77
qw422016.N().S(`</a>
</span>
`)
//line views/history.qtpl:79
}
//line views/history.qtpl:79
qw422016.N().S(`
</div>
<div>
<span class="recent-changes__entry__links">
`)
//line views/history.qtpl:83
qw422016.N().S(rev.HyphaeLinksHTML())
//line views/history.qtpl:83
qw422016.N().S(`
</span>
<span class="recent-changes__entry__message">
`)
//line views/history.qtpl:86
qw422016.E().S(rev.Message)
//line views/history.qtpl:86
qw422016.N().S(`
</span>
</div>
`)
//line views/history.qtpl:89
}
//line views/history.qtpl:89
func writerecentChanges(qq422016 qtio422016.Writer, rev history.Revision) {
//line views/history.qtpl:89
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/history.qtpl:89
streamrecentChanges(qw422016, rev)
//line views/history.qtpl:89
qt422016.ReleaseWriter(qw422016)
//line views/history.qtpl:89
}
//line views/history.qtpl:89
func recentChanges(rev history.Revision) string {
//line views/history.qtpl:89
qb422016 := qt422016.AcquireByteBuffer()
//line views/history.qtpl:89
writerecentChanges(qb422016, rev)
//line views/history.qtpl:89
qs422016 := string(qb422016.B)
//line views/history.qtpl:89
qt422016.ReleaseByteBuffer(qb422016)
//line views/history.qtpl:89
return qs422016
//line views/history.qtpl:89
}
//line views/history.qtpl:91
//line views/history.qtpl:6
func StreamHistory(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, list string, lc *l18n.Localizer) {
//line views/history.qtpl:91
//line views/history.qtpl:6
qw422016.N().S(`
<main class="main-width">
<article class="history">
<h1>`)
//line views/history.qtpl:94
//line views/history.qtpl:9
qw422016.N().S(fmt.Sprintf(lc.Get("ui.history_title"), beautifulLink(hyphaName)))
//line views/history.qtpl:94
//line views/history.qtpl:9
qw422016.N().S(`</h1>
`)
//line views/history.qtpl:95
//line views/history.qtpl:10
qw422016.N().S(list)
//line views/history.qtpl:95
//line views/history.qtpl:10
qw422016.N().S(`
</article>
</main>
`)
//line views/history.qtpl:98
//line views/history.qtpl:13
}
//line views/history.qtpl:98
//line views/history.qtpl:13
func WriteHistory(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, list string, lc *l18n.Localizer) {
//line views/history.qtpl:98
//line views/history.qtpl:13
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/history.qtpl:98
//line views/history.qtpl:13
StreamHistory(qw422016, rq, hyphaName, list, lc)
//line views/history.qtpl:98
//line views/history.qtpl:13
qt422016.ReleaseWriter(qw422016)
//line views/history.qtpl:98
//line views/history.qtpl:13
}
//line views/history.qtpl:98
//line views/history.qtpl:13
func History(rq *http.Request, hyphaName, list string, lc *l18n.Localizer) string {
//line views/history.qtpl:98
//line views/history.qtpl:13
qb422016 := qt422016.AcquireByteBuffer()
//line views/history.qtpl:98
//line views/history.qtpl:13
WriteHistory(qb422016, rq, hyphaName, list, lc)
//line views/history.qtpl:98
//line views/history.qtpl:13
qs422016 := string(qb422016.B)
//line views/history.qtpl:98
//line views/history.qtpl:13
qt422016.ReleaseByteBuffer(qb422016)
//line views/history.qtpl:98
//line views/history.qtpl:13
return qs422016
//line views/history.qtpl:98
//line views/history.qtpl:13
}

View File

@ -3,11 +3,9 @@ package web
import (
"fmt"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/gorilla/mux"
"log"
"net/http"
"strconv"
"github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/l18n"
@ -18,11 +16,6 @@ import (
func initHistory(r *mux.Router) {
r.PathPrefix("/history/").HandlerFunc(handlerHistory)
r.HandleFunc("/recent-changes/{count:[0-9]+}", handlerRecentChanges)
r.HandleFunc("/recent-changes/", func(w http.ResponseWriter, rq *http.Request) {
http.Redirect(w, rq, "/recent-changes/20", http.StatusSeeOther)
})
r.HandleFunc("/recent-changes-rss", handlerRecentChangesRSS)
r.HandleFunc("/recent-changes-atom", handlerRecentChangesAtom)
r.HandleFunc("/recent-changes-json", handlerRecentChangesJSON)
@ -48,21 +41,6 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) {
))
}
// handlerRecentChanges displays the /recent-changes/ page.
func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
// Error ignored: filtered by regex
n, _ := strconv.Atoi(mux.Vars(rq)["count"])
if n > 100 {
return
}
var lc = l18n.FromRequest(rq)
util.HTTP200Page(w, views.Base(
viewutil.MetaFrom(w, rq),
lc.GetPlural("ui.recent_title", n),
views.RecentChanges(n, lc),
))
}
// genericHandlerOfFeeds is a helper function for the web feed handlers.
func genericHandlerOfFeeds(w http.ResponseWriter, rq *http.Request, f func(history.FeedOptions) (string, error), name string, contentType string) {
opts, err := history.ParseFeedOptions(rq.URL.Query())