From 44a751e08c2360bb1d42f3a6bacf152cdabc2220 Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 10:53:12 +0300 Subject: [PATCH 01/27] chore(ytscrape): docs --- app/services/youtube/search/search_scraper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/youtube/search/search_scraper.go b/app/services/youtube/search/search_scraper.go index 62a9022..ea9199b 100644 --- a/app/services/youtube/search/search_scraper.go +++ b/app/services/youtube/search/search_scraper.go @@ -134,7 +134,7 @@ func search(q string) ([]videoResult, error) { return resSuka, nil } -// ScraperSearch is a scrapper enabled YouTube search, using the search service under ~/ytscraper +// ScraperSearch is a scrapper enabled YouTube search. type ScraperSearch struct{} func (y *ScraperSearch) Search(query string) (results []entities.Song, err error) { From 6ccaa3219209f3b58d4fc20c03a67d08a3cb26ba Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 12:42:05 +0300 Subject: [PATCH 02/27] chore(ytscrape): renameings --- app/services/youtube/search/search_scraper.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/services/youtube/search/search_scraper.go b/app/services/youtube/search/search_scraper.go index ea9199b..3c004cd 100644 --- a/app/services/youtube/search/search_scraper.go +++ b/app/services/youtube/search/search_scraper.go @@ -12,9 +12,9 @@ import ( ) var ( - pat0 = regexp.MustCompile(`"innertubeApiKey":"([^"]*)`) - pat = regexp.MustCompile(`ytInitialData[^{]*(.*?);\s*<\/script>`) - pat2 = regexp.MustCompile(`ytInitialData"[^{]*(.*);\s*window\["ytInitialPlayerResponse"\]`) + keyPattern = regexp.MustCompile(`"innertubeApiKey":"([^"]*)`) + dataPattern = regexp.MustCompile(`ytInitialData[^{]*(.*?);\s*<\/script>`) + dataPattern2 = regexp.MustCompile(`ytInitialData"[^{]*(.*);\s*window\["ytInitialPlayerResponse"\]`) ) type videoResult struct { @@ -95,15 +95,15 @@ func search(q string) ([]videoResult, error) { Parser: "json_format", Key: "", } - key := pat0.FindSubmatch(respBody) + key := keyPattern.FindSubmatch(respBody) jojo.Key = string(key[1]) - matches := pat.FindSubmatch(respBody) + matches := dataPattern.FindSubmatch(respBody) if len(matches) > 1 { jojo.Parser += ".object_var" } else { jojo.Parser += ".original" - matches = pat2.FindSubmatch(respBody) + matches = dataPattern2.FindSubmatch(respBody) } data := ytSearchData{} err = json.Unmarshal(matches[1], &data) From f6ea89d7c4a7536f69c2916c12a918903d54eb9f Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 13:04:12 +0300 Subject: [PATCH 03/27] feat(player): add play playlist function --- app/static/js/player.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/static/js/player.js b/app/static/js/player.js index 642dbd1..41609d7 100644 --- a/app/static/js/player.js +++ b/app/static/js/player.js @@ -645,6 +645,23 @@ async function playSingleSongNext(song) { alert(`Playing ${song.title} next!`); } +/** + * @param {Playlist} playlist + */ +async function playPlaylistNext(playlist) { + if (!playlist || !playlist.songs || playlist.songs.length === 0) { + alert("Can't do that!"); + return; + } + if (playerState.playlist.songs.length === 0) { + playSongFromPlaylist(playlist.songs[0].yt_id, playlist); + return; + } + playerState.playlist.songs.push(...playlist.songs); + playerState.playlist.title = `${playerState.playlist.title} + ${playlist.title}`; + alert(`Playing ${playlist.title} next!`); +} + /** * @param {string} songYtId * @param {Playlist} playlist @@ -917,6 +934,7 @@ window.Player.hidePlayer = hide; window.Player.playSingleSong = playSingleSong; window.Player.playSingleSongNext = playSingleSongNext; window.Player.playSongFromPlaylist = playSongFromPlaylist; +window.Player.playPlaylistNext = playPlaylistNext; window.Player.removeSongFromPlaylist = removeSongFromPlaylist; window.Player.addSongToQueue = appendSongToCurrentQueue; window.Player.stopMuzikk = stopMuzikk; From edcdad55feac891cb11d6436aa193e865392324a Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 13:31:42 +0300 Subject: [PATCH 04/27] chore(playlist): update view --- app/views/pages/playlist.templ | 2 +- app/views/pages/playlists.templ | 31 +++++++++++++++++-------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/app/views/pages/playlist.templ b/app/views/pages/playlist.templ index 8029602..4e26865 100644 --- a/app/views/pages/playlist.templ +++ b/app/views/pages/playlist.templ @@ -31,7 +31,7 @@ templ playlistHeader(pl entities.Playlist) { @icons.ShareLink() if perm, ok := ctx.Value("playlist-permission").(models.PlaylistPermissions); ok && (perm & models.OwnerPermission) != 0 { - @playlist.PlaylistsOptionsPopover(pl) + @playlist.PlaylistsOptions(pl) } else { @joinLeavePlaylist(pl.PublicId, perm) } diff --git a/app/views/pages/playlists.templ b/app/views/pages/playlists.templ index 78413b8..69ab5e9 100644 --- a/app/views/pages/playlists.templ +++ b/app/views/pages/playlists.templ @@ -6,6 +6,7 @@ import ( "fmt" "dankmuzikk/views/components/navlink" "dankmuzikk/views/components/page" + "dankmuzikk/views/icons" ) templ Playlists(pls []entities.Playlist) { @@ -33,12 +34,11 @@ templ JustPlaylists(pls []entities.Playlist) {
for _, playlist := range pls { - @navlink.JustLink(fmt.Sprintf("/playlist/%s", playlist.PublicId), playlist.Title, singlePlaylist(playlist)) + @singlePlaylist(playlist) }
} @@ -46,19 +46,22 @@ templ JustPlaylists(pls []entities.Playlist) { templ singlePlaylist(pl entities.Playlist) {
- - - - - - -
-

{ pl.Title }

-

{ fmt.Sprint(pl.SongsCount) } Songs

+ @navlink.JustLink(fmt.Sprintf("/playlist/%s", pl.PublicId), pl.Title, singlePlaylistNav(pl)) +
+ @playlist.PlaylistsOptions(pl)
} + +templ singlePlaylistNav(pl entities.Playlist) { +
+ @icons.Playlist() +

{ pl.Title }

+ +

{ fmt.Sprint(pl.SongsCount) } Songs

+
+} From 5c5df78f5aa696de8595887faa6c14fa844d7274 Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 13:32:04 +0300 Subject: [PATCH 05/27] chore(playlist): update options --- app/views/components/playlist/options.templ | 65 ++++++++++++++++++--- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/app/views/components/playlist/options.templ b/app/views/components/playlist/options.templ index b752a24..a77214a 100644 --- a/app/views/components/playlist/options.templ +++ b/app/views/components/playlist/options.templ @@ -8,19 +8,50 @@ import ( "dankmuzikk/views/icons" ) -templ PlaylistsOptionsPopover(playlist entities.Playlist) { - @menus.Popover("playlist-"+playlist.PublicId, "Playlist options", icons.Options(), playlistOptions(playlist)) +templ PlaylistsOptions(playlist entities.Playlist) { + if isMobile, ok := ctx.Value("is-mobile").(bool); ok && isMobile { + @menus.MobileMenu("playlist-"+playlist.PublicId, "Playlist's options", icons.Options(), playlistOptions(playlist)) + } else { + @menus.Popover("playlist-"+playlist.PublicId, "Playlist's options", icons.Options(), playlistOptions(playlist)) + } } templ playlistOptions(playlist entities.Playlist) {
-

Options

+

Playlist's Options

+
@publicPlaylistToggle(playlist.PublicId, playlist.IsPublic) + if len(playlist.Songs) != 0 { + + } + if perm, ok := ctx.Value("playlist-permission").(models.PlaylistPermissions); ok && (perm & models.JoinerPermission) != 0 {
-

- Public +

+ Sharable

} + +script playPlaylistNext(pl entities.Playlist) { + Player.playPlaylistNext(pl) +} + +script copyLink(isPublic bool, plPubId string) { + window.Utils.copyTextToClipboard(`${location.protocol}//${location.host}/playlist/${plPubId}`) + if (isPublic) { + alert("Playlist's links was copied!"); + } else { + alert("Playlist's links was copied!\nMake sure to make it public before sharing the link 😁") + } +} From 1548c643ac8723ef1ec519489922d1f7d3748a6b Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 13:32:49 +0300 Subject: [PATCH 06/27] chore(mobilemenu): remove console log --- app/views/components/menus/mobile_menu.templ | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/components/menus/mobile_menu.templ b/app/views/components/menus/mobile_menu.templ index df79df4..4cfa1ba 100644 --- a/app/views/components/menus/mobile_menu.templ +++ b/app/views/components/menus/mobile_menu.templ @@ -52,7 +52,6 @@ script toggleMobileMenu(id string) { popover.classList.toggle("mobile-menu-exapnded"); const player = document.getElementById("ze-player"); - console.log(player.getBoundingClientRect()) const rect = popover.getBoundingClientRect(); popover.style.bottom = `-${ From 054014ce459e21ca54a1496ceabdfd51a461904b Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 13:33:07 +0300 Subject: [PATCH 07/27] chore(song): update menu --- app/views/components/song/mobile_details.templ | 4 ++-- app/views/components/song/song.templ | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/views/components/song/mobile_details.templ b/app/views/components/song/mobile_details.templ index c649477..9be7681 100644 --- a/app/views/components/song/mobile_details.templ +++ b/app/views/components/song/mobile_details.templ @@ -5,9 +5,9 @@ import "fmt" templ DetailsMobile(song entities.Song) {
-

Details and options

+

Details

Added on { song.AddedAt }

if song.PlayTimes == 1 {

Played once

diff --git a/app/views/components/song/song.templ b/app/views/components/song/song.templ index 30da0e6..24c0d41 100644 --- a/app/views/components/song/song.templ +++ b/app/views/components/song/song.templ @@ -91,10 +91,12 @@ templ Song(s entities.Song, additionalData []string, additionalOptions []templ.C templ options(song entities.Song, additionalOptions []templ.Component) {
+

Song's Options

+
for _, option := range additionalOptions { @option } From e9c2972855f1b301b40cf33e218530b21d00ed1a Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 13:48:33 +0300 Subject: [PATCH 08/27] fix(mobilemenu): exapand transition --- app/views/components/menus/mobile_menu.templ | 42 +++++++++++--------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/app/views/components/menus/mobile_menu.templ b/app/views/components/menus/mobile_menu.templ index 4cfa1ba..958dee0 100644 --- a/app/views/components/menus/mobile_menu.templ +++ b/app/views/components/menus/mobile_menu.templ @@ -39,30 +39,36 @@ templ MobileMenu(id, title string, button, child templ.Component) { .mobile-menu-collapsed { height: 0; max-height: 0; - } - .mobile-menu-exapnded { - min-height: 200px; - max-height: 500px; + min-height: 0; } } script toggleMobileMenu(id string) { const popover = document.getElementById(`mobile-menu-${id}`); - popover.classList.toggle("mobile-menu-exapnded"); + if (popover.classList.contains("mobile-menu-collapsed")) { + const player = document.getElementById("ze-player"); + const rect = popover.getBoundingClientRect(); + popover.style.bottom = `-${ + (window.innerHeight-(rect.y+rect.height)) - + 65 - + (!player.classList.contains("hidden")? + player.getBoundingClientRect().height + 5: + 0) + }px` + popover.style.height = ( + popover.children[0].getBoundingClientRect().height + 4 + + popover.children[1].getBoundingClientRect().height + ).toString() + "px" ; + popover.style.maxHeight = "500px"; + popover.style.minHeight = "200px"; - const player = document.getElementById("ze-player"); + popover.classList.remove("mobile-menu-collapsed"); + } else { + popover.classList.add("mobile-menu-collapsed"); + popover.style.height = 0; + popover.style.maxHeight = 0; + popover.style.minHeight = 0; + } - const rect = popover.getBoundingClientRect(); - popover.style.bottom = `-${ - (window.innerHeight-(rect.y+rect.height)) - - 65 - - (!player.classList.contains("hidden")? - player.getBoundingClientRect().height + 5: - 0) - }px` - popover.style.height = ( - popover.children[0].getBoundingClientRect().height + 4 + - popover.children[1].getBoundingClientRect().height - ).toString() + "px" ; } From 4238880894b90a0feb7498f465188c412af2b25f Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 14:18:52 +0300 Subject: [PATCH 09/27] chore(playlist): remove outer playlist's menu --- app/views/pages/playlists.templ | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/app/views/pages/playlists.templ b/app/views/pages/playlists.templ index 69ab5e9..935ed58 100644 --- a/app/views/pages/playlists.templ +++ b/app/views/pages/playlists.templ @@ -38,7 +38,7 @@ templ JustPlaylists(pls []entities.Playlist) { } > for _, playlist := range pls { - @singlePlaylist(playlist) + @navlink.JustLink(fmt.Sprintf("/playlist/%s", playlist.PublicId), playlist.Title, singlePlaylist(playlist)) }
} @@ -46,22 +46,14 @@ templ JustPlaylists(pls []entities.Playlist) { templ singlePlaylist(pl entities.Playlist) {
- @navlink.JustLink(fmt.Sprintf("/playlist/%s", pl.PublicId), pl.Title, singlePlaylistNav(pl)) -
- @playlist.PlaylistsOptions(pl) +
+ @icons.Playlist() +

{ pl.Title }

-
-} - -templ singlePlaylistNav(pl entities.Playlist) { -
- @icons.Playlist() -

{ pl.Title }

-

{ fmt.Sprint(pl.SongsCount) } Songs

} From f9950019124a9afee33c5064dd65a5ebe365a3d0 Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 14:23:13 +0300 Subject: [PATCH 10/27] chore(playlist): spacing --- app/views/pages/playlists.templ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/pages/playlists.templ b/app/views/pages/playlists.templ index 935ed58..bf0fc19 100644 --- a/app/views/pages/playlists.templ +++ b/app/views/pages/playlists.templ @@ -52,8 +52,8 @@ templ singlePlaylist(pl entities.Playlist) { >
@icons.Playlist() -

{ pl.Title }

+

{ pl.Title }

-

{ fmt.Sprint(pl.SongsCount) } Songs

+

{ fmt.Sprint(pl.SongsCount) } Songs

} From 3fa05dc9b435958322529fdabbeab79cacd50d41 Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 14:25:25 +0300 Subject: [PATCH 11/27] chore(playlist): organize options --- app/views/components/playlist/options.templ | 24 ++++++++++----------- app/views/pages/playlist.templ | 22 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/views/components/playlist/options.templ b/app/views/components/playlist/options.templ index a77214a..8b2316e 100644 --- a/app/views/components/playlist/options.templ +++ b/app/views/components/playlist/options.templ @@ -26,6 +26,18 @@ templ playlistOptions(playlist entities.Playlist) {

Playlist's Options


@publicPlaylistToggle(playlist.PublicId, playlist.IsPublic) + if len(playlist.Songs) != 0 { } - if perm, ok := ctx.Value("playlist-permission").(models.PlaylistPermissions); ok && (perm & models.JoinerPermission) != 0 { if perm, ok := ctx.Value("playlist-permission").(models.PlaylistPermissions); ok && (perm & models.OwnerPermission) != 0 { @playlist.PlaylistsOptions(pl) } else { + @joinLeavePlaylist(pl.PublicId, perm) }
From e7ccd6f0cb5491cbc738e7f3b47d87514868d7d8 Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 18:18:56 +0300 Subject: [PATCH 12/27] fix(player): fixy wixy --- app/static/js/player.js | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/app/static/js/player.js b/app/static/js/player.js index 41609d7..fad287f 100644 --- a/app/static/js/player.js +++ b/app/static/js/player.js @@ -376,7 +376,8 @@ function playlister(state) { ? 0 : state.currentSongIdx + 1; const songToPlay = state.playlist.songs[state.currentSongIdx]; - playSongFromPlaylist(songToPlay.yt_id, state.playlist); + await highlightSongInPlaylist(songToPlay.yt_id, state.playlist); + await playSong(songToPlay); __setSongInPlaylistStyle(songToPlay.yt_id, state.playlist); }; @@ -415,7 +416,8 @@ function playlister(state) { ? state.playlist.songs.length - 1 : state.currentSongIdx - 1; const songToPlay = state.playlist.songs[state.currentSongIdx]; - playSongFromPlaylist(songToPlay.yt_id, state.playlist); + await highlightSongInPlaylist(songToPlay.yt_id, state.playlist); + await playSong(songToPlay); __setSongInPlaylistStyle(songToPlay.yt_id, state.playlist); }; @@ -662,6 +664,23 @@ async function playPlaylistNext(playlist) { alert(`Playing ${playlist.title} next!`); } +/** + * @param {Playlist} playlist + */ +async function playPlaylistNext(playlist) { + if (!playlist || !playlist.songs || playlist.songs.length === 0) { + alert("Can't do that!"); + return; + } + if (playerState.playlist.songs.length === 0) { + playSongFromPlaylist(playlist.songs[0].yt_id, playlist); + return; + } + playerState.playlist.songs.push(...playlist.songs); + playerState.playlist.title = `${playerState.playlist.title} + ${playlist.title}`; + alert(`Playing ${playlist.title} next!`); +} + /** * @param {string} songYtId * @param {Playlist} playlist @@ -701,12 +720,6 @@ async function playSongFromPlaylist(songYtId, playlist) { * @param {Song} song */ function appendSongToCurrentQueue(song) { - if ( - playerState.playlist.songs.findIndex((s) => s.yt_id === song.yt_id) !== -1 - ) { - alert(`${song.title} exists in the queue!`); - return; - } if (playerState.playlist.songs.length === 0) { playSingleSong(song); return; From 7730e4c1a29837d43ebcff3efd19ef4185502f0a Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 18:19:48 +0300 Subject: [PATCH 13/27] chore(playlist): add to queue, play next --- app/static/js/player.js | 11 +++++++++-- app/views/components/playlist/options.templ | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/static/js/player.js b/app/static/js/player.js index fad287f..ccd914c 100644 --- a/app/static/js/player.js +++ b/app/static/js/player.js @@ -659,7 +659,13 @@ async function playPlaylistNext(playlist) { playSongFromPlaylist(playlist.songs[0].yt_id, playlist); return; } - playerState.playlist.songs.push(...playlist.songs); + playerState.playlist.songs.splice( + playerState.currentSongIdx + 1, + 0, + ...song.map((s) => { + return { ...s, votes: 1 }; + }), + ); playerState.playlist.title = `${playerState.playlist.title} + ${playlist.title}`; alert(`Playing ${playlist.title} next!`); } @@ -667,7 +673,7 @@ async function playPlaylistNext(playlist) { /** * @param {Playlist} playlist */ -async function playPlaylistNext(playlist) { +async function appendPlaylistToCurrentQueue(playlist) { if (!playlist || !playlist.songs || playlist.songs.length === 0) { alert("Can't do that!"); return; @@ -950,6 +956,7 @@ window.Player.playSongFromPlaylist = playSongFromPlaylist; window.Player.playPlaylistNext = playPlaylistNext; window.Player.removeSongFromPlaylist = removeSongFromPlaylist; window.Player.addSongToQueue = appendSongToCurrentQueue; +window.Player.appendPlaylistToCurrentQueue = appendPlaylistToCurrentQueue; window.Player.stopMuzikk = stopMuzikk; window.Player.expand = () => expand(); window.Player.collapse = () => collapse(); diff --git a/app/views/components/playlist/options.templ b/app/views/components/playlist/options.templ index 8b2316e..257b325 100644 --- a/app/views/components/playlist/options.templ +++ b/app/views/components/playlist/options.templ @@ -39,6 +39,18 @@ templ playlistOptions(playlist entities.Playlist) { Share playlist if len(playlist.Songs) != 0 { + + > + + + + + + + + Delete playlist + } } From 7f82057daaa95879437e0c54859eeadb0c5ebf9e Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 19:40:42 +0300 Subject: [PATCH 19/27] fix(history): add to playlist option for paged songs --- app/handlers/apis/history.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/handlers/apis/history.go b/app/handlers/apis/history.go index 34d9bdf..b4e34e3 100644 --- a/app/handlers/apis/history.go +++ b/app/handlers/apis/history.go @@ -6,10 +6,13 @@ import ( "dankmuzikk/handlers" "dankmuzikk/log" "dankmuzikk/services/history" + "dankmuzikk/views/components/playlist" "dankmuzikk/views/components/song" "fmt" "net/http" "strconv" + + "github.com/a-h/templ" ) type historyApi struct { @@ -43,8 +46,12 @@ func (h *historyApi) HandleGetMoreHistoryItems(w http.ResponseWriter, r *http.Re } outBuf := bytes.NewBuffer([]byte{}) - for _, s := range recentPlays { - song.Song(s, []string{"Played " + s.AddedAt}, nil, entities.Playlist{}). + for idx, s := range recentPlays { + song.Song(s, []string{"Played " + s.AddedAt}, + []templ.Component{ + playlist.PlaylistsPopup((idx+1)*page, s.YtId), + }, + entities.Playlist{}). Render(r.Context(), outBuf) } From b219a5872cba418db143f84b786085409778303e Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Mon, 3 Jun 2024 19:47:57 +0300 Subject: [PATCH 20/27] fix(playlist): popup loading delay --- app/views/components/playlist/popup.templ | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/app/views/components/playlist/popup.templ b/app/views/components/playlist/popup.templ index ef44f21..e7b35d8 100644 --- a/app/views/components/playlist/popup.templ +++ b/app/views/components/playlist/popup.templ @@ -9,7 +9,7 @@ import ( ) templ PlaylistsPopup(index int, songId string) { - @menus.Popup(fmt.Sprint(index), "Add to playlist", popupButton(songId), playlistSelector(songId)) + @menus.Popup(fmt.Sprint(index), "Add to playlist", popupButton(), playlistSelector(songId)) } templ playlistSelector(songId string) { @@ -19,11 +19,14 @@ templ playlistSelector(songId string) { "min-w-[350px]", "bg-accent-trans-30", "backdrop-blur-xl", "p-3", "text-secondary", "rounded-b-[10px]", "rounded-l-[10px]", } + hx-get={ "/api/playlist/all?song-id=" + songId } + hx-swap="outerHTML" + hx-trigger="intersect" > -
+
+ Loading playlists...
- Loading playlists...
} @@ -84,13 +87,9 @@ templ PlaylistsSelector(songId string, playlists []entities.Playlist, songsInPla } -templ popupButton(songId string) { +templ popupButton() {
@icons.AddToPlaytlist() Save to a playlist From caba64b393f781acca8f190253882f3a20753486 Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Tue, 4 Jun 2024 09:54:15 +0300 Subject: [PATCH 21/27] feat(router): handle pop state with partial views --- app/static/js/router.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/static/js/router.js b/app/static/js/router.js index e316c9b..058a931 100644 --- a/app/static/js/router.js +++ b/app/static/js/router.js @@ -27,6 +27,23 @@ window.addEventListener("load", () => { updateActiveNavLink(); }); +window.addEventListener("popstate", async (e) => { + const mainContentsEl = document.getElementById("main-contents"); + if (!!mainContentsEl && !!e.target.location.pathname) { + e.stopImmediatePropagation(); + e.preventDefault(); + await fetch(e.target.location.pathname + "?no_layout=true") + .then((res) => res.text()) + .then((page) => { + mainContentsEl.innerHTML = page; + }) + .catch(() => { + window.location.reload(); + }); + return; + } +}); + document.addEventListener("htmx:afterRequest", function (e) { if (!!e.detail && !!e.detail.xhr) { const newTitle = e.detail.xhr.getResponseHeader("HX-Title"); From d4de79ae66ff0beaafb3a010f1914cf08d6bc136 Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Tue, 4 Jun 2024 10:29:13 +0300 Subject: [PATCH 22/27] chore(router): add loader (more tasty) --- app/static/js/router.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/static/js/router.js b/app/static/js/router.js index 058a931..5e72995 100644 --- a/app/static/js/router.js +++ b/app/static/js/router.js @@ -32,6 +32,7 @@ window.addEventListener("popstate", async (e) => { if (!!mainContentsEl && !!e.target.location.pathname) { e.stopImmediatePropagation(); e.preventDefault(); + Utils.showLoading(); await fetch(e.target.location.pathname + "?no_layout=true") .then((res) => res.text()) .then((page) => { @@ -39,6 +40,9 @@ window.addEventListener("popstate", async (e) => { }) .catch(() => { window.location.reload(); + }) + .finally(() => { + Utils.hideLoading(); }); return; } From 97e02db117e6697c9bdf8a0f84dc7d6472e3fdb1 Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Tue, 4 Jun 2024 10:32:31 +0300 Subject: [PATCH 23/27] chore(router): update highlights --- app/static/js/router.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/static/js/router.js b/app/static/js/router.js index 5e72995..10c097c 100644 --- a/app/static/js/router.js +++ b/app/static/js/router.js @@ -43,6 +43,7 @@ window.addEventListener("popstate", async (e) => { }) .finally(() => { Utils.hideLoading(); + updateActiveNavLink(); }); return; } From 80440f4664b778b45a6fa0f8f0cc66f68502504a Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Tue, 4 Jun 2024 20:46:27 +0300 Subject: [PATCH 24/27] feat(refresh): pull to refresh copy-pasta --- app/static/css/refresher.css | 80 ++++++++++++++++++++++++++++++++++++ app/static/js/refresher.js | 62 ++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 app/static/css/refresher.css create mode 100644 app/static/js/refresher.js diff --git a/app/static/css/refresher.css b/app/static/css/refresher.css new file mode 100644 index 0000000..6a8ccc4 --- /dev/null +++ b/app/static/css/refresher.css @@ -0,0 +1,80 @@ +/* I herby admit that this code is a copy-pasta from https://developer.chrome.com/blog/overscroll-behavior/ */ + +body.refreshing #main-contents, +body.refreshing header { + filter: blur(1px); + touch-action: none; +} + +body.refreshing .refresher { + transform: translate3d(0, 150%, 0) scale(1); + z-index: 1; + visibility: visible; +} + +.refresher { + pointer-events: none; + --refresh-width: 55px; + background: var(--secondary-color); + width: var(--refresh-width); + height: var(--refresh-width); + border-radius: 50%; + position: absolute; + left: calc(50% - var(--refresh-width) / 2); + padding: 8px; + box-shadow: + 0 2px 2px 0 rgba(0, 0, 0, 0.14), + 0 1px 5px 0 rgba(0, 0, 0, 0.12), + 0 3px 1px -2px rgba(0, 0, 0, 0.2); + transition: all 300ms cubic-bezier(0, 0, 0.2, 1); + will-change: transform, opacity; + display: inline-flex; + justify-content: space-evenly; + align-items: center; + visibility: hidden; +} + +body.refreshing .refresher.shrink { + transform: translate3d(0, 150%, 0) scale(0); + opacity: 0; +} + +.refresher.done { + transition: none; +} + +.loading-bar { + width: 4px; + height: 18px; + border-radius: 4px; + animation: loading 1s ease-in-out infinite; +} + +.loading-bar:nth-child(1) { + background-color: var(--primary-color); + animation-delay: 0; +} +.loading-bar:nth-child(2) { + background-color: var(--primary-color); + animation-delay: 0.09s; +} +.loading-bar:nth-child(3) { + background-color: var(--primary-color); + animation-delay: 0.18s; +} +.loading-bar:nth-child(4) { + background-color: var(--primary-color); + animation-delay: 0.27s; +} + +@keyframes loading { + 0% { + transform: scale(1); + } + 20% { + transform: scale(1, 2.2); + } + 40% { + transform: scale(1); + } +} diff --git a/app/static/js/refresher.js b/app/static/js/refresher.js new file mode 100644 index 0000000..83d21df --- /dev/null +++ b/app/static/js/refresher.js @@ -0,0 +1,62 @@ +/* I herby admit that this code is a copy-pasta from https://developer.chrome.com/blog/overscroll-behavior/ */ + +"use strict"; + +const mainContentsEl = document.getElementById("main-contents"); +let _startY = 0; + +async function simulateRefreshAction() { + const sleep = (timeout) => + new Promise((resolve) => setTimeout(resolve, timeout)); + + const transitionEnd = function (propertyName, node) { + return new Promise((resolve) => { + function callback(e) { + e.stopPropagation(); + if (e.propertyName === propertyName) { + node.removeEventListener("transitionend", callback); + resolve(e); + } + } + node.addEventListener("transitionend", callback); + }); + }; + + const refresher = document.querySelector(".refresher"); + + document.body.classList.add("refreshing"); + await sleep(2000); + + refresher.classList.add("shrink"); + await transitionEnd("transform", refresher); + refresher.classList.add("done"); + + refresher.classList.remove("shrink"); + document.body.classList.remove("refreshing"); + await sleep(0); // let new styles settle. + refresher.classList.remove("done"); +} + +mainContentsEl.addEventListener( + "touchstart", + (e) => { + _startY = e.touches[0].pageY; + }, + { passive: true }, +); + +mainContentsEl.addEventListener( + "touchmove", + async (e) => { + const y = e.touches[0].pageY; + if ( + document.scrollingElement.scrollTop === 0 && + y > _startY && + !document.body.classList.contains("refreshing") + ) { + await simulateRefreshAction(); + await updateMainContent(window.location.pathname); + } + }, + { passive: true }, +); From 07e2808035a6dfbc815d42ab0a33a3a066e3a6d8 Mon Sep 17 00:00:00 2001 From: Baraa Al-Masri Date: Tue, 4 Jun 2024 20:51:48 +0300 Subject: [PATCH 25/27] chore(refresher): rest of the stuff --- app/static/js/router.js | 35 ++++++++++++++++++++------------- app/views/layouts/default.templ | 10 +++++++++- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/app/static/js/router.js b/app/static/js/router.js index 10c097c..537e45f 100644 --- a/app/static/js/router.js +++ b/app/static/js/router.js @@ -27,24 +27,31 @@ window.addEventListener("load", () => { updateActiveNavLink(); }); +/** + * @param {string} path the requested path to update. + */ +async function updateMainContent(path) { + Utils.showLoading(); + await fetch(path + "?no_layout=true") + .then((res) => res.text()) + .then((page) => { + mainContentsEl.innerHTML = page; + }) + .catch(() => { + window.location.reload(); + }) + .finally(() => { + Utils.hideLoading(); + updateActiveNavLink(); + }); +} + window.addEventListener("popstate", async (e) => { const mainContentsEl = document.getElementById("main-contents"); if (!!mainContentsEl && !!e.target.location.pathname) { e.stopImmediatePropagation(); e.preventDefault(); - Utils.showLoading(); - await fetch(e.target.location.pathname + "?no_layout=true") - .then((res) => res.text()) - .then((page) => { - mainContentsEl.innerHTML = page; - }) - .catch(() => { - window.location.reload(); - }) - .finally(() => { - Utils.hideLoading(); - updateActiveNavLink(); - }); + await updateMainContent(e.target.location.pathname); return; } }); @@ -58,4 +65,4 @@ document.addEventListener("htmx:afterRequest", function (e) { } }); -window.Router = { updateActiveNavLink }; +window.Router = { updateActiveNavLink, updateMainContent }; diff --git a/app/views/layouts/default.templ b/app/views/layouts/default.templ index 4a22067..5c22230 100644 --- a/app/views/layouts/default.templ +++ b/app/views/layouts/default.templ @@ -39,6 +39,7 @@ templ Default(title string, children ...templ.Component) { type="text/css" href="/static/css/ubuntu-font.css" /> + @@ -50,7 +51,13 @@ templ Default(title string, children ...templ.Component) { class={ "min-w-screen", "min-h-screen", "p-0", "m-0", "font-Ubuntu" } > @header.Header() -
+
+
+
+
+
+
+
for _, child := range children { @child } @@ -71,6 +78,7 @@ templ Default(title string, children ...templ.Component) { // +