A number of style-based changes

This commit is contained in:
akp 2025-07-22 23:36:09 +01:00
parent 7f85a5848b
commit 6a875894a4
10 changed files with 265 additions and 89 deletions

View file

@ -0,0 +1,15 @@
package assets
import (
"embed"
"io/fs"
"net/http"
)
//go:embed static/*
var staticFS embed.FS
func New() http.Handler {
sffs, _ := fs.Sub(staticFS, "static")
return http.StripPrefix("/assets", http.FileServerFS(sffs))
}

View file

@ -0,0 +1,50 @@
/* hello */
html, body {
width: 100%;
min-height: 100%;
background-color: lightblue;
font-family: sans-serif;
}
main {
max-width: 60%; /* TODO: Responsivity */
margin: 1em auto;
background-color: white;
padding: 2em;
border-radius: 0.5em;
}
.display-none {
display: none;
}
ul.track-list {
list-style: none;
padding: 0;
}
ul.track-list li {
display: grid;
grid-template-columns: 1fr repeat(3, 3fr);
align-items: center;
gap: 0.5em;
padding: 0.5em;
}
ul.track-list li > div:not(.display-none):has(img) {
display: flex;
flex-direction: row;
justify-content: center;
}
ul.track-list li > div:not(.display-none):has(img) img {
height: 3em;
}
ul.track-list li:nth-of-type(odd) {
background-color: lightgray;
}
ul.track-list li:has(> input[type=checkbox]:checked) {
background-color: yellow;
}
ul.track-list li:has(> input[type=checkbox]:checked):nth-of-type(odd) {
background-color: yellowgreen;
}
/*# sourceMappingURL=main.css.map */

View file

@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":["main.scss"],"names":[],"mappings":"AAAA;AAEA;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;AAEA;EACI;EACA;EAIA;EACA;EACA;;AAEA;EAII;EACA;EACA;;AALA;EACI;;AAOR;EACI;;AAGJ;EACI;;AACA;EACI","file":"main.css"}

View file

@ -0,0 +1,56 @@
/* hello */
html, body {
width: 100%;
min-height: 100%;
background-color: lightblue;
font-family: sans-serif;
}
main {
max-width: 60%; /* TODO: Responsivity */
margin: 1em auto;
background-color: white;
padding: 2em;
border-radius: 0.5em;
}
.display-none {
display: none;
}
ul.track-list {
list-style: none;
padding: 0;
li {
display: grid;
grid-template-columns: 1fr repeat(3, 3fr);
//display: flex;
//flex-direction: row;
align-items: center;
gap: 0.5em;
padding: 0.5em;
> div:not(.display-none):has(img) {
img {
height: 3em;
}
display: flex;
flex-direction: row;
justify-content: center;
}
&:nth-of-type(odd) {
background-color: lightgray;
}
&:has(>input[type=checkbox]:checked) {
background-color: yellow;
&:nth-of-type(odd) {
background-color: yellowgreen;
}
}
}
}

View file

