Implement posts index page
This commit is contained in:
parent
5a99778187
commit
1242be7cfe
@ -212,7 +212,9 @@ func (a *api) handler() http.Handler {
|
||||
{
|
||||
v2Mux := http.NewServeMux()
|
||||
v2Mux.Handle("/follow.html", a.renderDumbTplHandler("follow.html"))
|
||||
v2Mux.Handle("/posts/", a.renderPostHandler())
|
||||
v2Mux.Handle("/posts/", http.StripPrefix("/posts",
|
||||
a.renderPostHandler(),
|
||||
))
|
||||
v2Mux.Handle("/assets/", http.StripPrefix("/assets",
|
||||
apiutil.MethodMux(map[string]http.Handler{
|
||||
"GET": a.getPostAssetHandler(),
|
||||
|
@ -50,16 +50,41 @@ func resizeImage(out io.Writer, in io.Reader, maxWidth float64) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *api) renderPostAssetsIndexHandler() http.Handler {
|
||||
|
||||
tpl := a.mustParseBasedTpl("assets.html")
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ids, err := a.params.PostAssetStore.List()
|
||||
|
||||
if err != nil {
|
||||
apiutil.InternalServerError(
|
||||
rw, r, fmt.Errorf("getting list of asset ids: %w", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
tplPayload := struct {
|
||||
IDs []string
|
||||
}{
|
||||
IDs: ids,
|
||||
}
|
||||
|
||||
executeTemplate(rw, r, tpl, tplPayload)
|
||||
})
|
||||
}
|
||||
|
||||
func (a *api) getPostAssetHandler() http.Handler {
|
||||
|
||||
renderHandler := a.renderPostAssetsIndexHandler()
|
||||
renderIndexHandler := a.renderPostAssetsIndexHandler()
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
id := filepath.Base(r.URL.Path)
|
||||
|
||||
if id == "/" {
|
||||
renderHandler.ServeHTTP(rw, r)
|
||||
renderIndexHandler.ServeHTTP(rw, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,17 @@ import (
|
||||
func (a *api) renderPostHandler() http.Handler {
|
||||
|
||||
tpl := a.mustParseBasedTpl("post.html")
|
||||
renderIndexHandler := a.renderPostsIndexHandler()
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
id := strings.TrimSuffix(filepath.Base(r.URL.Path), ".html")
|
||||
|
||||
if id == "/" {
|
||||
renderIndexHandler.ServeHTTP(rw, r)
|
||||
return
|
||||
}
|
||||
|
||||
storedPost, err := a.params.PostStore.GetByID(id)
|
||||
|
||||
if errors.Is(err, post.ErrPostNotFound) {
|
||||
@ -88,25 +94,44 @@ func (a *api) renderPostHandler() http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func (a *api) renderPostAssetsIndexHandler() http.Handler {
|
||||
func (a *api) renderPostsIndexHandler() http.Handler {
|
||||
|
||||
tpl := a.mustParseBasedTpl("assets.html")
|
||||
tpl := a.mustParseBasedTpl("posts.html")
|
||||
const pageCount = 20
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ids, err := a.params.PostAssetStore.List()
|
||||
page, err := apiutil.StrToInt(r.FormValue("p"), 0)
|
||||
if err != nil {
|
||||
apiutil.BadRequest(
|
||||
rw, r, fmt.Errorf("invalid page number: %w", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
posts, hasMore, err := a.params.PostStore.WithOrderDesc().Get(page, pageCount)
|
||||
if err != nil {
|
||||
apiutil.InternalServerError(
|
||||
rw, r, fmt.Errorf("getting list of asset ids: %w", err),
|
||||
rw, r, fmt.Errorf("fetching page %d of posts: %w", page, err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
tplPayload := struct {
|
||||
IDs []string
|
||||
Posts []post.StoredPost
|
||||
PrevPage, NextPage int
|
||||
}{
|
||||
IDs: ids,
|
||||
Posts: posts,
|
||||
PrevPage: -1,
|
||||
NextPage: -1,
|
||||
}
|
||||
|
||||
if page > 0 {
|
||||
tplPayload.PrevPage = page - 1
|
||||
}
|
||||
|
||||
if hasMore {
|
||||
tplPayload.NextPage = page + 1
|
||||
}
|
||||
|
||||
executeTemplate(rw, r, tpl, tplPayload)
|
||||
|
@ -42,8 +42,12 @@ func (a *api) mustParseTpl(name string) *template.Template {
|
||||
|
||||
tpl := template.New("").Funcs(template.FuncMap{
|
||||
"BlogURL": blogURL,
|
||||
"AssetURL": func(path string) string {
|
||||
path = filepath.Join("assets", path)
|
||||
"AssetURL": func(id string) string {
|
||||
path := filepath.Join("assets", id)
|
||||
return blogURL(path)
|
||||
},
|
||||
"PostURL": func(id string) string {
|
||||
path := filepath.Join("posts", id)
|
||||
return blogURL(path)
|
||||
},
|
||||
})
|
||||
|
@ -33,7 +33,7 @@
|
||||
<td><a href="{{ AssetURL . }}" target="_blank">{{ . }}</a></td>
|
||||
<td>
|
||||
<form
|
||||
action="{{ BlogURL "assets/" }}{{ . }}?method=delete"
|
||||
action="{{ AssetURL . }}?method=delete"
|
||||
method="POST"
|
||||
style="margin-bottom: 0;"
|
||||
>
|
||||
|
@ -5,7 +5,7 @@
|
||||
{{ range .Payload.Posts }}
|
||||
<li>
|
||||
<h2>
|
||||
<a href="posts/{{ .HTTPPath }}">{{ .Title }}</a>
|
||||
<a href="{{ PostURL .ID }}">{{ .Title }}</a>
|
||||
</h2>
|
||||
<span>{{ .PublishedAt.Format "2006-01-02" }}</span>
|
||||
{{ if not .LastUpdatedAt.IsZero }}
|
||||
|
@ -19,10 +19,10 @@
|
||||
<p class="light"><em>
|
||||
This post is part of a series:<br/>
|
||||
{{ if .Payload.SeriesPrevious }}
|
||||
Previously: <a href="{{ .Payload.SeriesPrevious.HTTPPath }}">{{ .Payload.SeriesPrevious.Title }}</a></br>
|
||||
Previously: <a href="{{ PostURL .Payload.SeriesPrevious.ID }}">{{ .Payload.SeriesPrevious.Title }}</a></br>
|
||||
{{ end }}
|
||||
{{ if .Payload.SeriesNext }}
|
||||
Next: <a href="{{ .Payload.SeriesNext.HTTPPath }}">{{ .Payload.SeriesNext.Title }}</a></br>
|
||||
Next: <a href="{{ PostURL .Payload.SeriesNext.ID }}">{{ .Payload.SeriesNext.Title }}</a></br>
|
||||
{{ end }}
|
||||
</em></p>
|
||||
{{ end }}
|
||||
@ -35,10 +35,10 @@
|
||||
<p class="light"><em>
|
||||
If you liked this post, consider checking out other posts in the series:<br/>
|
||||
{{ if .Payload.SeriesPrevious }}
|
||||
Previously: <a href="{{ .Payload.SeriesPrevious.HTTPPath }}">{{ .Payload.SeriesPrevious.Title }}</a></br>
|
||||
Previously: <a href="{{ PostURL .Payload.SeriesPrevious.ID }}">{{ .Payload.SeriesPrevious.Title }}</a></br>
|
||||
{{ end }}
|
||||
{{ if .Payload.SeriesNext }}
|
||||
Next: <a href="{{ .Payload.SeriesNext.HTTPPath }}">{{ .Payload.SeriesNext.Title }}</a></br>
|
||||
Next: <a href="{{ PostURL .Payload.SeriesNext.ID }}">{{ .Payload.SeriesNext.Title }}</a></br>
|
||||
{{ end }}
|
||||
</em></p>
|
||||
{{ end }}
|
||||
|
61
srv/src/api/tpl/posts.html
Normal file
61
srv/src/api/tpl/posts.html
Normal file
@ -0,0 +1,61 @@
|
||||
{{ define "posts-nextprev" }}
|
||||
|
||||
{{ if or (ge .Payload.PrevPage 0) (ge .Payload.NextPage 0) }}
|
||||
<div id="page-turner">
|
||||
|
||||
{{ if ge .Payload.PrevPage 0 }}
|
||||
<a style="float: left;" href="?p={{ .Payload.PrevPage}}">Newer</a>
|
||||
{{ end }}
|
||||
|
||||
{{ if ge .Payload.NextPage 0 }}
|
||||
<a style="float:right;" href="?p={{ .Payload.NextPage}}">Older</a>
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ define "body" }}
|
||||
|
||||
{{ $csrfFormInput := .CSRFFormInput }}
|
||||
|
||||
|
||||
<p style="text-align: center;">
|
||||
<a href="{{ BlogURL "posts/" }}?method=new">
|
||||
<button>New Post</button>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
{{ template "posts-nextprev" . }}
|
||||
|
||||
<table style="margin-top: 2rem;">
|
||||
|
||||
{{ range .Payload.Posts }}
|
||||
<tr>
|
||||
<td>{{ .PublishedAt }}</td>
|
||||
<td><a href="{{ PostURL .ID }}" target="_blank">{{ .Title }}</a></td>
|
||||
<td>
|
||||
<a href="{{ PostURL .ID }}?method=edit">
|
||||
<button>Edit</button>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<form
|
||||
action="{{ PostURL .ID }}?method=delete"
|
||||
method="POST"
|
||||
>
|
||||
{{ $csrfFormInput }}
|
||||
<input type="submit" value="Delete" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
|
||||
</table>
|
||||
|
||||
{{ template "posts-nextprev" . }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ template "base.html" . }}
|
@ -36,12 +36,6 @@ type Post struct {
|
||||
Body string
|
||||
}
|
||||
|
||||
// HTTPPath returns the relative URL path of the StoredPost, when querying it
|
||||
// over HTTP.
|
||||
func (p Post) HTTPPath() string {
|
||||
return fmt.Sprintf("%s.html", p.ID)
|
||||
}
|
||||
|
||||
// StoredPost is a Post which has been stored in a Store, and has been given
|
||||
// some extra fields as a result.
|
||||
type StoredPost struct {
|
||||
|
@ -15,6 +15,10 @@ ul {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.light {
|
||||
color: #666;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user