Compare commits
No commits in common. "faf6095d1680d4ec3c78e40b7d9a269f396197c4" and "3da0249555ed533bbc639e058fcaa36ddc5d5525" have entirely different histories.
faf6095d16
...
3da0249555
|
@ -1,83 +0,0 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"arimelody-web/model"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func GetBlogPost(db *sqlx.DB, id string) (*model.BlogPost, error) {
|
||||
var blog = model.BlogPost{}
|
||||
|
||||
err := db.Get(&blog, "SELECT * FROM blogpost WHERE id=$1", id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &blog, nil
|
||||
}
|
||||
|
||||
func GetBlogPosts(db *sqlx.DB, onlyVisible bool, limit int, offset int) ([]*model.BlogPost, error) {
|
||||
var blogs = []*model.BlogPost{}
|
||||
|
||||
query := "SELECT * FROM blogpost ORDER BY created_at"
|
||||
if onlyVisible {
|
||||
query = "SELECT * FROM blogpost WHERE visible=true ORDER BY created_at"
|
||||
}
|
||||
|
||||
var err error
|
||||
if limit < 0 {
|
||||
err = db.Select(&blogs, query)
|
||||
} else {
|
||||
err = db.Select(&blogs, query + " LIMIT $1 OFFSET $2", limit, offset)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// for range 4 {
|
||||
// blog := *blogs[len(blogs)-1]
|
||||
// blog.CreatedAt = blog.CreatedAt.Add(time.Hour * -5000)
|
||||
// blogs = append(blogs, &blog)
|
||||
// }
|
||||
|
||||
return blogs, nil
|
||||
}
|
||||
|
||||
func CreateBlogPost(db *sqlx.DB, post *model.BlogPost) error {
|
||||
_, err := db.Exec(
|
||||
"INSERT INTO blogpost (id,title,description,visible,author,markdown,html,bluesky_actor,bluesky_post) " +
|
||||
"VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)",
|
||||
post.ID,
|
||||
post.Title,
|
||||
post.Description,
|
||||
post.Visible,
|
||||
post.AuthorID,
|
||||
post.Markdown,
|
||||
post.HTML,
|
||||
post.BlueskyActorID,
|
||||
post.BlueskyPostID,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateBlogPost(db *sqlx.DB, postID string, post *model.BlogPost) error {
|
||||
_, err := db.Exec(
|
||||
"UPDATE blogpost SET " +
|
||||
"id=$2,title=$3,description=$4,visible=$5,author=$6,markdown=$7,html=$8,bluesky_actor=$9,bluesky_post=$10,modified_at=CURRENT_TIMESTAMP " +
|
||||
"WHERE id=$1",
|
||||
postID,
|
||||
post.ID,
|
||||
post.Title,
|
||||
post.Description,
|
||||
post.Visible,
|
||||
post.AuthorID,
|
||||
post.Markdown,
|
||||
post.HTML,
|
||||
post.BlueskyActorID,
|
||||
post.BlueskyPostID,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
const DB_VERSION int = 5
|
||||
const DB_VERSION int = 4
|
||||
|
||||
func CheckDBVersionAndMigrate(db *sqlx.DB) {
|
||||
db.MustExec("CREATE SCHEMA IF NOT EXISTS arimelody")
|
||||
|
@ -49,10 +49,6 @@ func CheckDBVersionAndMigrate(db *sqlx.DB) {
|
|||
ApplyMigration(db, "003-fail-lock")
|
||||
oldDBVersion = 4
|
||||
|
||||
case 4:
|
||||
ApplyMigration(db, "004-blog")
|
||||
oldDBVersion = 5
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"regexp"
|
||||
|
@ -10,22 +9,20 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
BlogPost struct {
|
||||
ID string `db:"id"`
|
||||
Title string `db:"title"`
|
||||
Description string `db:"description"`
|
||||
Visible bool `db:"visible"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
ModifiedAt sql.NullTime `db:"modified_at"`
|
||||
AuthorID string `db:"author"`
|
||||
Markdown string `db:"markdown"`
|
||||
HTML template.HTML `db:"html"`
|
||||
BlueskyActorID *string `db:"bluesky_actor"`
|
||||
BlueskyPostID *string `db:"bluesky_post"`
|
||||
Blog struct {
|
||||
Title string `db:"title"`
|
||||
Description string `db:"description"`
|
||||
Visible bool `db:"visible"`
|
||||
Date time.Time `db:"date"`
|
||||
AuthorID string `db:"author"`
|
||||
Markdown string `db:"markdown"`
|
||||
HTML template.HTML `db:"html"`
|
||||
BlueskyActorID string `db:"bsky_actor"`
|
||||
BlueskyPostID string `db:"bsky_post"`
|
||||
}
|
||||
)
|
||||
|
||||
func (b *BlogPost) TitleNormalised() string {
|
||||
func (b *Blog) TitleNormalised() string {
|
||||
rgx := regexp.MustCompile(`[^a-z0-9\-]`)
|
||||
return rgx.ReplaceAllString(
|
||||
strings.ReplaceAll(
|
||||
|
@ -35,17 +32,10 @@ func (b *BlogPost) TitleNormalised() string {
|
|||
)
|
||||
}
|
||||
|
||||
func (b *BlogPost) GetMonth() string {
|
||||
return fmt.Sprintf("%02d", int(b.CreatedAt.Month()))
|
||||
func (b *Blog) GetMonth() string {
|
||||
return fmt.Sprintf("%02d", int(b.Date.Month()))
|
||||
}
|
||||
|
||||
func (b *BlogPost) PrintDate() string {
|
||||
return b.CreatedAt.Format("2 January 2006, 03:04")
|
||||
}
|
||||
|
||||
func (b *BlogPost) PrintModifiedDate() string {
|
||||
if !b.ModifiedAt.Valid {
|
||||
return ""
|
||||
}
|
||||
return b.ModifiedAt.Time.Format("2 January 2006, 03:04")
|
||||
func (b *Blog) PrintDate() string {
|
||||
return b.Date.Format("02 January 2006")
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ type (
|
|||
func (record *Record) CreatedAtPrint() (string, error) {
|
||||
t, err := record.CreatedAtTime()
|
||||
if err != nil { return "", err }
|
||||
return t.Format("2 Jan 2006, 15:04"), nil
|
||||
return t.Format("15:04, 2 February 2006"), nil
|
||||
}
|
||||
|
||||
func (record *Record) CreatedAtTime() (time.Time, error) {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 31 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="bluesky-dark" x="0" y="0" width="30.72" height="30.72" style="fill:none;"/><path d="M9.176,7.042c2.503,1.879 5.195,5.69 6.184,7.734c0.989,-2.044 3.681,-5.855 6.184,-7.734c1.806,-1.356 4.733,-2.405 4.733,0.933c-0,0.667 -0.383,5.601 -0.607,6.403c-0.779,2.785 -3.619,3.495 -6.145,3.065c4.415,0.752 5.539,3.241 3.113,5.73c-4.608,4.728 -6.622,-1.186 -7.138,-2.701c-0.095,-0.278 -0.139,-0.408 -0.14,-0.298c-0.001,-0.11 -0.045,0.02 -0.14,0.298c-0.516,1.515 -2.53,7.429 -7.138,2.701c-2.426,-2.489 -1.302,-4.978 3.113,-5.73c-2.526,0.43 -5.366,-0.28 -6.145,-3.065c-0.224,-0.802 -0.607,-5.736 -0.607,-6.403c0,-3.338 2.927,-2.289 4.733,-0.933Z" style="fill:#fff;fill-rule:nonzero;"/></svg>
|
Before Width: | Height: | Size: 1.1 KiB |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 31 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="bluesky-light" x="0" y="0" width="30.72" height="30.72" style="fill:none;"/><path d="M9.176,7.042c2.503,1.879 5.195,5.69 6.184,7.734c0.989,-2.044 3.681,-5.855 6.184,-7.734c1.806,-1.356 4.733,-2.405 4.733,0.933c-0,0.667 -0.383,5.601 -0.607,6.403c-0.779,2.785 -3.619,3.495 -6.145,3.065c4.415,0.752 5.539,3.241 3.113,5.73c-4.608,4.728 -6.622,-1.186 -7.138,-2.701c-0.095,-0.278 -0.139,-0.408 -0.14,-0.298c-0.001,-0.11 -0.045,0.02 -0.14,0.298c-0.516,1.515 -2.53,7.429 -7.138,2.701c-2.426,-2.489 -1.302,-4.978 3.113,-5.73c-2.526,0.43 -5.366,-0.28 -6.145,-3.065c-0.224,-0.802 -0.607,-5.736 -0.607,-6.403c0,-3.338 2.927,-2.289 4.733,-0.933Z" style="fill-rule:nonzero;"/></svg>
|
Before Width: | Height: | Size: 1.1 KiB |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 31 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="boost" x="0" y="0" width="30.72" height="30.72" style="fill:none;"/><path d="M20.14,5.466c1.708,-0.458 4.028,2.658 5.179,6.953c1.151,4.295 0.699,8.154 -1.008,8.611c-1.708,0.458 -4.029,-2.658 -5.179,-6.953c-1.151,-4.295 -0.699,-8.153 1.008,-8.611Zm0.954,3.56c-0.08,1.236 0.101,2.772 0.532,4.383c0.432,1.611 1.043,3.031 1.731,4.062c0.08,-1.237 -0.101,-2.772 -0.533,-4.383c-0.432,-1.611 -1.042,-3.031 -1.73,-4.062Zm-12.3,12.963l-0.907,0.056c-1.177,0.073 -2.242,-0.693 -2.548,-1.833l-0.468,-1.747c-0.305,-1.139 0.234,-2.336 1.29,-2.861l10.773,-8.542c0.173,-0.086 0.379,-0.064 0.53,0.056c0.151,0.12 0.218,0.316 0.174,0.503c-0.318,1.746 -0.19,4.164 0.496,6.724c0.685,2.559 1.784,4.717 2.928,6.074c0.132,0.138 0.171,0.34 0.101,0.517c-0.07,0.178 -0.236,0.299 -0.427,0.311c-1.7,0.108 -5.142,0.322 -8.287,0.516c-0.044,0.385 -0.09,0.771 -0.131,1.119c-0.091,0.771 -0.713,1.371 -1.486,1.435l-0.219,0.018c-0.647,0.054 -1.241,-0.355 -1.422,-0.978l-0.397,-1.368Z" style="fill:#fff;"/></svg>
|
Before Width: | Height: | Size: 1.4 KiB |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 31 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="boost" x="0" y="0" width="30.72" height="30.72" style="fill:none;"/><path d="M20.14,5.466c1.708,-0.458 4.028,2.658 5.179,6.953c1.151,4.295 0.699,8.154 -1.008,8.611c-1.708,0.458 -4.029,-2.658 -5.179,-6.953c-1.151,-4.295 -0.699,-8.153 1.008,-8.611Zm0.954,3.56c-0.08,1.236 0.101,2.772 0.532,4.383c0.432,1.611 1.043,3.031 1.731,4.062c0.08,-1.237 -0.101,-2.772 -0.533,-4.383c-0.432,-1.611 -1.042,-3.031 -1.73,-4.062Zm-12.3,12.963l-0.907,0.056c-1.177,0.073 -2.242,-0.693 -2.548,-1.833l-0.468,-1.747c-0.305,-1.139 0.234,-2.336 1.29,-2.861l10.773,-8.542c0.173,-0.086 0.379,-0.064 0.53,0.056c0.151,0.12 0.218,0.316 0.174,0.503c-0.318,1.746 -0.19,4.164 0.496,6.724c0.685,2.559 1.784,4.717 2.928,6.074c0.132,0.138 0.171,0.34 0.101,0.517c-0.07,0.178 -0.236,0.299 -0.427,0.311c-1.7,0.108 -5.142,0.322 -8.287,0.516c-0.044,0.385 -0.09,0.771 -0.131,1.119c-0.091,0.771 -0.713,1.371 -1.486,1.435l-0.219,0.018c-0.647,0.054 -1.241,-0.355 -1.422,-0.978l-0.397,-1.368Z"/></svg>
|
Before Width: | Height: | Size: 1.4 KiB |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 31 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="comment" x="0" y="0" width="30.72" height="30.72" style="fill:none;"/><path d="M26.117,14.576c-0,4.724 -4.82,8.559 -10.757,8.559c-1.015,-0 -2.025,-0.114 -3,-0.34l-5.008,1.907l-0.007,-4.418c-1.765,-1.569 -2.742,-3.601 -2.742,-5.708c0,-4.723 4.82,-8.558 10.757,-8.558c5.937,-0 10.757,3.835 10.757,8.558Zm-15.557,-1.135c-0.795,-0 -1.44,0.645 -1.44,1.44c0,0.795 0.645,1.44 1.44,1.44c0.795,-0 1.44,-0.645 1.44,-1.44c-0,-0.795 -0.645,-1.44 -1.44,-1.44Zm9.6,-0c-0.795,-0 -1.44,0.645 -1.44,1.44c-0,0.795 0.645,1.44 1.44,1.44c0.795,-0 1.44,-0.645 1.44,-1.44c-0,-0.795 -0.645,-1.44 -1.44,-1.44Zm-4.8,-0c-0.795,-0 -1.44,0.645 -1.44,1.44c-0,0.795 0.645,1.44 1.44,1.44c0.795,-0 1.44,-0.645 1.44,-1.44c-0,-0.795 -0.645,-1.44 -1.44,-1.44Z" style="fill:#fff;"/></svg>
|
Before Width: | Height: | Size: 1.2 KiB |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 31 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="comment" x="0" y="0" width="30.72" height="30.72" style="fill:none;"/><path d="M26.117,14.576c-0,4.724 -4.82,8.559 -10.757,8.559c-1.015,-0 -2.025,-0.114 -3,-0.34l-5.008,1.907l-0.007,-4.418c-1.765,-1.569 -2.742,-3.601 -2.742,-5.708c0,-4.723 4.82,-8.558 10.757,-8.558c5.937,-0 10.757,3.835 10.757,8.558Zm-15.557,-1.135c-0.795,-0 -1.44,0.645 -1.44,1.44c-0,0.795 0.645,1.44 1.44,1.44c0.795,-0 1.44,-0.645 1.44,-1.44c-0,-0.795 -0.645,-1.44 -1.44,-1.44Zm9.6,-0c-0.795,-0 -1.44,0.645 -1.44,1.44c-0,0.795 0.645,1.44 1.44,1.44c0.795,-0 1.44,-0.645 1.44,-1.44c-0,-0.795 -0.645,-1.44 -1.44,-1.44Zm-4.8,-0c-0.795,-0 -1.44,0.645 -1.44,1.44c-0,0.795 0.645,1.44 1.44,1.44c0.795,-0 1.44,-0.645 1.44,-1.44c-0,-0.795 -0.645,-1.44 -1.44,-1.44Z"/></svg>
|
Before Width: | Height: | Size: 1.2 KiB |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 31 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="copy-link" serif:id="copy link" x="0" y="0" width="30.72" height="30.72" style="fill:none;"/><path d="M10.578,14.996c-0.133,-1.199 0.107,-2.433 0.717,-3.512c0.254,-0.448 0.571,-0.869 0.953,-1.251l3.502,-3.503c2.274,-2.273 5.966,-2.273 8.24,0c2.273,2.274 2.273,5.966 -0,8.24l-1.962,1.962c-0.145,0.144 -0.364,0.186 -0.552,0.105c-0.188,-0.081 -0.307,-0.269 -0.302,-0.473c0.023,-0.778 -0.087,-1.559 -0.329,-2.307c-0.058,-0.179 -0.011,-0.375 0.122,-0.507l0.901,-0.902c1.103,-1.103 1.103,-2.893 0,-3.996c-1.103,-1.103 -2.893,-1.103 -3.996,-0l-3.503,3.502c-0.306,0.306 -0.527,0.665 -0.663,1.048c-0.355,0.996 -0.134,2.152 0.663,2.949c0.283,0.283 0.611,0.493 0.962,0.631c0.157,0.062 0.272,0.198 0.306,0.364c0.035,0.165 -0.016,0.336 -0.136,0.455l-1.491,1.491c-0.161,0.161 -0.411,0.193 -0.608,0.077c-0.412,-0.244 -0.8,-0.543 -1.154,-0.897c-0.973,-0.973 -1.53,-2.206 -1.67,-3.476Zm8.847,4.24c-0.254,0.448 -0.571,0.869 -0.953,1.251l-3.502,3.503c-2.274,2.273 -5.966,2.273 -8.24,-0c-2.273,-2.274 -2.273,-5.966 0,-8.24l1.962,-1.962c0.145,-0.144 0.364,-0.186 0.552,-0.105c0.188,0.081 0.307,0.269 0.302,0.473c-0.023,0.778 0.087,1.559 0.329,2.307c0.058,0.179 0.011,0.375 -0.122,0.507l-0.901,0.902c-1.103,1.103 -1.103,2.893 -0,3.996c1.103,1.103 2.893,1.103 3.996,0l3.503,-3.502c0.306,-0.306 0.527,-0.665 0.663,-1.048c0.355,-0.996 0.134,-2.152 -0.663,-2.949c-0.283,-0.283 -0.611,-0.493 -0.962,-0.631c-0.157,-0.062 -0.272,-0.198 -0.306,-0.364c-0.035,-0.165 0.016,-0.336 0.136,-0.455l1.491,-1.491c0.161,-0.161 0.411,-0.193 0.608,-0.077c0.412,0.244 0.8,0.543 1.154,0.897c0.973,0.973 1.53,2.205 1.67,3.476c0.133,1.199 -0.107,2.433 -0.717,3.512Z" style="fill:#fff;"/></svg>
|
Before Width: | Height: | Size: 2 KiB |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 31 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="copy-link" serif:id="copy link" x="0" y="-0" width="30.72" height="30.72" style="fill:none;"/><g id="copy-link1" serif:id="copy link"><path d="M10.578,14.996c-0.133,-1.199 0.107,-2.433 0.717,-3.512c0.254,-0.448 0.571,-0.869 0.953,-1.251l3.502,-3.503c2.274,-2.273 5.966,-2.273 8.24,0c2.273,2.274 2.273,5.966 -0,8.24l-1.962,1.962c-0.145,0.144 -0.364,0.186 -0.552,0.105c-0.188,-0.081 -0.307,-0.269 -0.302,-0.473c0.023,-0.778 -0.087,-1.559 -0.329,-2.307c-0.058,-0.179 -0.011,-0.375 0.122,-0.507l0.901,-0.902c1.103,-1.103 1.103,-2.893 0,-3.996c-1.103,-1.103 -2.893,-1.103 -3.996,-0l-3.503,3.502c-0.306,0.306 -0.527,0.665 -0.663,1.048c-0.355,0.996 -0.134,2.152 0.663,2.949c0.283,0.283 0.611,0.493 0.962,0.631c0.157,0.062 0.272,0.198 0.306,0.364c0.035,0.165 -0.016,0.336 -0.136,0.455l-1.491,1.491c-0.161,0.161 -0.411,0.193 -0.608,0.077c-0.412,-0.244 -0.8,-0.543 -1.154,-0.897c-0.973,-0.973 -1.53,-2.206 -1.67,-3.476Zm8.847,4.24c-0.254,0.448 -0.571,0.869 -0.953,1.251l-3.502,3.503c-2.274,2.273 -5.966,2.273 -8.24,-0c-2.273,-2.274 -2.273,-5.966 0,-8.24l1.962,-1.962c0.145,-0.144 0.364,-0.186 0.552,-0.105c0.188,0.081 0.307,0.269 0.302,0.473c-0.023,0.778 0.087,1.559 0.329,2.307c0.058,0.179 0.011,0.375 -0.122,0.507l-0.901,0.902c-1.103,1.103 -1.103,2.893 -0,3.996c1.103,1.103 2.893,1.103 3.996,0l3.503,-3.502c0.306,-0.306 0.527,-0.665 0.663,-1.048c0.355,-0.996 0.134,-2.152 -0.663,-2.949c-0.283,-0.283 -0.611,-0.493 -0.962,-0.631c-0.157,-0.062 -0.272,-0.198 -0.306,-0.364c-0.035,-0.165 0.016,-0.336 0.136,-0.455l1.491,-1.491c0.161,-0.161 0.411,-0.193 0.608,-0.077c0.412,0.244 0.8,0.543 1.154,0.897c0.973,0.973 1.53,2.205 1.67,3.476c0.133,1.199 -0.107,2.433 -0.717,3.512Z"/></g></svg>
|
Before Width: | Height: | Size: 2.1 KiB |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 31 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="like" x="0" y="0" width="30.72" height="30.72" style="fill:none;"/><path d="M15.36,9.159c2.176,-4.134 6.527,-4.134 8.703,-2.067c2.176,2.067 2.176,6.201 0,10.335c-1.523,3.1 -5.439,6.201 -8.703,8.268c-3.264,-2.067 -7.18,-5.168 -8.703,-8.268c-2.176,-4.134 -2.176,-8.268 -0,-10.335c2.176,-2.067 6.527,-2.067 8.703,2.067Z" style="fill:#fff;"/></svg>
|
Before Width: | Height: | Size: 794 B |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 31 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="like" x="0" y="0" width="30.72" height="30.72" style="fill:none;"/><path d="M15.36,9.159c2.176,-4.134 6.527,-4.134 8.703,-2.067c2.176,2.067 2.176,6.201 0,10.335c-1.523,3.1 -5.439,6.201 -8.703,8.268c-3.264,-2.067 -7.18,-5.168 -8.703,-8.268c-2.176,-4.134 -2.176,-8.268 -0,-10.335c2.176,-2.067 6.527,-2.067 8.703,2.067Z"/></svg>
|
Before Width: | Height: | Size: 775 B |
|
@ -1,15 +1,19 @@
|
|||
import { hijackClickEvent } from "./main.js";
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('.comment-hover').forEach((/** @type {HTMLDivElement} */ comment) => {
|
||||
/** @type {HTMLLinkElement} */
|
||||
const commentBody = comment.querySelector('a.comment-body');
|
||||
|
||||
document.querySelectorAll('article.blog-post').forEach(element => {
|
||||
const link = element.querySelector('.blog-title a');
|
||||
hijackClickEvent(element, link);
|
||||
});
|
||||
document.querySelectorAll('article.blog-post').forEach(element => {
|
||||
const link = element.querySelector('.blog-title a');
|
||||
hijackClickEvent(element, link);
|
||||
});
|
||||
comment.querySelectorAll('a').forEach((/** @type {HTMLLinkElement} */ element) => {
|
||||
element.addEventListener('click', event => {
|
||||
event.stopPropagation();
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('load-more').addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
alert('ok');
|
||||
comment.addEventListener('click', () => {
|
||||
commentBody.click();
|
||||
});
|
||||
|
||||
comment.style.cursor = 'pointer';
|
||||
comment.role = 'link';
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import { hijackClickEvent } from "./main.js";
|
||||
|
||||
document.querySelectorAll('.comment-hover').forEach((/** @type {HTMLDivElement} */ comment) => {
|
||||
/** @type {HTMLLinkElement} */
|
||||
const commentDate = comment.querySelector('.comment-date');
|
||||
hijackClickEvent(comment, commentDate);
|
||||
});
|
||||
|
||||
document.getElementById('blog-copy-link').addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
if (navigator.clipboard === undefined) {
|
||||
console.error("clipboard is not supported by this browser!");
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(location.protocol + "//" + location.host + location.pathname);
|
||||
});
|
|
@ -1,70 +1,216 @@
|
|||
article.blog-post {
|
||||
margin-bottom: 1rem;
|
||||
padding: 1.5rem;
|
||||
border: 1px solid #8882;
|
||||
border-radius: 4px;
|
||||
background-color: #ffffff08;
|
||||
transition: background-color .1s;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
:root {
|
||||
--like: rgb(223, 104, 104);
|
||||
--repost: rgb(162, 223, 73);
|
||||
--bluesky: rgb(16, 131, 254);
|
||||
--mastodon: rgb(86, 58, 204);
|
||||
}
|
||||
|
||||
.blog-post h2:hover,
|
||||
.blog-post p:hover {
|
||||
background: none;
|
||||
main {
|
||||
width: min(calc(100% - 4rem), 1200px);
|
||||
margin: 0 auto 1rem auto;
|
||||
}
|
||||
|
||||
.blog-title {
|
||||
margin: 0;
|
||||
}
|
||||
.blog-title a {
|
||||
display: inherit;
|
||||
text-wrap: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
.blog p:hover,
|
||||
.comment p:hover {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.blog-meta {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.blog-author {
|
||||
margin: 0;
|
||||
font-size: .8em;
|
||||
}
|
||||
.blog-author img {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
display: inline-block;
|
||||
transform: translate(0, 4px);
|
||||
border-radius: 4px;
|
||||
article.blog {
|
||||
font-family: 'Lora', serif;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.blog-date {
|
||||
margin: 0;
|
||||
font-size: .8em;
|
||||
margin-top: -1em;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
.blog-description {
|
||||
margin: .5em 0 0 0;
|
||||
display: -webkit-box;
|
||||
font-size: .8em;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
.blog p {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
#load-more {
|
||||
margin: 0 auto;
|
||||
padding: .5em 2em;
|
||||
.blog sub {
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
.blog pre {
|
||||
max-height: 15em;
|
||||
padding: .5em;
|
||||
font-size: .9em;
|
||||
border: 1px solid #8884;
|
||||
border-radius: 2px;
|
||||
overflow: scroll;
|
||||
background: var(--background-alt);
|
||||
}
|
||||
|
||||
.blog p code {
|
||||
padding: .2em .3em;
|
||||
font-size: .9em;
|
||||
border: 1px solid #8884;
|
||||
border-radius: 2px;
|
||||
background: var(--background-alt);
|
||||
}
|
||||
|
||||
.blog img {
|
||||
max-height: 50%;
|
||||
max-width: 100%;
|
||||
|
||||
display: block;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1500px) {
|
||||
#blog-sidebar {
|
||||
display: none;
|
||||
}
|
||||
.blog i.end-mark {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
margin-top: -.2em;
|
||||
|
||||
display: inline-block;
|
||||
transform: translateY(.2em);
|
||||
|
||||
background: url("/img/aridoodle.png");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* COMMENTS */
|
||||
|
||||
.interactions {
|
||||
margin: 1em 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: .5em;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: .4em .6em;
|
||||
border: 1px solid var(--on-background);
|
||||
border-radius: 2px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.interactions .likes,
|
||||
.interactions .reposts {
|
||||
padding: 0 .5em;
|
||||
min-width: fit-content;
|
||||
font-family: monospace;
|
||||
text-align: center;
|
||||
line-height: 2em;
|
||||
border: 1px solid var(--on-background);
|
||||
border-radius: 2px;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
.interactions .likes {
|
||||
border-color: var(--on-background);
|
||||
}
|
||||
.interactions .reposts {
|
||||
border-color: var(--on-background);
|
||||
}
|
||||
|
||||
.comment-callout {
|
||||
padding: 1em;
|
||||
background: var(--background-alt);
|
||||
border-radius: 2px;
|
||||
border: 1px solid #8884;
|
||||
text-align: center;
|
||||
}
|
||||
.comment-callout:hover {
|
||||
background: var(--background-alt);
|
||||
}
|
||||
|
||||
.btn.bluesky,
|
||||
.btn.mastodon {
|
||||
font-family: monospace;
|
||||
text-wrap: nowrap;
|
||||
text-decoration: none;
|
||||
transition-property: color background-color;
|
||||
transition-duration: .1s;
|
||||
}
|
||||
|
||||
.btn.bluesky {
|
||||
color: var(--bluesky);
|
||||
border-color: var(--bluesky);
|
||||
}
|
||||
.btn.bluesky:hover {
|
||||
background-color: var(--bluesky);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn.mastodon {
|
||||
color: var(--mastodon);
|
||||
border-color: var(--mastodon);
|
||||
}
|
||||
.btn.mastodon:hover {
|
||||
background-color: var(--mastodon);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.comment {
|
||||
font-family: 'Inter', 'Arial', sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.comment .comment-hover {
|
||||
padding: 1em;
|
||||
transition: background-color .1s;
|
||||
}
|
||||
|
||||
.comment .comment-hover:hover {
|
||||
background-color: #8881;
|
||||
}
|
||||
|
||||
.comment .comment-header a {
|
||||
display: flex;
|
||||
gap: .5em;
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.comment .comment-header a .display-name {
|
||||
overflow: inherit;
|
||||
text-overflow: inherit;
|
||||
}
|
||||
|
||||
.comment .comment-header a .handle {
|
||||
opacity: .5;
|
||||
font-family: monospace;
|
||||
font-size: .9em;
|
||||
overflow: inherit;
|
||||
text-overflow: inherit;
|
||||
}
|
||||
|
||||
.comment .comment-header img.avatar {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
.comment .comment-body {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.comment p.comment-text {
|
||||
margin: .5em 0;
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
.comment .comment-footer {
|
||||
margin: 0;
|
||||
font-size: .8em;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.comment .comment-replies {
|
||||
margin-left: 1em;
|
||||
border-left: 2px solid #8884;
|
||||
}
|
||||
|
|
|
@ -1,314 +0,0 @@
|
|||
:root {
|
||||
--like: rgb(223, 104, 104);
|
||||
--boost: rgb(162, 223, 73);
|
||||
--bluesky: rgb(16, 131, 254);
|
||||
--mastodon: rgb(86, 58, 204);
|
||||
}
|
||||
|
||||
main {
|
||||
width: min(calc(100% - 4rem), 1200px);
|
||||
margin: 0 auto 1rem auto;
|
||||
}
|
||||
|
||||
#blog-sidebar {
|
||||
position: fixed;
|
||||
width: 3em;
|
||||
padding: 3em;
|
||||
transform: translate(-9em, -1em);
|
||||
overflow: clip;
|
||||
opacity: .5;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
#blog-sidebar:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#blog-sidebar ul {
|
||||
margin: 0;
|
||||
padding: .3em;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .3em;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--on-background);
|
||||
box-shadow: 4px 4px 4px #0001;
|
||||
}
|
||||
|
||||
#blog-sidebar a {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
display: block;
|
||||
padding: .2em;
|
||||
border-radius: 2px;
|
||||
text-decoration: none;
|
||||
}
|
||||
#blog-sidebar a:hover {
|
||||
background: #0001;
|
||||
}
|
||||
#blog-sidebar a:active {
|
||||
background: #0002;
|
||||
}
|
||||
|
||||
#blog-sidebar a img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
#blog-sidebar span {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
#blog-sidebar hr {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.blog p:hover,
|
||||
.comment p:hover {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
article.blog {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.blog h1 {
|
||||
margin-bottom: 0;
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.blog-author {
|
||||
margin: .2em 0;
|
||||
}
|
||||
.blog .blog-author img {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
display: inline-block;
|
||||
transform: translate(0, 6px);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.blog-date {
|
||||
margin: .5em 0;
|
||||
font-size: .7em;
|
||||
}
|
||||
.blog-modified-date {
|
||||
font-style: italic;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
#blog-content {
|
||||
font-family: 'Lora', serif;
|
||||
}
|
||||
|
||||
.blog p {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.blog sub {
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
.blog pre {
|
||||
max-height: 15em;
|
||||
padding: .5em;
|
||||
font-size: .9em;
|
||||
border: 1px solid #8884;
|
||||
border-radius: 2px;
|
||||
overflow: scroll;
|
||||
background: var(--background-alt);
|
||||
}
|
||||
|
||||
.blog p code {
|
||||
padding: .2em .3em;
|
||||
font-size: .9em;
|
||||
border: 1px solid #8884;
|
||||
border-radius: 2px;
|
||||
background: var(--background-alt);
|
||||
}
|
||||
|
||||
.blog img {
|
||||
max-height: 50%;
|
||||
max-width: 100%;
|
||||
|
||||
display: block;
|
||||
}
|
||||
|
||||
.blog i.end-mark {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
margin-top: -.2em;
|
||||
|
||||
display: inline-block;
|
||||
transform: translateY(.2em);
|
||||
|
||||
background: url("/img/aridoodle.png");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* COMMENTS */
|
||||
|
||||
#interactions {
|
||||
margin: 1em 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: .5em;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: .4em .6em;
|
||||
border: 1px solid var(--on-background);
|
||||
border-radius: 2px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#interactions button {
|
||||
min-width: fit-content;
|
||||
padding: 0 .75em 0 .5em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: .5em;
|
||||
|
||||
font-family: monospace;
|
||||
font-size: inherit;
|
||||
text-align: center;
|
||||
line-height: 2em;
|
||||
text-wrap: nowrap;
|
||||
|
||||
color: inherit;
|
||||
background: none;
|
||||
border: 1px solid var(--on-background);
|
||||
border-radius: 4px;
|
||||
}
|
||||
#interactions button:hover {
|
||||
background: #0001;
|
||||
}
|
||||
#interactions button:active {
|
||||
background: #0002;
|
||||
}
|
||||
|
||||
#interactions img {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.comment-callout {
|
||||
margin: 0 0 0 1em;
|
||||
}
|
||||
.comment-callout:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.bluesky {
|
||||
color: var(--bluesky);
|
||||
}
|
||||
|
||||
.mastodon {
|
||||
color: var(--mastodon);
|
||||
}
|
||||
|
||||
.comment {
|
||||
font-family: 'Inter', 'Arial', sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.comment .comment-hover {
|
||||
padding: 1em;
|
||||
transition: background-color .1s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.comment .comment-hover:hover {
|
||||
background-color: #8881;
|
||||
}
|
||||
|
||||
.comment .comment-header a {
|
||||
display: flex;
|
||||
gap: .5em;
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.comment .comment-header a .display-name {
|
||||
overflow: inherit;
|
||||
text-overflow: inherit;
|
||||
}
|
||||
|
||||
.comment .comment-header a .handle {
|
||||
opacity: .5;
|
||||
font-family: monospace;
|
||||
font-size: .9em;
|
||||
overflow: inherit;
|
||||
text-overflow: inherit;
|
||||
}
|
||||
|
||||
.comment .comment-header img.avatar {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.comment .comment-body {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.comment p.comment-text {
|
||||
margin: .5em 0;
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
.comment .comment-footer {
|
||||
margin: 0;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
.comment .comment-footer .comment-footer-static {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.comment .comment-replies {
|
||||
margin-left: 1em;
|
||||
border-left: 2px solid #8884;
|
||||
}
|
||||
|
||||
.comment .comment-date {
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
#blog-sidebar a:hover {
|
||||
background: #fff2;
|
||||
}
|
||||
#blog-sidebar a:active {
|
||||
background: #fff4;
|
||||
}
|
||||
|
||||
.comment-date {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ a:hover {
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.link-button {
|
||||
a.link-button {
|
||||
padding: .3em .5em;
|
||||
border: 1px solid var(--links);
|
||||
color: var(--links);
|
||||
|
@ -51,7 +51,7 @@ a:hover {
|
|||
opacity: 0;
|
||||
}
|
||||
|
||||
.link-button:hover {
|
||||
a.link-button:hover {
|
||||
color: #eee;
|
||||
border-color: #eee;
|
||||
background-color: var(--links) !important;
|
||||
|
@ -141,23 +141,8 @@ a#backtotop:hover {
|
|||
}
|
||||
}
|
||||
|
||||
.light-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark-only {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.light-only {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.dark-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a.link-button:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
@ -176,14 +161,6 @@ a#backtotop:hover {
|
|||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.light-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark-only {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
body.crt {
|
||||
text-shadow: 0 0 3em;
|
||||
}
|
||||
|
|
|
@ -127,39 +127,17 @@ ALTER TABLE arimelody.musicreleasetrack ADD CONSTRAINT musicreleasetrack_pk PRIM
|
|||
|
||||
|
||||
|
||||
CREATE TABLE arimelody.blogpost (
|
||||
id TEXT NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
visible BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
modified_at TIMESTAMP,
|
||||
author UUID NOT NULL,
|
||||
markdown TEXT NOT NULL,
|
||||
html TEXT NOT NULL,
|
||||
bluesky_actor TEXT,
|
||||
bluesky_post TEXT
|
||||
);
|
||||
ALTER TABLE arimelody.blogpost ADD CONSTRAINT blogpost_pk PRIMARY KEY (id);
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Foreign keys
|
||||
--
|
||||
|
||||
-- Account
|
||||
ALTER TABLE arimelody.privilege ADD CONSTRAINT privilege_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_attempt_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.totp ADD CONSTRAINT totp_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
|
||||
-- Music
|
||||
ALTER TABLE arimelody.musiccredit ADD CONSTRAINT musiccredit_artist_fk FOREIGN KEY (artist) REFERENCES artist(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE arimelody.musiccredit ADD CONSTRAINT musiccredit_release_fk FOREIGN KEY (release) REFERENCES musicrelease(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.musiclink ADD CONSTRAINT musiclink_release_fk FOREIGN KEY (release) REFERENCES musicrelease(id) ON UPDATE CASCADE ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.musicreleasetrack ADD CONSTRAINT music_pair_trackref_fk FOREIGN KEY (release) REFERENCES musicrelease(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.musicreleasetrack ADD CONSTRAINT music_pair_releaseref_fk FOREIGN KEY (track) REFERENCES musictrack(id) ON DELETE CASCADE;
|
||||
|
||||
-- Blog
|
||||
ALTER TABLE arimelody.blogpost ADD CONSTRAINT blogpost_author_fk FOREIGN KEY (author) REFERENCES account(id) ON DELETE CASCADE;
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
CREATE TABLE arimelody.blogpost (
|
||||
id TEXT NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
visible BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
modified_at TIMESTAMP,
|
||||
author UUID NOT NULL,
|
||||
markdown TEXT NOT NULL,
|
||||
html TEXT NOT NULL,
|
||||
bluesky_actor TEXT,
|
||||
bluesky_post TEXT
|
||||
);
|
||||
ALTER TABLE arimelody.blogpost ADD CONSTRAINT blogpost_pk PRIMARY KEY (id);
|
||||
ALTER TABLE arimelody.blogpost ADD CONSTRAINT blogpost_author_fk FOREIGN KEY (author) REFERENCES account(id) ON DELETE CASCADE;
|
|
@ -33,10 +33,3 @@ var BlogTemplate = template.Must(template.ParseFiles(
|
|||
filepath.Join("view", "prideflag.html"),
|
||||
filepath.Join("view", "blog.html"),
|
||||
))
|
||||
var BlogPostTemplate = template.Must(template.ParseFiles(
|
||||
filepath.Join("view", "layout.html"),
|
||||
filepath.Join("view", "header.html"),
|
||||
filepath.Join("view", "footer.html"),
|
||||
filepath.Join("view", "prideflag.html"),
|
||||
filepath.Join("view", "blogpost.html"),
|
||||
))
|
||||
|
|
218
view/blog.go
|
@ -5,9 +5,7 @@ import (
|
|||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"arimelody-web/controller"
|
||||
"arimelody-web/model"
|
||||
|
@ -18,26 +16,14 @@ import (
|
|||
"github.com/gomarkdown/markdown/parser"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
)
|
||||
type BlogView struct {
|
||||
*model.Blog
|
||||
Comments []*model.ThreadViewPost
|
||||
Likes int
|
||||
Reposts int
|
||||
BlueskyURL string
|
||||
MastodonURL string
|
||||
}
|
||||
|
||||
var mdRenderer = html.NewRenderer(html.RendererOptions{
|
||||
Flags: html.CommonFlags | html.HrefTargetBlank,
|
||||
|
@ -45,105 +31,78 @@ var mdRenderer = html.NewRenderer(html.RendererOptions{
|
|||
|
||||
func BlogHandler(app *model.AppState) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.Count(r.URL.Path, "/") > 1 {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
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!**
|
||||
|
||||
if len(r.URL.Path) > 1 {
|
||||
ServeBlogPost(app, r.URL.Path[1:]).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
she/her 🏳️⚧️🏳️🌈💫🦆🇮🇪
|
||||
|
||||
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
|
||||
}
|
||||
i'm a musician, developer, streamer, youtuber, and probably a bunch of other things i forgot to mention!
|
||||
|
||||
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
|
||||
}
|
||||
you're very welcome to take a look around my little space on the internet here, or explore any of the other parts i inhabit!
|
||||
|
||||
if i == 0 {
|
||||
collectionYear = post.CreatedAt.Year()
|
||||
}
|
||||
if you're looking to support me financially, that's so cool of you!! if you like, you can buy some of my music over on bandcamp so you can at least get something for your money. thank you very much either way!! 💕
|
||||
|
||||
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()
|
||||
}
|
||||
for anything else, you can reach me for any and all communications through ari@arimelody.me. if your message contains anything beyond a silly gag, i strongly recommend encrypting your message using my public pgp key, listed below!
|
||||
|
||||
posts = append(posts, &BlogPostView{
|
||||
BlogPost: post,
|
||||
Author: author,
|
||||
})
|
||||
}
|
||||
thank you for stopping by- i hope you have a lovely rest of your day! 💫
|
||||
|
||||
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
|
||||
}
|
||||
})
|
||||
## metadata
|
||||
|
||||
**my colours 🌈**
|
||||
|
||||
- primary: <span class="col-primary">#b7fd49</span>
|
||||
- secondary: <span class="col-secondary">#f8e05b</span>
|
||||
- tertiary: <span class="col-tertiary">#f788fe</span>
|
||||
|
||||
**my keys 🔑**
|
||||
|
||||
- pgp: [[link]](/keys/ari%20melody_0x92678188_public.asc)
|
||||
- ssh (ed25519): [[link]](/keys/id_ari_ed25519.pub)
|
||||
|
||||
**where to find me 🛰️**
|
||||
|
||||
- youtube
|
||||
- twitch
|
||||
- spotify
|
||||
- bandcamp
|
||||
- github
|
||||
|
||||
**projects i've worked on 🛠️**
|
||||
|
||||
- catdance
|
||||
- pride flag
|
||||
- ipaddrgen
|
||||
- impact meme
|
||||
- OpenTerminal
|
||||
- Silver.js
|
||||
|
||||
### code block test
|
||||
|
||||
~~~ c
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("hello world!~\n");
|
||||
return 0;
|
||||
}
|
||||
~~~
|
||||
|
||||
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
|
||||
}
|
||||
### aridoodle
|
||||
|
||||
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
|
||||
}
|
||||
this is `+"`"+`aridoodle`+"`"+`. please take care of her.
|
||||
|
||||
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)
|
||||
}
|
||||

|
||||
`,
|
||||
BlueskyActorID: "did:plc:yct6cvgfipngizry5umzkxr3",
|
||||
BlueskyPostID: "3llsudsx7pc2u",
|
||||
}
|
||||
|
||||
// blog.Markdown += " <i class=\"end-mark\"></i>"
|
||||
|
||||
|
@ -152,32 +111,23 @@ func ServeBlogPost(app *model.AppState, blogPostID string) http.Handler {
|
|||
blog.HTML = template.HTML(markdown.Render(md, mdRenderer))
|
||||
|
||||
comments := []*model.ThreadViewPost{}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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...)
|
||||
}
|
||||
|
||||
err = templates.BlogPostTemplate.Execute(w, BlogPostView{
|
||||
BlogPost: blog,
|
||||
Author: author,
|
||||
Comments: comments,
|
||||
Likes: likeCount,
|
||||
Boosts: boostCount,
|
||||
BlueskyURL: blueskyURL,
|
||||
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: "#",
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Error rendering blog post: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "Error rendering blog post: %v\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
|
102
view/blog.html
|
@ -1,15 +1,15 @@
|
|||
{{define "head"}}
|
||||
<title>blog - ari melody 💫</title>
|
||||
<title>{{.Title}} - ari melody 💫</title>
|
||||
<link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
|
||||
|
||||
<meta name="description" content="thoughts from your local SPACEGIRL 💫">
|
||||
<meta name="description" content="{{.Description}}">
|
||||
|
||||
<meta property="og:title" content="ari melody blog 💫">
|
||||
<meta property="og:title" content="{{.Title}}">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://arimelody.space/blog/">
|
||||
<meta property="og:image" content="https://arimelody.space/img/favicon.png">
|
||||
<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:site_name" content="ari melody">
|
||||
<meta property="og:description" content="thoughts from your local SPACEGIRL 💫">
|
||||
<meta property="og:description" content="{{.Description}}">
|
||||
|
||||
<link rel="stylesheet" href="/style/main.css">
|
||||
<link rel="stylesheet" href="/style/index.css">
|
||||
|
@ -18,34 +18,78 @@
|
|||
|
||||
{{define "content"}}
|
||||
<main>
|
||||
<h1 class="typeout"># blog</h1>
|
||||
<p class="">thoughts from your local SPACEGIRL 💫</p>
|
||||
<article class="blog">
|
||||
<h1 class="typeout">{{.Title}}</h1>
|
||||
<p class="blog-date">Posted by <a href="/blog/{{.AuthorID}}">{{.AuthorID}}</a> @ {{.PrintDate}}</p>
|
||||
|
||||
<hr>
|
||||
{{.HTML}}
|
||||
|
||||
</article>
|
||||
|
||||
<hr>
|
||||
|
||||
<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}}
|
||||
<div class="interactions">
|
||||
<span class="likes">❤️ {{.Likes}}</span>
|
||||
<span class="reposts">🔁 {{.Reposts}}</span>
|
||||
</div>
|
||||
|
||||
<p class="comment-callout">
|
||||
join the conversation on
|
||||
<a class="btn bluesky" href="{{.BlueskyURL}}" target="_blank">Bluesky 🦋</a>
|
||||
<!-- TODO: mastodon support -->
|
||||
<!--
|
||||
or
|
||||
<a class="btn mastodon" href="{{.MastodonURL}}" target="_blank">Mastodon 🐘</a>
|
||||
-->
|
||||
</p>
|
||||
|
||||
<div class="comments">
|
||||
{{range .Comments}}
|
||||
{{template "comment" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<!-- <button type="submit" class="link-button" id="load-more">load more</button> -->
|
||||
|
||||
<script src="/script/blog.js" type="module" defer></script>
|
||||
<script type="module" src="/script/blog.js"></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">
|
||||
<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}}
|
||||
|
||||
|
|
|
@ -1,178 +0,0 @@
|
|||
{{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! -->
|
||||
<a href="/blog/" preload="mouseover">blog</a>
|
||||
<span title="coming later!">blog</span>
|
||||
</li>
|
||||
<li>
|
||||
<!-- coming later! -->
|
||||
|
|