blog visitor frontend (pretty much) done!
This commit is contained in:
parent
3d64333b4f
commit
faf6095d16
16 changed files with 903 additions and 463 deletions
187
view/blog.go
187
view/blog.go
|
@ -5,7 +5,9 @@ import (
|
|||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"arimelody-web/controller"
|
||||
"arimelody-web/model"
|
||||
|
@ -16,14 +18,26 @@ import (
|
|||
"github.com/gomarkdown/markdown/parser"
|
||||
)
|
||||
|
||||
type BlogView struct {
|
||||
*model.Blog
|
||||
Comments []*model.ThreadViewPost
|
||||
Likes int
|
||||
Reposts int
|
||||
BlueskyURL string
|
||||
MastodonURL string
|
||||
}
|
||||
type (
|
||||
BlogView struct {
|
||||
Collections []*BlogViewPostCollection
|
||||
}
|
||||
|
||||
BlogViewPostCollection struct {
|
||||
Name string
|
||||
Posts []*BlogPostView
|
||||
}
|
||||
|
||||
BlogPostView struct {
|
||||
*model.BlogPost
|
||||
Author *model.Account
|
||||
Comments []*model.ThreadViewPost
|
||||
Likes int
|
||||
Boosts int
|
||||
BlueskyURL string
|
||||
MastodonURL string
|
||||
}
|
||||
)
|
||||
|
||||
var mdRenderer = html.NewRenderer(html.RendererOptions{
|
||||
Flags: html.CommonFlags | html.HrefTargetBlank,
|
||||
|
@ -31,41 +45,105 @@ var mdRenderer = html.NewRenderer(html.RendererOptions{
|
|||
|
||||
func BlogHandler(app *model.AppState) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
blog := model.Blog{
|
||||
Title: "hello world!",
|
||||
Description: "lorem ipsum yadda yadda something boobies babababababababa",
|
||||
Visible: true,
|
||||
Date: time.Now(),
|
||||
AuthorID: "ari",
|
||||
Markdown:
|
||||
`
|
||||
**i'm ari!** (she/her) 🏳️⚧️🏳️🌈💫🦆🇮🇪
|
||||
if strings.Count(r.URL.Path, "/") > 1 {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
welcome to my blog!
|
||||
if len(r.URL.Path) > 1 {
|
||||
ServeBlogPost(app, r.URL.Path[1:]).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
i'm a musician, developer, streamer, youtuber, and probably a bunch of other things i forgot to mention!
|
||||
dbPosts, err := controller.GetBlogPosts(app.DB, true, -1, 0)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to fetch blog posts: %v\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
collections := []*BlogViewPostCollection{}
|
||||
posts := []*BlogPostView{}
|
||||
collectionYear := 0
|
||||
for i, post := range dbPosts {
|
||||
author, err := controller.GetAccountByID(app.DB, post.AuthorID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve author of blog %s: %v\n", post.ID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
## code block test
|
||||
if i == 0 {
|
||||
collectionYear = post.CreatedAt.Year()
|
||||
}
|
||||
|
||||
~~~ c
|
||||
#include <stdio.h>
|
||||
if post.CreatedAt.Year() != collectionYear || i == len(dbPosts) - 1 {
|
||||
if i == len(dbPosts) - 1 {
|
||||
posts = append(posts, &BlogPostView{
|
||||
BlogPost: post,
|
||||
Author: author,
|
||||
})
|
||||
}
|
||||
postsCopy := slices.Clone(posts)
|
||||
collections = append(collections, &BlogViewPostCollection{
|
||||
Name: strconv.Itoa(collectionYear),
|
||||
Posts: postsCopy,
|
||||
})
|
||||
posts = []*BlogPostView{}
|
||||
collectionYear = post.CreatedAt.Year()
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("hello world!~\n");
|
||||
return 0;
|
||||
}
|
||||
~~~
|
||||
posts = append(posts, &BlogPostView{
|
||||
BlogPost: post,
|
||||
Author: author,
|
||||
})
|
||||
}
|
||||
|
||||
## aridoodle
|
||||
|
||||
this is `+"`"+`aridoodle`+"`"+`. please take care of her.
|
||||
|
||||

|
||||
`,
|
||||
BlueskyActorID: "did:plc:yct6cvgfipngizry5umzkxr3",
|
||||
BlueskyPostID: "3llsudsx7pc2u",
|
||||
err = templates.BlogTemplate.Execute(w, BlogView{
|
||||
Collections: collections,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Error rendering blog post: %v\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func ServeBlogPost(app *model.AppState, blogPostID string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
blog, err := controller.GetBlogPost(app.DB, blogPostID)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to fetch blog post Bluesky thread: %v\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if !blog.Visible {
|
||||
session, err := controller.GetSessionFromRequest(app, r)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve session: %v\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if session == nil || session.Account == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
author, err := controller.GetAccountByID(app.DB, blog.AuthorID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve author of blog %s: %v\n", blog.ID, err)
|
||||
}
|
||||
|
||||
// blog.Markdown += " <i class=\"end-mark\"></i>"
|
||||
|
||||
|
@ -74,23 +152,32 @@ this is `+"`"+`aridoodle`+"`"+`. please take care of her.
|
|||
blog.HTML = template.HTML(markdown.Render(md, mdRenderer))
|
||||
|
||||
comments := []*model.ThreadViewPost{}
|
||||
blueskyPost, err := controller.FetchThreadViewPost(blog.BlueskyActorID, blog.BlueskyPostID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to fetch blog post Bluesky thread: %v\n", err)
|
||||
} else {
|
||||
comments = append(comments, blueskyPost.Replies...)
|
||||
}
|
||||
likeCount := 0
|
||||
boostCount := 0
|
||||
var blueskyURL string
|
||||
var blueskyPost *model.ThreadViewPost
|
||||
if blog.BlueskyActorID != nil && blog.BlueskyPostID != nil {
|
||||
blueskyPost, err = controller.FetchThreadViewPost(*blog.BlueskyActorID, *blog.BlueskyPostID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to fetch blog post Bluesky thread: %v\n", err)
|
||||
} else {
|
||||
comments = append(comments, blueskyPost.Replies...)
|
||||
likeCount += blueskyPost.Post.LikeCount
|
||||
boostCount += blueskyPost.Post.RepostCount
|
||||
blueskyURL = fmt.Sprintf("https://bsky.app/profile/%s/post/%s", blueskyPost.Post.Author.Handle, *blog.BlueskyPostID)
|
||||
}
|
||||
}
|
||||
|
||||
err = templates.BlogTemplate.Execute(w, BlogView{
|
||||
Blog: &blog,
|
||||
Comments: blueskyPost.Replies,
|
||||
Likes: blueskyPost.Post.LikeCount,
|
||||
Reposts: blueskyPost.Post.RepostCount,
|
||||
BlueskyURL: fmt.Sprintf("https://bsky.app/profile/%s/post/%s", blog.BlueskyActorID, blog.BlueskyPostID),
|
||||
MastodonURL: "#",
|
||||
err = templates.BlogPostTemplate.Execute(w, BlogPostView{
|
||||
BlogPost: blog,
|
||||
Author: author,
|
||||
Comments: comments,
|
||||
Likes: likeCount,
|
||||
Boosts: boostCount,
|
||||
BlueskyURL: blueskyURL,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error rendering blog post: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "WARN: Error rendering blog post: %v\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
|
166
view/blog.html
166
view/blog.html
|
@ -1,15 +1,15 @@
|
|||
{{define "head"}}
|
||||
<title>{{.Title}} - ari melody 💫</title>
|
||||
<title>blog - ari melody 💫</title>
|
||||
<link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
|
||||
|
||||
<meta name="description" content="{{.Description}}">
|
||||
<meta name="description" content="thoughts from your local SPACEGIRL 💫">
|
||||
|
||||
<meta property="og:title" content="{{.Title}}">
|
||||
<meta property="og:title" content="ari melody blog 💫">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="www.arimelody.me/blog/{{.Date.Year}}/{{.GetMonth}}/{{.TitleNormalised}}">
|
||||
<meta property="og:image" content="https://www.arimelody.me/img/favicon.png">
|
||||
<meta property="og:url" content="https://arimelody.space/blog/">
|
||||
<meta property="og:image" content="https://arimelody.space/img/favicon.png">
|
||||
<meta property="og:site_name" content="ari melody">
|
||||
<meta property="og:description" content="{{.Description}}">
|
||||
<meta property="og:description" content="thoughts from your local SPACEGIRL 💫">
|
||||
|
||||
<link rel="stylesheet" href="/style/main.css">
|
||||
<link rel="stylesheet" href="/style/index.css">
|
||||
|
@ -18,138 +18,34 @@
|
|||
|
||||
{{define "content"}}
|
||||
<main>
|
||||
<div id="blog-sidebar">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#copy-link" id="blog-copy-link" title="copy link">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/copy-link-dark.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/copy-link-light.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{{if ne .BlueskyURL ""}}
|
||||
<li>
|
||||
<a href="{{.BlueskyURL}}" id="blog-share-bsky" title="share on bluesky">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/bluesky-dark.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/bluesky-light.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{.BlueskyURL}}" id="blog-like" title="like this post">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/like-dark.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/like-light.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{.BlueskyURL}}" id="blog-boost" title="boost this post">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/boost-dark.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/boost-light.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#comments" id="blog-comments" title="comments">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/comment-dark.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/comment-light.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
<div id="blog-container">
|
||||
<article class="blog">
|
||||
<h1 class="typeout">{{.Title}}</h1>
|
||||
<p class="blog-date">Posted by <a href="/blog/{{.AuthorID}}">{{.AuthorID}}</a> @ {{.PrintDate}}</p>
|
||||
<h1 class="typeout"># blog</h1>
|
||||
<p class="">thoughts from your local SPACEGIRL 💫</p>
|
||||
|
||||
<hr>
|
||||
{{.HTML}}
|
||||
<hr>
|
||||
|
||||
</article>
|
||||
|
||||
{{if ne .BlueskyURL ""}}
|
||||
<hr>
|
||||
|
||||
<div id="interactions">
|
||||
<span class="likes">❤️ {{.Likes}}</span>
|
||||
<span class="reposts">🔁 {{.Reposts}}</span>
|
||||
</div>
|
||||
|
||||
<p class="comment-callout">
|
||||
join the conversation on
|
||||
<a class="bluesky" href="{{.BlueskyURL}}" target="_blank">Bluesky 🦋</a>
|
||||
<!-- TODO: mastodon support -->
|
||||
<!--
|
||||
and
|
||||
<a class="mastodon" href="{{.MastodonURL}}" target="_blank">Mastodon 🐘</a>
|
||||
-->
|
||||
</p>
|
||||
|
||||
<div id="comments">
|
||||
{{range .Comments}}
|
||||
{{template "comment" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="posts">
|
||||
{{if eq (len .Collections) 0}}
|
||||
<p>there are no posts! 🍃</p>
|
||||
{{end}}
|
||||
{{range .Collections}}
|
||||
<h2 id="{{.Name}}" class="collection-name">{{.Name}}</h2>
|
||||
{{range .Posts}}
|
||||
<article class="blog-post">
|
||||
<h3 class="blog-title"><a href="/blog/{{.ID}}">{{.Title}}</a></h3>
|
||||
<p class="blog-meta">
|
||||
<span class="blog-author"><img src="/img/favicon.png" alt="{{.Author.Username}}'s avatar" width="32" height="32"/> {{.Author.Username}}</span>
|
||||
<span class="blog-date">• {{.PrintDate}}</span>
|
||||
</p>
|
||||
{{if ne .Description ""}}
|
||||
<p class="blog-description">{{.Description}}</p>
|
||||
{{end}}
|
||||
</article>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
<script type="module" src="/script/blog.js"></script>
|
||||
</div>
|
||||
|
||||
<!-- <button type="submit" class="link-button" id="load-more">load more</button> -->
|
||||
|
||||
<script src="/script/blog.js" type="module" defer></script>
|
||||
</main>
|
||||
{{end}}
|
||||
|
||||
{{define "comment"}}
|
||||
<article class="comment">
|
||||
<div class="comment-hover">
|
||||
<div class="comment-header">
|
||||
<a href="https://bsky.app/profile/{{.Post.Author.DID}}" target="_blank">
|
||||
<img class="avatar" src="{{.Post.Author.Avatar}}" alt="{{.Post.Author.DisplayName}}'s avatar" width="32" height="32">
|
||||
<span class="display-name">{{.Post.Author.DisplayName}}</span>
|
||||
<span class="handle">@{{.Post.Author.Handle}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<a class="comment-body" href="{{.Post.BskyURL}}" target="_blank">
|
||||
<div>
|
||||
<p class="comment-text">{{.Post.Record.Text}}</p>
|
||||
{{if .Post.HasImage}}
|
||||
<p class="comment-images">
|
||||
{{range .Post.Embed.Media.Images}}
|
||||
<a href="{{.Fullsize}}" target="_blank">[image]</a>
|
||||
{{end}}
|
||||
</p>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="comment-footer">
|
||||
<span>{{.Post.LikeCount}} like{{if ne .Post.LikeCount 1}}s{{end}}</span>
|
||||
•
|
||||
<span>{{.Post.RepostCount}} repost{{if ne .Post.RepostCount 1}}s{{end}}</span>
|
||||
•
|
||||
<span class="comment-date">{{.Post.Record.CreatedAtPrint}}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="comment-replies">
|
||||
{{range .Replies}}
|
||||
{{template "comment" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</article>
|
||||
{{end}}
|
||||
|
||||
|
|
178
view/blogpost.html
Normal file
178
view/blogpost.html
Normal file
|
@ -0,0 +1,178 @@
|
|||
{{define "head"}}
|
||||
<title>{{.Title}} - ari melody 💫</title>
|
||||
<link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
|
||||
|
||||
<meta name="description" content="{{.Description}}">
|
||||
|
||||
<meta property="og:title" content="{{.Title}}">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://arimelody.space/blog/{{.ID}}">
|
||||
<meta property="og:image" content="https://arimelody.space/img/favicon.png">
|
||||
<meta property="og:site_name" content="ari melody">
|
||||
<meta property="og:description" content="{{.Description}}">
|
||||
|
||||
<link rel="stylesheet" href="/style/main.css">
|
||||
<link rel="stylesheet" href="/style/index.css">
|
||||
<link rel="stylesheet" href="/style/blogpost.css">
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<main>
|
||||
<div id="blog-sidebar">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#copy-link" id="blog-copy-link" title="copy link">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/copy-link-dark.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/copy-link-light.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{{if ne .BlueskyURL ""}}
|
||||
<li>
|
||||
<a href="{{.BlueskyURL}}" id="blog-share-bsky" title="share on bluesky">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/bluesky-dark.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/bluesky-light.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<hr>
|
||||
<li>
|
||||
<a href="{{.BlueskyURL}}" id="blog-like" title="like this post">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/like-dark.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/like-light.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{.BlueskyURL}}" id="blog-boost" title="boost this post">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/boost-dark.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/boost-light.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#comments" id="blog-comments" title="comments">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/comment-dark.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/comment-light.svg" alt="" width="36" height="36">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
<div id="blog-container">
|
||||
<article class="blog">
|
||||
<div id="blog-header">
|
||||
<h1 class="typeout"># {{.Title}}</h1>
|
||||
<p class="blog-author">by <a href="/blog?author={{.Author.Username}}">{{.Author.Username}} <img src="/img/favicon.png" alt="{{.Author.Username}}'s avatar" width="32" height="32"/></a></p>
|
||||
<p class="blog-date">posted {{.PrintDate}}{{if .ModifiedAt.Valid}} <span class="blog-modified-date">• updated {{.PrintModifiedDate}}</span>{{end}}</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div id="blog-content">
|
||||
{{.HTML}}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{{if ne .BlueskyURL ""}}
|
||||
<hr>
|
||||
|
||||
<div id="interactions">
|
||||
<button class="likes" aria-label="{{.Likes}} likes">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/like-dark.svg" alt="" width="32" height="32">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/like-light.svg" alt="" width="32" height="32">
|
||||
</div>
|
||||
{{.Likes}}
|
||||
</button>
|
||||
<button class="boosts" aria-label="{{.Boosts}} boosts">
|
||||
<div class="dark-only">
|
||||
<img src="/img/blog/boost-dark.svg" alt="" width="32" height="32">
|
||||
</div>
|
||||
<div class="light-only">
|
||||
<img src="/img/blog/boost-light.svg" alt="" width="32" height="32">
|
||||
</div>
|
||||
{{.Boosts}}
|
||||
</button>
|
||||
|
||||
<p class="comment-callout">
|
||||
join the conversation on
|
||||
<a class="bluesky" href="{{.BlueskyURL}}">Bluesky 🦋</a>
|
||||
<!-- TODO: mastodon support -->
|
||||
<!--
|
||||
and
|
||||
<a class="mastodon" href="{{.MastodonURL}}">Mastodon 🐘</a>
|
||||
-->
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="comments">
|
||||
{{range .Comments}}
|
||||
{{template "comment" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<script type="module" src="/script/blogpost.js"></script>
|
||||
</div>
|
||||
</main>
|
||||
{{end}}
|
||||
|
||||
{{define "comment"}}
|
||||
<article class="comment">
|
||||
<div class="comment-hover">
|
||||
<div class="comment-header">
|
||||
<a href="https://bsky.app/profile/{{.Post.Author.Handle}}" target="_blank">
|
||||
<img class="avatar" src="{{.Post.Author.Avatar}}" alt="{{.Post.Author.DisplayName}}'s avatar" width="32" height="32">
|
||||
<span class="display-name">{{.Post.Author.DisplayName}}</span>
|
||||
<span class="handle">@{{.Post.Author.Handle}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="comment-body" target="_blank">
|
||||
<div>
|
||||
<p class="comment-text">{{.Post.Record.Text}}</p>
|
||||
{{if .Post.HasImage}}
|
||||
<p class="comment-images">
|
||||
{{range .Post.Embed.Media.Images}}
|
||||
<a href="{{.Fullsize}}" target="_blank">[image]</a>
|
||||
{{end}}
|
||||
</p>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="comment-footer">
|
||||
<span class="comment-footer-static">
|
||||
<span>{{.Post.LikeCount}} like{{if ne .Post.LikeCount 1}}s{{end}}</span>
|
||||
•
|
||||
<span>{{.Post.RepostCount}} boost{{if ne .Post.RepostCount 1}}s{{end}}</span>
|
||||
•
|
||||
</span>
|
||||
<a href="{{.Post.BskyURL}}" class="comment-date">{{.Post.Record.CreatedAtPrint}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comment-replies">
|
||||
{{range .Replies}}
|
||||
{{template "comment" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</article>
|
||||
{{end}}
|
|
@ -21,11 +21,11 @@
|
|||
<a href="/" preload="mouseover">home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/music" preload="mouseover">music</a>
|
||||
<a href="/music/" preload="mouseover">music</a>
|
||||
</li>
|
||||
<li>
|
||||
<!-- coming later! -->
|
||||
<span title="coming later!">blog</span>
|
||||
<a href="/blog/" preload="mouseover">blog</a>
|
||||
</li>
|
||||
<li>
|
||||
<!-- coming later! -->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue