From 68f990aada38fc03379d41d59916d45bf47f320c Mon Sep 17 00:00:00 2001 From: geoffrey45 Date: Tue, 3 Jan 2023 14:53:53 +0300 Subject: [PATCH] search tracks page scroll to top on search + add composable to wait for scroll --- src/composables/useWaitForScroll.ts | 41 +++++++++++++++++++++++++++++ src/stores/search.ts | 35 +++++++++++++++--------- 2 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 src/composables/useWaitForScroll.ts diff --git a/src/composables/useWaitForScroll.ts b/src/composables/useWaitForScroll.ts new file mode 100644 index 0000000..41127da --- /dev/null +++ b/src/composables/useWaitForScroll.ts @@ -0,0 +1,41 @@ +// CREDITS: https://stackoverflow.com/a/66664192 + +/** + * Scrolls and waits for the scroll to finish. Returns a promise that resolves when the scroll is finished. + * @param elem The element to scroll and wait for + * @param pos The position to scroll to + * @param delay The delay in seconds to wait for + * @returns A promise that resolves when the element has been scrolled to the position + */ +export default function waitForScrollEnd( + elem: HTMLElement, + pos = 0, + delay = 100 +): Promise { + elem.scroll({ + top: pos, + behavior: "smooth", + }); + const frame_limit = 20; + let last_changed_frame = 0; + let last_y = elem.scrollTop; + + return new Promise((resolve) => { + function tick(frames: number) { + // We requestAnimationFrame either for 500 frames or until 20 frames with + // no change have been observed. + if (frames >= 500 || frames - last_changed_frame > frame_limit) { + setTimeout(() => { + resolve(); + }, delay); + } else { + if (window.scrollY != last_y) { + last_changed_frame = frames; + last_y = window.scrollY; + } + requestAnimationFrame(tick.bind(null, frames + 1)); + } + } + tick(0); + }); +} diff --git a/src/stores/search.ts b/src/stores/search.ts index 9747183..c5d3b58 100644 --- a/src/stores/search.ts +++ b/src/stores/search.ts @@ -3,15 +3,20 @@ import { useDebounce } from "@vueuse/core"; import { defineStore } from "pinia"; import { watch } from "vue"; import { useRoute } from "vue-router"; -import { - loadMoreAlbums, - loadMoreArtists, loadMoreTracks, searchAlbums, - searchArtists, searchTracks -} from "../composables/fetch/searchMusic"; -import { Album, Artist, Playlist, Track } from "../interfaces"; import { Routes } from "@/router/routes"; + +import { + loadMoreAlbums, + loadMoreArtists, + loadMoreTracks, + searchAlbums, + searchArtists, + searchTracks, +} from "../composables/fetch/searchMusic"; import useLoaderStore from "./loader"; import useTabStore from "./tabs"; +import waitForScrollEnd from "@/composables/useWaitForScroll"; +import { Album, Artist, Playlist, Track } from "../interfaces"; /** * * Scrolls on clicking the loadmore button @@ -76,10 +81,16 @@ export default defineStore("search", () => { function fetchTracks(query: string) { if (!query) return; - searchTracks(query).then((res) => { - tracks.value = res.tracks; - tracks.more = res.more; - tracks.query = query; + searchTracks(query).then((data) => { + const scrollable = document.getElementById( + "songlist-scroller" + ) as HTMLElement; + + waitForScrollEnd(scrollable, 0).then(() => { + tracks.value = data.tracks; + tracks.more = data.more; + tracks.query = query; + }); }); } @@ -107,7 +118,7 @@ export default defineStore("search", () => { function loadTracks() { loadCounter.tracks += RESULT_COUNT; - startLoading(); + startLoading(); loadMoreTracks(loadCounter.tracks) .then((res) => { tracks.value = [...tracks.value, ...res.tracks]; @@ -137,7 +148,7 @@ export default defineStore("search", () => { function loadArtists() { loadCounter.artists += RESULT_COUNT; - startLoading(); + startLoading(); loadMoreArtists(loadCounter.artists) .then((res) => { artists.value = [...artists.value, ...res.artists];