From 0bce16dc09afef44b3533d53dc37636fb1d8903a Mon Sep 17 00:00:00 2001 From: Abigail Pain Date: Wed, 23 Jul 2025 23:44:48 +0100 Subject: [PATCH] Alter 9 files Add _colours.scss Update main.css Update main.css.map Update main.scss Update index.templ Update index_templ.go Update recentlyListened.go Update basePage.templ Update basePage_templ.go --- .../components/assets/static/_colours.scss | 244 ++++++++++++++ backseat/components/assets/static/main.css | 30 +- .../components/assets/static/main.css.map | 2 +- backseat/components/assets/static/main.scss | 33 +- .../components/recentlyListened/index.templ | 67 +++- .../recentlyListened/index_templ.go | 317 +++++++++++++----- .../recentlyListened/recentlyListened.go | 72 +++- backseat/httpUtil/basePage.templ | 15 + backseat/httpUtil/basePage_templ.go | 31 +- 9 files changed, 678 insertions(+), 133 deletions(-) create mode 100644 backseat/components/assets/static/_colours.scss diff --git a/backseat/components/assets/static/_colours.scss b/backseat/components/assets/static/_colours.scss new file mode 100644 index 0000000..c62fb62 --- /dev/null +++ b/backseat/components/assets/static/_colours.scss @@ -0,0 +1,244 @@ +$red-50: oklch(0.971 0.013 17.38); +$red-100: oklch(0.936 0.032 17.717); +$red-200: oklch(0.885 0.062 18.334); +$red-300: oklch(0.808 0.114 19.571); +$red-400: oklch(0.704 0.191 22.216); +$red-500: oklch(0.637 0.237 25.331); +$red-600: oklch(0.577 0.245 27.325); +$red-700: oklch(0.505 0.213 27.518); +$red-800: oklch(0.444 0.177 26.899); +$red-900: oklch(0.396 0.141 25.723); +$red-950: oklch(0.258 0.092 26.042); +$orange-50: oklch(0.98 0.016 73.684); +$orange-100: oklch(0.954 0.038 75.164); +$orange-200: oklch(0.901 0.076 70.697); +$orange-300: oklch(0.837 0.128 66.29); +$orange-400: oklch(0.75 0.183 55.934); +$orange-500: oklch(0.705 0.213 47.604); +$orange-600: oklch(0.646 0.222 41.116); +$orange-700: oklch(0.553 0.195 38.402); +$orange-800: oklch(0.47 0.157 37.304); +$orange-900: oklch(0.408 0.123 38.172); +$orange-950: oklch(0.266 0.079 36.259); +$amber-50: oklch(0.987 0.022 95.277); +$amber-100: oklch(0.962 0.059 95.617); +$amber-200: oklch(0.924 0.12 95.746); +$amber-300: oklch(0.879 0.169 91.605); +$amber-400: oklch(0.828 0.189 84.429); +$amber-500: oklch(0.769 0.188 70.08); +$amber-600: oklch(0.666 0.179 58.318); +$amber-700: oklch(0.555 0.163 48.998); +$amber-800: oklch(0.473 0.137 46.201); +$amber-900: oklch(0.414 0.112 45.904); +$amber-950: oklch(0.279 0.077 45.635); +$yellow-50: oklch(0.987 0.026 102.212); +$yellow-100: oklch(0.973 0.071 103.193); +$yellow-200: oklch(0.945 0.129 101.54); +$yellow-300: oklch(0.905 0.182 98.111); +$yellow-400: oklch(0.852 0.199 91.936); +$yellow-500: oklch(0.795 0.184 86.047); +$yellow-600: oklch(0.681 0.162 75.834); +$yellow-700: oklch(0.554 0.135 66.442); +$yellow-800: oklch(0.476 0.114 61.907); +$yellow-900: oklch(0.421 0.095 57.708); +$yellow-950: oklch(0.286 0.066 53.813); +$lime-50: oklch(0.986 0.031 120.757); +$lime-100: oklch(0.967 0.067 122.328); +$lime-200: oklch(0.938 0.127 124.321); +$lime-300: oklch(0.897 0.196 126.665); +$lime-400: oklch(0.841 0.238 128.85); +$lime-500: oklch(0.768 0.233 130.85); +$lime-600: oklch(0.648 0.2 131.684); +$lime-700: oklch(0.532 0.157 131.589); +$lime-800: oklch(0.453 0.124 130.933); +$lime-900: oklch(0.405 0.101 131.063); +$lime-950: oklch(0.274 0.072 132.109); +$green-50: oklch(0.982 0.018 155.826); +$green-100: oklch(0.962 0.044 156.743); +$green-200: oklch(0.925 0.084 155.995); +$green-300: oklch(0.871 0.15 154.449); +$green-400: oklch(0.792 0.209 151.711); +$green-500: oklch(0.723 0.219 149.579); +$green-600: oklch(0.627 0.194 149.214); +$green-700: oklch(0.527 0.154 150.069); +$green-800: oklch(0.448 0.119 151.328); +$green-900: oklch(0.393 0.095 152.535); +$green-950: oklch(0.266 0.065 152.934); +$emerald-50: oklch(0.979 0.021 166.113); +$emerald-100: oklch(0.95 0.052 163.051); +$emerald-200: oklch(0.905 0.093 164.15); +$emerald-300: oklch(0.845 0.143 164.978); +$emerald-400: oklch(0.765 0.177 163.223); +$emerald-500: oklch(0.696 0.17 162.48); +$emerald-600: oklch(0.596 0.145 163.225); +$emerald-700: oklch(0.508 0.118 165.612); +$emerald-800: oklch(0.432 0.095 166.913); +$emerald-900: oklch(0.378 0.077 168.94); +$emerald-950: oklch(0.262 0.051 172.552); +$teal-50: oklch(0.984 0.014 180.72); +$teal-100: oklch(0.953 0.051 180.801); +$teal-200: oklch(0.91 0.096 180.426); +$teal-300: oklch(0.855 0.138 181.071); +$teal-400: oklch(0.777 0.152 181.912); +$teal-500: oklch(0.704 0.14 182.503); +$teal-600: oklch(0.6 0.118 184.704); +$teal-700: oklch(0.511 0.096 186.391); +$teal-800: oklch(0.437 0.078 188.216); +$teal-900: oklch(0.386 0.063 188.416); +$teal-950: oklch(0.277 0.046 192.524); +$cyan-50: oklch(0.984 0.019 200.873); +$cyan-100: oklch(0.956 0.045 203.388); +$cyan-200: oklch(0.917 0.08 205.041); +$cyan-300: oklch(0.865 0.127 207.078); +$cyan-400: oklch(0.789 0.154 211.53); +$cyan-500: oklch(0.715 0.143 215.221); +$cyan-600: oklch(0.609 0.126 221.723); +$cyan-700: oklch(0.52 0.105 223.128); +$cyan-800: oklch(0.45 0.085 224.283); +$cyan-900: oklch(0.398 0.07 227.392); +$cyan-950: oklch(0.302 0.056 229.695); +$sky-50: oklch(0.977 0.013 236.62); +$sky-100: oklch(0.951 0.026 236.824); +$sky-200: oklch(0.901 0.058 230.902); +$sky-300: oklch(0.828 0.111 230.318); +$sky-400: oklch(0.746 0.16 232.661); +$sky-500: oklch(0.685 0.169 237.323); +$sky-600: oklch(0.588 0.158 241.966); +$sky-700: oklch(0.5 0.134 242.749); +$sky-800: oklch(0.443 0.11 240.79); +$sky-900: oklch(0.391 0.09 240.876); +$sky-950: oklch(0.293 0.066 243.157); +$blue-50: oklch(0.97 0.014 254.604); +$blue-100: oklch(0.932 0.032 255.585); +$blue-200: oklch(0.882 0.059 254.128); +$blue-300: oklch(0.809 0.105 251.813); +$blue-400: oklch(0.707 0.165 254.624); +$blue-500: oklch(0.623 0.214 259.815); +$blue-600: oklch(0.546 0.245 262.881); +$blue-700: oklch(0.488 0.243 264.376); +$blue-800: oklch(0.424 0.199 265.638); +$blue-900: oklch(0.379 0.146 265.522); +$blue-950: oklch(0.282 0.091 267.935); +$indigo-50: oklch(0.962 0.018 272.314); +$indigo-100: oklch(0.93 0.034 272.788); +$indigo-200: oklch(0.87 0.065 274.039); +$indigo-300: oklch(0.785 0.115 274.713); +$indigo-400: oklch(0.673 0.182 276.935); +$indigo-500: oklch(0.585 0.233 277.117); +$indigo-600: oklch(0.511 0.262 276.966); +$indigo-700: oklch(0.457 0.24 277.023); +$indigo-800: oklch(0.398 0.195 277.366); +$indigo-900: oklch(0.359 0.144 278.697); +$indigo-950: oklch(0.257 0.09 281.288); +$violet-50: oklch(0.969 0.016 293.756); +$violet-100: oklch(0.943 0.029 294.588); +$violet-200: oklch(0.894 0.057 293.283); +$violet-300: oklch(0.811 0.111 293.571); +$violet-400: oklch(0.702 0.183 293.541); +$violet-500: oklch(0.606 0.25 292.717); +$violet-600: oklch(0.541 0.281 293.009); +$violet-700: oklch(0.491 0.27 292.581); +$violet-800: oklch(0.432 0.232 292.759); +$violet-900: oklch(0.38 0.189 293.745); +$violet-950: oklch(0.283 0.141 291.089); +$purple-50: oklch(0.977 0.014 308.299); +$purple-100: oklch(0.946 0.033 307.174); +$purple-200: oklch(0.902 0.063 306.703); +$purple-300: oklch(0.827 0.119 306.383); +$purple-400: oklch(0.714 0.203 305.504); +$purple-500: oklch(0.627 0.265 303.9); +$purple-600: oklch(0.558 0.288 302.321); +$purple-700: oklch(0.496 0.265 301.924); +$purple-800: oklch(0.438 0.218 303.724); +$purple-900: oklch(0.381 0.176 304.987); +$purple-950: oklch(0.291 0.149 302.717); +$fuchsia-50: oklch(0.977 0.017 320.058); +$fuchsia-100: oklch(0.952 0.037 318.852); +$fuchsia-200: oklch(0.903 0.076 319.62); +$fuchsia-300: oklch(0.833 0.145 321.434); +$fuchsia-400: oklch(0.74 0.238 322.16); +$fuchsia-500: oklch(0.667 0.295 322.15); +$fuchsia-600: oklch(0.591 0.293 322.896); +$fuchsia-700: oklch(0.518 0.253 323.949); +$fuchsia-800: oklch(0.452 0.211 324.591); +$fuchsia-900: oklch(0.401 0.17 325.612); +$fuchsia-950: oklch(0.293 0.136 325.661); +$pink-50: oklch(0.971 0.014 343.198); +$pink-100: oklch(0.948 0.028 342.258); +$pink-200: oklch(0.899 0.061 343.231); +$pink-300: oklch(0.823 0.12 346.018); +$pink-400: oklch(0.718 0.202 349.761); +$pink-500: oklch(0.656 0.241 354.308); +$pink-600: oklch(0.592 0.249 0.584); +$pink-700: oklch(0.525 0.223 3.958); +$pink-800: oklch(0.459 0.187 3.815); +$pink-900: oklch(0.408 0.153 2.432); +$pink-950: oklch(0.284 0.109 3.907); +$rose-50: oklch(0.969 0.015 12.422); +$rose-100: oklch(0.941 0.03 12.58); +$rose-200: oklch(0.892 0.058 10.001); +$rose-300: oklch(0.81 0.117 11.638); +$rose-400: oklch(0.712 0.194 13.428); +$rose-500: oklch(0.645 0.246 16.439); +$rose-600: oklch(0.586 0.253 17.585); +$rose-700: oklch(0.514 0.222 16.935); +$rose-800: oklch(0.455 0.188 13.697); +$rose-900: oklch(0.41 0.159 10.272); +$rose-950: oklch(0.271 0.105 12.094); +$slate-50: oklch(0.984 0.003 247.858); +$slate-100: oklch(0.968 0.007 247.896); +$slate-200: oklch(0.929 0.013 255.508); +$slate-300: oklch(0.869 0.022 252.894); +$slate-400: oklch(0.704 0.04 256.788); +$slate-500: oklch(0.554 0.046 257.417); +$slate-600: oklch(0.446 0.043 257.281); +$slate-700: oklch(0.372 0.044 257.287); +$slate-800: oklch(0.279 0.041 260.031); +$slate-900: oklch(0.208 0.042 265.755); +$slate-950: oklch(0.129 0.042 264.695); +$gray-50: oklch(0.985 0.002 247.839); +$gray-100: oklch(0.967 0.003 264.542); +$gray-200: oklch(0.928 0.006 264.531); +$gray-300: oklch(0.872 0.01 258.338); +$gray-400: oklch(0.707 0.022 261.325); +$gray-500: oklch(0.551 0.027 264.364); +$gray-600: oklch(0.446 0.03 256.802); +$gray-700: oklch(0.373 0.034 259.733); +$gray-800: oklch(0.278 0.033 256.848); +$gray-900: oklch(0.21 0.034 264.665); +$gray-950: oklch(0.13 0.028 261.692); +$zinc-50: oklch(0.985 0 0); +$zinc-100: oklch(0.967 0.001 286.375); +$zinc-200: oklch(0.92 0.004 286.32); +$zinc-300: oklch(0.871 0.006 286.286); +$zinc-400: oklch(0.705 0.015 286.067); +$zinc-500: oklch(0.552 0.016 285.938); +$zinc-600: oklch(0.442 0.017 285.786); +$zinc-700: oklch(0.37 0.013 285.805); +$zinc-800: oklch(0.274 0.006 286.033); +$zinc-900: oklch(0.21 0.006 285.885); +$zinc-950: oklch(0.141 0.005 285.823); +$neutral-50: oklch(0.985 0 0); +$neutral-100: oklch(0.97 0 0); +$neutral-200: oklch(0.922 0 0); +$neutral-300: oklch(0.87 0 0); +$neutral-400: oklch(0.708 0 0); +$neutral-500: oklch(0.556 0 0); +$neutral-600: oklch(0.439 0 0); +$neutral-700: oklch(0.371 0 0); +$neutral-800: oklch(0.269 0 0); +$neutral-900: oklch(0.205 0 0); +$neutral-950: oklch(0.145 0 0); +$stone-50: oklch(0.985 0.001 106.423); +$stone-100: oklch(0.97 0.001 106.424); +$stone-200: oklch(0.923 0.003 48.717); +$stone-300: oklch(0.869 0.005 56.366); +$stone-400: oklch(0.709 0.01 56.259); +$stone-500: oklch(0.553 0.013 58.071); +$stone-600: oklch(0.444 0.011 73.639); +$stone-700: oklch(0.374 0.01 67.558); +$stone-800: oklch(0.268 0.007 34.298); +$stone-900: oklch(0.216 0.006 56.043); +$stone-950: oklch(0.147 0.004 49.25); +$black: #000; +$white: #fff; \ No newline at end of file diff --git a/backseat/components/assets/static/main.css b/backseat/components/assets/static/main.css index 5ac37cf..80b279a 100644 --- a/backseat/components/assets/static/main.css +++ b/backseat/components/assets/static/main.css @@ -2,7 +2,7 @@ html, body { width: 100%; min-height: 100%; - background-color: lightblue; + background-color: oklch(88.2% 0.059 254.128deg); font-family: sans-serif; } @@ -24,7 +24,7 @@ ul.track-list { } ul.track-list li { display: grid; - grid-template-columns: 1fr repeat(3, 3fr); + grid-template-columns: 1fr 1fr repeat(3, 4fr); align-items: center; gap: 0.5em; padding: 0.5em; @@ -38,13 +38,33 @@ ul.track-list li > div:not(.display-none):has(img) img { height: 3em; } ul.track-list li:nth-of-type(odd) { - background-color: lightgray; + background-color: oklch(92.8% 0.006 264.531deg); } ul.track-list li:has(> input[type=checkbox]:checked) { - background-color: yellow; + background-color: oklch(87.1% 0.15 154.449deg); } ul.track-list li:has(> input[type=checkbox]:checked):nth-of-type(odd) { - background-color: yellowgreen; + background-color: oklch(92.5% 0.084 155.995deg); +} + +.loader { + width: 40px; + height: 40px; + border: 4px solid oklch(37.3% 0.034 259.733deg); + border-bottom-color: transparent; + border-radius: 50%; + display: inline-block; + box-sizing: border-box; + animation: rotation 0.8s linear infinite; +} + +@keyframes rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } } /*# sourceMappingURL=main.css.map */ diff --git a/backseat/components/assets/static/main.css.map b/backseat/components/assets/static/main.css.map index 7053847..3d9caed 100644 --- a/backseat/components/assets/static/main.css.map +++ b/backseat/components/assets/static/main.css.map @@ -1 +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"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["main.scss","_colours.scss"],"names":[],"mappings":"AAAA;AAGA;EACI;EACA;EACA,kBC0GO;EDzGP;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;AAEA;EACI;EACA;EAEA;EACA;EACA;;AAEA;EAII;EACA;EACA;;AALA;EACI;;AAOR;EACI,kBC4JD;;ADzJH;EACI,kBCUA;;ADTA;EACI,kBCOJ;;;ADDZ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;IACI;;EAEJ;IACI","file":"main.css"} \ No newline at end of file diff --git a/backseat/components/assets/static/main.scss b/backseat/components/assets/static/main.scss index 9ba1816..8514344 100644 --- a/backseat/components/assets/static/main.scss +++ b/backseat/components/assets/static/main.scss @@ -1,9 +1,10 @@ /* hello */ +@import "_colours"; html, body { width: 100%; min-height: 100%; - background-color: lightblue; + background-color: $blue-200; font-family: sans-serif; } @@ -25,10 +26,8 @@ ul.track-list { li { display: grid; - grid-template-columns: 1fr repeat(3, 3fr); + grid-template-columns: 1fr 1fr repeat(3, 4fr); - //display: flex; - //flex-direction: row; align-items: center; gap: 0.5em; padding: 0.5em; @@ -43,14 +42,34 @@ ul.track-list { } &:nth-of-type(odd) { - background-color: lightgray; + background-color: $gray-200; } &:has(>input[type=checkbox]:checked) { - background-color: yellow; + background-color: $green-300; &:nth-of-type(odd) { - background-color: yellowgreen; + background-color: $green-200; } } } +} + +.loader { + width: 40px; + height: 40px; + border: 4px solid $gray-700; + border-bottom-color: transparent; + border-radius: 50%; + display: inline-block; + box-sizing: border-box; + animation: rotation 0.8s linear infinite; +} + +@keyframes rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } } \ No newline at end of file diff --git a/backseat/components/recentlyListened/index.templ b/backseat/components/recentlyListened/index.templ index 44c2833..3cce0ad 100644 --- a/backseat/components/recentlyListened/index.templ +++ b/backseat/components/recentlyListened/index.templ @@ -3,6 +3,7 @@ package recentlyListened import ( "git.akpain.net/codemicro/backseat-music/backseat/httpUtil" "github.com/zmb3/spotify/v2" + "strconv" ) func formatArtistNames(artists []spotify.SimpleArtist) string { @@ -30,27 +31,59 @@ func getSmallestImage(images []spotify.Image) spotify.Image { return *smallest } -templ indexPage(recentlyListenedTracks []spotify.RecentlyPlayedItem) { +templ indexPage() { @httpUtil.BasePage("Recently Listened"){ - +

