optimised templates, broke tracks, improved music gateway UX. we ball

Signed-off-by: ari melody <ari@arimelody.me>
This commit is contained in:
ari melody 2024-03-21 05:19:18 +00:00
parent 6ec813dd58
commit 18c13699af
17 changed files with 593 additions and 496 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
**/.DS_Store
.idea/
tmp/

View file

@ -619,7 +619,7 @@ var placeholders = []MusicRelease{
},
}
func GetAlbum(id string) (MusicRelease, bool) {
func GetRelease(id string) (MusicRelease, bool) {
for _, album := range placeholders {
if album.Id == id {
return album, true
@ -628,7 +628,7 @@ func GetAlbum(id string) (MusicRelease, bool) {
return MusicRelease{}, false
}
func QueryAllAlbums() ([]MusicRelease) {
func QueryAllMusic() ([]MusicRelease) {
return placeholders
}

63
main.go
View file

@ -30,13 +30,12 @@ var mime_types = map[string]string{
"js": "application/javascript",
}
var templates = template.Must(template.ParseFiles(
var base_template = template.Must(template.ParseFiles(
"views/base.html",
"views/header.html",
"views/footer.html",
"views/index.html",
"views/music.html",
"views/music-gateway.html",
))
var htmx_template = template.Must(template.New("root").Parse(`<head>{{block "head" .}}{{end}}</head>{{block "content" .}}{{end}}`))
func log_request(req *http.Request, code int, start_time time.Time) {
now := time.Now()
@ -56,31 +55,41 @@ func log_request(req *http.Request, code int, start_time time.Time) {
)
}
func web_handler(writer http.ResponseWriter, req *http.Request) {
func handle_request(writer http.ResponseWriter, req *http.Request) {
uri := req.URL.Path
start_time := time.Now()
hx_boosted := len(req.Header["Hx-Boosted"]) > 0 && req.Header["Hx-Boosted"][0] == "true"
code := func(writer http.ResponseWriter, req *http.Request) int {
var root *template.Template
if hx_boosted {
root = template.Must(htmx_template.Clone())
} else {
root = template.Must(base_template.Clone())
}
if req.URL.Path == "/" {
code := index_handler(writer, req)
log_request(req, code, start_time)
return
return index_handler(writer, root)
}
if uri == "/music" {
code := music_handler(writer, req)
log_request(req, code, start_time)
return
return music_directory_handler(writer, root)
}
if strings.HasPrefix(uri, "/music/") {
code := music_gateway_handler(writer, req)
log_request(req, code, start_time)
return
return music_gateway_handler(writer, req, root)
}
code := static_handler(writer, req)
return static_handler(writer, req)
}(writer, req)
log_request(req, code, start_time)
}
func index_handler(writer http.ResponseWriter, req *http.Request) int {
err := templates.ExecuteTemplate(writer, "index.html", nil)
func index_handler(writer http.ResponseWriter, root *template.Template) int {
index_template := template.Must(root.ParseFiles("views/index.html"))
err := index_template.Execute(writer, nil)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500
@ -88,8 +97,10 @@ func index_handler(writer http.ResponseWriter, req *http.Request) int {
return 200
}
func music_handler(writer http.ResponseWriter, req *http.Request) int {
err := templates.ExecuteTemplate(writer, "music.html", music.QueryAllAlbums())
func music_directory_handler(writer http.ResponseWriter, root *template.Template) int {
music_template := template.Must(root.ParseFiles("views/music.html"))
music := music.QueryAllMusic()
err := music_template.Execute(writer, music)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500
@ -97,7 +108,7 @@ func music_handler(writer http.ResponseWriter, req *http.Request) int {
return 200
}
func music_gateway_handler(writer http.ResponseWriter, req *http.Request) int {
func music_gateway_handler(writer http.ResponseWriter, req *http.Request, root *template.Template) int {
if len(req.URL.Path) <= len("/music/") {
http.Error(writer, "400 bad request", http.StatusBadRequest)
return 400
@ -106,12 +117,13 @@ func music_gateway_handler(writer http.ResponseWriter, req *http.Request) int {
id := req.URL.Path[len("/music/"):]
// http.Redirect(writer, req, "https://mellodoot.com/music/"+title, 302)
// return
album, ok := music.GetAlbum(id)
release, ok := music.GetRelease(id)
if !ok {
http.Error(writer, "404 not found", http.StatusNotFound)
return 404
}
err := templates.ExecuteTemplate(writer, "music-gateway.html", album)
gateway_template := template.Must(root.ParseFiles("views/music-gateway.html"))
err := gateway_template.Execute(writer, release)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500
@ -180,22 +192,21 @@ func parse_markdown(md []byte) []byte {
func push_to_db_this_is_a_testing_thing_and_will_be_superfluous_later() {
db := InitDatabase()
defer db.Close()
for _, artist := range music.QueryAllArtists() {
PushArtist(db, artist)
}
for _, album := range music.QueryAllAlbums() {
for _, album := range music.QueryAllMusic() {
PushRelease(db, album)
}
defer db.Close()
}
func main() {
push_to_db_this_is_a_testing_thing_and_will_be_superfluous_later()
http.HandleFunc("/", web_handler)
http.HandleFunc("/", handle_request)
fmt.Printf("now serving at http://127.0.0.1:%d\n", PORT)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", PORT), nil))

1
public/script/lib/htmx.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -55,3 +55,11 @@ function fill_list(list) {
fill_list(e);
});
document.addEventListener("htmx:afterSwap", async event => {
const res = await event.detail.xhr.response;
var new_head = res.substring(res.indexOf("<head>")+1, res.indexOf("</head>"));
if (new_head) {
document.head.innerHTML = new_head;
}
window.scrollY = 0;
});

View file

@ -28,6 +28,34 @@ function apply_funny_bob_to_upcoming_tags() {
}
}
const extras_pairs = Array.from(document.querySelectorAll("div#info > div").values().map(container => {
return {
container,
button: document.getElementById("extras").querySelector(`ul li a[href="#${container.id}"]`)
};
}));
const info_container = document.getElementById("info")
info_container.addEventListener("scroll", update_extras_buttons);
function update_extras_buttons() {
console.clear();
const info_rect = info_container.getBoundingClientRect();
const info_y = info_rect.y;
const font_size = parseFloat(getComputedStyle(document.documentElement).fontSize);
console.log("info_y: " + info_y);
let current = extras_pairs[0];
extras_pairs.forEach(pair => {
pair.button.classList.remove("active");
const scroll_diff = pair.container.getBoundingClientRect().y -
info_rect.y -
info_rect.height / 2 +
4 * font_size;
if (scroll_diff <= 0) current = pair;
console.log(`${pair.container.id}: ${scroll_diff}`);
})
current.button.classList.add("active");
}
update_extras_buttons();
document.querySelectorAll("div#extras ul li a[href]").forEach(link => {
link.addEventListener("click", event => {
event.preventDefault();

View file

@ -63,4 +63,4 @@ function load_pride_flag_style() {
load_pride_flag_style();
pride_flag = create_pride_flag();
document.body.appendChild(pride_flag);
document.querySelector("main").appendChild(pride_flag);

View file

@ -3,7 +3,8 @@
main {
width: min(calc(100% - 4rem), 720px);
min-height: calc(100vh - 10.3rem);
margin: 5rem auto 2rem auto;
margin: 0 auto 2rem auto;
padding-top: 5rem;
}
main h1 {

View file

@ -21,6 +21,9 @@ body {
scroll-behavior: smooth;
}
main {
}
a {
color: var(--links);
text-decoration: none;

View file

@ -70,7 +70,6 @@ main {
gap: 4rem;
font-size: 16px;
animation: card-init .5s forwards;
overflow-y: clip;
padding: 4rem;
}
@ -97,7 +96,6 @@ main {
width: 33.3%;
height: 33.3%;
z-index: 2;
/* pointer-events: none; */
}
#art-container > div.tilt-topleft {
@ -208,10 +206,25 @@ div#vertical-line {
}
div#info {
display: flex;
flex-direction: column;
transition: transform .5s ease;
gap: 6rem;
margin: -4rem;
padding: 4rem;
height: 360px;
overflow: scroll;
scroll-behavior: smooth;
scrollbar-width: thin;
scrollbar-color: #111;
mask-image: linear-gradient(to bottom, transparent 0%, black 10%, black 90%, transparent 100%);
}
div#info > div {
min-height: 360px;
padding: 4rem 1rem 4rem 4rem;
margin: -4rem -3.5rem -4rem -4rem;
}
div#info p {
max-width: 500px;
white-space: pre-line;
}
#title-container {
@ -279,18 +292,26 @@ div#info {
display: inline-block;
}
#links {
ul#links {
width: 100%;
margin: 1rem 0;
padding: 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: .5rem
gap: .5rem;
list-style: none;
}
#links a {
padding: .5em .8em;
border-radius: 4px;
ul#links li {
flex-grow: 1;
}
ul#links a {
width: calc(100% - 1.6em);
padding: .5em .8em;
display: block;
border-radius: 4px;
font-size: 1em;
color: #111;
background-color: #fff;
@ -299,31 +320,31 @@ div#info {
transition: filter .1s,-webkit-filter .1s
}
#links a.buy {
ul#links a.buy {
background-color: #ff94e9
}
#links a.presave {
ul#links a.presave {
background-color: #ff94e9
}
#links a.spotify {
ul#links a.spotify {
background-color: #8cff83
}
#links a.applemusic {
ul#links a.applemusic {
background-color: #8cd9ff
}
#links a.soundcloud {
ul#links a.soundcloud {
background-color: #fdaa6d
}
#links a.youtube {
ul#links a.youtube {
background-color: #ff6e6e
}
#links a:hover {
ul#links a:hover {
filter: brightness(125%);
-webkit-filter: brightness(125%)
}
@ -357,24 +378,11 @@ div#extras ul li a {
transition: color .1s linear;
}
div#extras ul li a:hover {
div#extras ul li a:hover,
div#extras ul li a.active {
color: #eee;
}
div#info > div {
max-height: 360px;
min-height: 360px;
overflow-y: scroll;
padding: 2rem 1rem 2rem 4rem;
margin: -2rem -3.5rem -2rem -4rem;
mask-image: linear-gradient(to bottom, transparent 0%, black 10%, black 90%, transparent 100%);
}
div#info p {
max-width: 500px;
white-space: pre-line;
}
a.scrollback {
color: #888;
font-style: italic;
@ -497,6 +505,7 @@ footer a:hover {
text-decoration: underline
}
/*
@media only screen and (min-width: 1105px) {
div#music-container:not(:has(> div#info #credits:target)):not(:has(> div#info #lyrics:target)) {
div#extras ul li:first-of-type a {
@ -505,23 +514,18 @@ footer a:hover {
}
div#music-container:has(> div#info #credits:target) {
div#info {
transform: translateY(calc(-360px + -6rem));
}
div#extras ul li a[href="#credits"] {
color: #eee;
}
}
div#music-container:has(> div#info #lyrics:target) {
div#info {
transform: translateY(calc((-360px + -6rem) * 2));
}
div#extras ul li a[href="#lyrics"] {
color: #eee;
}
}
}
*/
@media only screen and (max-width: 1105px) {
main {
@ -557,11 +561,12 @@ footer a:hover {
div#info {
width: 100%;
gap: 2rem;
height: auto;
overflow-y: auto;
}
div#info > div {
max-height: fit-content;
min-height: fit-content;
min-height: auto;
padding: 0;
margin: 0;
overflow-y: unset;

50
views/base.html Normal file
View file

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{block "head" .}}
<!-- <title>ari melody 💫</title> -->
<!-- <link rel="shortcut icon" href="img/favicon.png" type="image/x-icon"> -->
<!---->
<!-- <meta name="description" content="home to your local SPACEGIRL 💫"> -->
<!---->
<!-- <meta property="og:title" content="ari melody"> -->
<!-- <meta property="og:type" content="website"> -->
<!-- <meta property="og:url" content="www.arimelody.me"> -->
<!-- <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="home to your local SPACEGIRL 💫"> -->
<!---->
<!-- <link rel="stylesheet" href="style/main.css"> -->
<!---->
<!-- <script type="module" src="/script/main.js" defer></script> -->
{{end}}
<!-- <script type="application/javascript" src="/script/lib/htmx.min.js"></script> -->
</head>
<body>
{{template "header"}}
{{block "content" .}}
<main>
<h1>
# hello, world!
</h1>
<p>
this is a default page!
</p>
</main>
{{end}}
{{template "footer"}}
<div id="overlay"></div>
</body>
</html>

View file

@ -1,6 +1,6 @@
{{define "footer"}}
<footer>
<footer hx-preserve="true">
<div id="footer">
<small><em>*made with ♥ by ari, 2024*</em></small>
</div>

View file

@ -1,6 +1,6 @@
{{define "header"}}
<header>
<header hx-preserve="true">
<nav>
<div id="header-home">
<img src="/img/favicon.png" id="header-icon" width="100" height="100" alt="">

28
views/htmx-base.html Normal file
View file

@ -0,0 +1,28 @@
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{block "head" .}}
<title>ari melody 💫</title>
<link rel="shortcut icon" href="img/favicon.png" type="image/x-icon">
<meta name="description" content="home to your local SPACEGIRL 💫">
<meta property="og:title" content="ari melody">
<meta property="og:type" content="website">
<meta property="og:url" content="www.arimelody.me">
<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="home to your local SPACEGIRL 💫">
<link rel="stylesheet" href="style/main.css">
<script type="module" src="/script/main.js" defer></script>
{{end}}
<script type="application/javascript" src="/script/lib/htmx.min.js"></script>
</head>
{{block "content" .}}{{end}}

View file

@ -1,13 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{define "head"}}
<title>ari melody 💫</title>
<link rel="shortcut icon" href="img/favicon.png" type="image/x-icon">
<link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
<meta name="description" content="home to your local SPACEGIRL 💫">
@ -18,14 +11,13 @@
<meta property="og:site_name" content="ari melody">
<meta property="og:description" content="home to your local SPACEGIRL 💫">
<link rel="stylesheet" href="style/index.css">
<link rel="stylesheet" href="/style/index.css">
<script type="module" src="/script/main.js" defer></script>
<link rel="me" href="https://wetdry.world/@ari">
</head>
<body>
{{block "header" .}}{{end}}
{{end}}
{{define "content"}}
<main>
<h1>
# hello, world!
@ -187,9 +179,4 @@
</a>
</div>
</main>
{{block "footer" .}}{{end}}
<div id="overlay"></div>
</body>
</html>
{{end}}

View file

@ -1,11 +1,4 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{define "head"}}
<title>{{.PrintPrimaryArtists}} - {{.Title}}</title>
<link rel="icon" href="{{.ResolveArtwork}}">
@ -32,18 +25,17 @@
<meta name="twitter:image:alt" content="Cover art for &quot;{{.Title}}&quot;">
<script type="module" src="/script/music-gateway.js" defer></script>
<script type="text/javascript" src="/script/prideflag.js" defer></script>
<script type="application/javascript" src="/script/prideflag.js" defer></script>
<link rel="stylesheet" href="/style/music-gateway.css">
</head>
<body>
{{end}}
{{define "content"}}
<main>
<div id="background" data-url="{{.ResolveArtwork}}"></div>
<div id="overlay"></div>
<a id="go-back" title="back to arimelody.me" href="/music">&lt;</a>
{{block "header" .}}{{end}}
<main>
<div id="music-container">
<div id="art-container">
<div class="tilt-topleft"></div>
@ -58,7 +50,7 @@
</div>
<div id="vertical-line"></div>
<div id="info">
<div id="main">
<div id="overview">
<div id="title-container">
<h1 id="title">{{.Title}}</h1>
<span id="year" title="{{.PrintReleaseDate}}">{{.GetReleaseYear}}</span>
@ -66,19 +58,19 @@
<p id="artist">{{.PrintPrimaryArtists}}</p>
<p id="type" class="{{.ResolveType}}">{{.ResolveType}}</p>
<div id="links">
<ul id="links">
{{if .Buylink}}
<a href="{{.Buylink}}" class="buy">
{{if .Buyname}}{{.Buyname}}{{else}}buy{{end}}
</a>
<li>
<a href="{{.Buylink}}" class="buy">{{or .Buyname "buy"}}</a>
</li>
{{end}}
{{range .Links}}
<a class="{{.NormaliseName}}" href="{{.Url}}">
{{.Name}}
</a>
<li>
<a class="{{.NormaliseName}}" href="{{.Url}}">{{.Name}}</a>
</li>
{{end}}
</div>
</ul>
{{if .Description}}
<p id="description">
@ -105,26 +97,26 @@
</div>
{{end}}
{{if .Lyrics}}
<div id="lyrics">
<h2>lyrics:</h2>
<p>{{.Lyrics}}</p>
</div>
{{end}}
<!-- {{if .Lyrics}} -->
<!-- <div id="lyrics"> -->
<!-- <h2>lyrics:</h2> -->
<!-- <p>{{.Lyrics}}</p> -->
<!-- </div> -->
<!-- {{end}} -->
</div>
{{if or .Credits .Lyrics}}
<div id="extras">
<ul>
<li><a href="#">overview</a></li>
<li><a href="#overview">overview</a></li>
{{if .Credits}}
<li><a href="#credits">credits</a></li>
{{end}}
{{if .Lyrics}}
<li><a href="#lyrics">lyrics</a></li>
{{end}}
<!-- {{if .Lyrics}} -->
<!-- <li><a href="#lyrics">lyrics</a></li> -->
<!-- {{end}} -->
</ul>
</div>
{{end}}
@ -154,7 +146,4 @@
<!-- </div> -->
</div>
</main>
{{block "footer" .}}{{end}}
</body>
</html>
{{end}}

View file

@ -1,11 +1,4 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{define "head"}}
<title>music - ari melody 💫</title>
<link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
@ -18,16 +11,13 @@
<meta property="og:site_name" content="ari melody">
<meta property="og:description" content="music from your local SPACEGIRL 💫">
<link rel="stylesheet" href="/style/main.css">
<link rel="stylesheet" href="/style/music.css">
<script type="module" src="/script/main.js" defer></script>
<script type="application/javascript" src="/script/accessibility.js" defer></script>
<script type="application/javascript" src="/script/music.js" defer></script>
</head>
<body>
{{block "header" .}}{{end}}
{{end}}
{{define "content"}}
<main>
<h1>
# my music
@ -96,9 +86,4 @@
</p>
</div>
</main>
{{block "footer" .}}{{end}}
<div id="overlay"></div>
</body>
</html>
{{end}}