@ -5,43 +5,52 @@ import (
"github.com/zmb3/spotify/v2"
)
func formatArtistNames(artists []spotify.SimpleArtist) string {
var res string
for i, artist := range artists {
if i != 0 {
if i == len(artists) - 1 {
res += " and "
} else {
res += ", "
}
}
res += artist.Name
}
return res
}
func getSmallestImage(images []spotify.Image) spotify.Image {
var smallest *spotify.Image
for _, image := range images {
if smallest == nil || image.Width < smallest.Width {
smallest = &image
}
}
return *smallest
}
templ indexPage(recentlyListenedTracks []spotify.RecentlyPlayedItem) {
@httpUtil.BasePage("Recently Listened"){
<style>
tr:has(td>input[type=checkbox]:checked) {
background-color: yellow;
<ul class="track-list">
for _, record := range recentlyListenedTracks {
<li class="track-row">
<input class="display-none" name={ record.Track.ID } type="checkbox" />
{{ smallestImage := getSmallestImage(record.Track.Album.Images) }}
<div><img src={ smallestImage.URL } loading="lazy" alt={ "album art for " + record.Track.Name } /></div>
<div>{ record.Track.Name }</div>
<div>{ formatArtistNames(record.Track.Artists) }</div>
<div>{ record.PlayedAt.String() }</div>
</li>
}
</style>
<table>
<thead>
<tr>
<th></th>
<th>Track</th>
<th>Artist</th>
<th>Played at</th>
</tr>
</thead>
<tbody>
for _, record := range recentlyListenedTracks {
<tr>
<td><input type="checkbox"></td>
<td>{ record.Track.Name }</td>
<td>
for i, artist := range record.Track.Artists {
if i != 0 {
if i != len(record.Track.Artists) - 1 {
<span> and </span>
} else {
<span>, </span>
}
}
<span>{ artist.Name }</span>
}
</td>
<td>{ record.PlayedAt.String() }</td>
</tr>
}
</tbody>
</table>
</ul>
<script>
const onTrackClick = (elem) => {
let checkbox = elem.querySelector("input[type=checkbox]")
checkbox.checked = !checkbox.checked
}
document.querySelectorAll("li.track-row").forEach((elem) => { elem.addEventListener("click", () => { onTrackClick(elem) }) });
</script>
}
}

View file

@ -13,6 +13,31 @@ import (
"github.com/zmb3/spotify/v2"
)
func formatArtistNames(artists []spotify.SimpleArtist) string {
var res string
for i, artist := range artists {
if i != 0 {
if i == len(artists)-1 {
res += " and "
} else {
res += ", "
}
}
res += artist.Name
}
return res
}
func getSmallestImage(images []spotify.Image) spotify.Image {
var smallest *spotify.Image
for _, image := range images {
if smallest == nil || image.Width < smallest.Width {
smallest = &image
}
}
return *smallest
}
func indexPage(recentlyListenedTracks []spotify.RecentlyPlayedItem) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
@ -46,79 +71,100 @@ func indexPage(recentlyListenedTracks []spotify.RecentlyPlayedItem) templ.Compon
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<style>\n tr:has(td>input[type=checkbox]:checked) {\n background-color: yellow;\n }\n </style> <table><thead><tr><th></th><th>Track</th><th>Artist</th><th>Played at</th></tr></thead> <tbody>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<ul class=\"track-list\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, record := range recentlyListenedTracks {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<tr><td><input type=\"checkbox\"></td><td>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<li class=\"track-row\"><input class=\"display-none\" name=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(record.Track.Name)
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(record.Track.ID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/components/recentlyListened/index.templ`, Line: 28, Col: 47}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/components/recentlyListened/index.templ`, Line: 38, Col: 70}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</td><td>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" type=\"checkbox\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for i, artist := range record.Track.Artists {
if i != 0 {
if i != len(record.Track.Artists)-1 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<span>and </span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<span>, </span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, " <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(artist.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/components/recentlyListened/index.templ`, Line: 38, Col: 47}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
smallestImage := getSmallestImage(record.Track.Album.Images)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div><img src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</td><td>")
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(smallestImage.URL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/components/recentlyListened/index.templ`, Line: 40, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\" loading=\"lazy\" alt=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(record.PlayedAt.String())
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs("album art for " + record.Track.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/components/recentlyListened/index.templ`, Line: 41, Col: 54}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/components/recentlyListened/index.templ`, Line: 40, Col: 113}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</td></tr>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\"></div><div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(record.Track.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/components/recentlyListened/index.templ`, Line: 41, Col: 44}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div><div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(formatArtistNames(record.Track.Artists))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/components/recentlyListened/index.templ`, Line: 42, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div><div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(record.PlayedAt.String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/components/recentlyListened/index.templ`, Line: 43, Col: 51}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</tbody></table>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</ul><script>\n const onTrackClick = (elem) => {\n let checkbox = elem.querySelector(\"input[type=checkbox]\")\n checkbox.checked = !checkbox.checked\n }\n document.querySelectorAll(\"li.track-row\").forEach((elem) => { elem.addEventListener(\"click\", () => { onTrackClick(elem) }) });\n </script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View file

@ -47,13 +47,6 @@ func (r *RecentlyListened) index(rw http.ResponseWriter, rq *http.Request) error
return fmt.Errorf("get recently played tracks: %w", err)
}
for _, track := range recentlyPlayed {
var artistNames []string
for _, artist := range track.Track.Artists {
artistNames = append(artistNames, artist.Name)
}
}
rw.Header().Set("Content-Type", "text/html")
if err := indexPage(recentlyPlayed).Render(rq.Context(), rw); err != nil {

View file

@ -2,12 +2,16 @@ package httpUtil
templ BasePage(title string) {
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{ title } - Backseat Music</title>
<link rel="stylesheet" href="/assets/main.css" />
</head>
<body>
{ children... }
<main>
{ children... }
</main>
</body>
</html>
}

View file

@ -29,20 +29,20 @@ func BasePage(title string) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html><head><title>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/httpUtil/basePage.templ`, Line: 7, Col: 26}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `backseat/httpUtil/basePage.templ`, Line: 8, Col: 26}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " - Backseat Music</title></head><body>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " - Backseat Music</title><link rel=\"stylesheet\" href=\"/assets/main.css\"></head><body><main>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -50,7 +50,7 @@ func BasePage(title string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</body></html>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</main></body></html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View file

@ -2,6 +2,7 @@ package main
import (
"fmt"
"git.akpain.net/codemicro/backseat-music/backseat/components/assets"
"git.akpain.net/codemicro/backseat-music/backseat/components/recentlyListened"
"git.akpain.net/codemicro/backseat-music/backseat/components/spotifyAuth"
"git.akpain.net/codemicro/backseat-music/backseat/config"
@ -25,6 +26,7 @@ func run() error {
}
mux := http.NewServeMux()
mux.Handle("/assets/", assets.New())
mux.Handle("/recentlyListened/", recentlyListened.New(dataStore))
mux.Handle("/spotify/", spotifyAuth.New(dataStore))