Recently Listened Tracks

+ +
+ + +
+
+ } +} + +templ trackList(recentlyListenedTracks []spotify.RecentlyPlayedItem) { + for _, record := range recentlyListenedTracks { +
  • + + {{ smallestImage := getSmallestImage(record.Track.Album.Images) }} +
    {
    +
    { record.Track.Name }
    +
    { formatArtistNames(record.Track.Artists) }
    +
    { record.PlayedAt.String() }
    +
  • + } + +
    + if len(recentlyListenedTracks) != 0 { +
    + @httpUtil.LoadingSpinner() +
    + + } +
    +} + +templ createPlaylistPage(trackIDs []string) { + @httpUtil.BasePage("Recently Listened"){ +

    Create Playlist

    + + } } \ No newline at end of file diff --git a/backseat/components/recentlyListened/index_templ.go b/backseat/components/recentlyListened/index_templ.go index c208ef6..68d9ef3 100644 --- a/backseat/components/recentlyListened/index_templ.go +++ b/backseat/components/recentlyListened/index_templ.go @@ -11,6 +11,7 @@ import templruntime "github.com/a-h/templ/runtime" import ( "git.akpain.net/codemicro/backseat-music/backseat/httpUtil" "github.com/zmb3/spotify/v2" + "strconv" ) func formatArtistNames(artists []spotify.SimpleArtist) string { @@ -38,7 +39,7 @@ func getSmallestImage(images []spotify.Image) spotify.Image { return *smallest } -func indexPage(recentlyListenedTracks []spotify.RecentlyPlayedItem) templ.Component { +func indexPage() 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 if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -71,100 +72,15 @@ func indexPage(recentlyListenedTracks []spotify.RecentlyPlayedItem) templ.Compon }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -178,4 +94,223 @@ func indexPage(recentlyListenedTracks []spotify.RecentlyPlayedItem) templ.Compon }) } +func trackList(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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templ.GetChildren(ctx) + if templ_7745c5c3_Var3 == nil { + templ_7745c5c3_Var3 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + for _, record := range recentlyListenedTracks { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
  • ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + smallestImage := getSmallestImage(record.Track.Album.Images) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "
    \"")
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, 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: 63, Col: 36} + } + _, 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, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, 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: 64, Col: 58} + } + _, 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, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var9 string + templ_7745c5c3_Var9, 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: 65, Col: 43} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
  • ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(recentlyListenedTracks) != 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = httpUtil.LoadingSpinner().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func createPlaylistPage(trackIDs []string) 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var11 := templ.GetChildren(ctx) + if templ_7745c5c3_Var11 == nil { + templ_7745c5c3_Var11 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var12 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "

    Create Playlist

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) + templ_7745c5c3_Err = httpUtil.BasePage("Recently Listened").Render(templ.WithChildren(ctx, templ_7745c5c3_Var12), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + var _ = templruntime.GeneratedTemplate diff --git a/backseat/components/recentlyListened/recentlyListened.go b/backseat/components/recentlyListened/recentlyListened.go index 265147f..32da5a2 100644 --- a/backseat/components/recentlyListened/recentlyListened.go +++ b/backseat/components/recentlyListened/recentlyListened.go @@ -7,6 +7,8 @@ import ( "git.akpain.net/codemicro/backseat-music/backseat/spotifyUtil" "github.com/zmb3/spotify/v2" "net/http" + "strconv" + "strings" ) type RecentlyListened struct { @@ -23,6 +25,9 @@ func New(data *data.Store) *RecentlyListened { } t.mux.Handle("GET /recentlyListened/{$}", httpUtil.HandleErrors(t.index)) + t.mux.Handle("POST /recentlyListened/{$}", httpUtil.HandleErrors(t.index)) + t.mux.Handle("GET /recentlyListened/tracks", httpUtil.HandleErrors(t.tracks)) + t.mux.Handle("PUT /recentlyListened/createPlaylist", httpUtil.HandleErrors(t.createPlaylist)) return t } @@ -37,19 +42,64 @@ func (r *RecentlyListened) index(rw http.ResponseWriter, rq *http.Request) error return nil } - spotifyClient, err := spotifyUtil.NewSpotityClient(rq.Context(), r.data) - if err != nil { - return fmt.Errorf("get Spotify client: %w", err) - } - - recentlyPlayed, err := spotifyClient.PlayerRecentlyPlayedOpt(rq.Context(), &spotify.RecentlyPlayedOptions{Limit: 50}) - if err != nil { - return fmt.Errorf("get recently played tracks: %w", err) - } - rw.Header().Set("Content-Type", "text/html") - if err := indexPage(recentlyPlayed).Render(rq.Context(), rw); err != nil { + if err := indexPage().Render(rq.Context(), rw); err != nil { + return fmt.Errorf("render template: %w", err) + } + + return nil +} + +func (r *RecentlyListened) tracks(rw http.ResponseWriter, rq *http.Request) error { + spotifyClient, err := spotifyUtil.NewSpotityClient(rq.Context(), r.data) + if err != nil { + return fmt.Errorf("get Spotify client: %w", err) + } + + recentlyPlayedOpts := &spotify.RecentlyPlayedOptions{Limit: 20} + + if beforeStr := rq.URL.Query().Get("before"); beforeStr != "" { + before, err := strconv.Atoi(beforeStr) + if err == nil { + recentlyPlayedOpts.BeforeEpochMs = int64(before) + } + } + + recentlyPlayed, err := spotifyClient.PlayerRecentlyPlayedOpt(rq.Context(), recentlyPlayedOpts) + if err != nil { + return fmt.Errorf("get recently played tracks: %w", err) + } + + { + n := 0 + seen := make(map[spotify.ID]struct{}) + for _, track := range recentlyPlayed { + if _, found := seen[track.Track.ID]; !found { + recentlyPlayed[n] = track + n += 1 + seen[track.Track.ID] = struct{}{} + } + } + recentlyPlayed = recentlyPlayed[:n] + } + + if err := trackList(recentlyPlayed).Render(rq.Context(), rw); err != nil { + return fmt.Errorf("render template: %w", err) + } + + return nil +} + +func (r *RecentlyListened) createPlaylist(rw http.ResponseWriter, rq *http.Request) error { + if err := rq.ParseForm(); err != nil { + rw.WriteHeader(http.StatusBadRequest) + return nil + } + + trackIDs := strings.Split(rq.FormValue("selected"), ",") + + if err := createPlaylistPage(trackIDs).Render(rq.Context(), rw); err != nil { return fmt.Errorf("render template: %w", err) } diff --git a/backseat/httpUtil/basePage.templ b/backseat/httpUtil/basePage.templ index c5ff363..de779d2 100644 --- a/backseat/httpUtil/basePage.templ +++ b/backseat/httpUtil/basePage.templ @@ -7,6 +7,17 @@ templ BasePage(title string) { { title } - Backseat Music + + +
    @@ -14,4 +25,8 @@ templ BasePage(title string) {
    +} + +templ LoadingSpinner() { + } \ No newline at end of file diff --git a/backseat/httpUtil/basePage_templ.go b/backseat/httpUtil/basePage_templ.go index daf2147..f0fca1c 100644 --- a/backseat/httpUtil/basePage_templ.go +++ b/backseat/httpUtil/basePage_templ.go @@ -42,7 +42,7 @@ func BasePage(title string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " - Backseat Music
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " - Backseat Music
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -58,4 +58,33 @@ func BasePage(title string) templ.Component { }) } +func LoadingSpinner() 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templ.GetChildren(ctx) + if templ_7745c5c3_Var3 == nil { + templ_7745c5c3_Var3 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + var _ = templruntime.GeneratedTemplate