diff --git a/frontend/components/Domain/Recipe/RecipeCardSection.vue b/frontend/components/Domain/Recipe/RecipeCardSection.vue
index fd9382194..4321c6d2e 100644
--- a/frontend/components/Domain/Recipe/RecipeCardSection.vue
+++ b/frontend/components/Domain/Recipe/RecipeCardSection.vue
@@ -160,13 +160,13 @@
-
-
-
+
+
+
@@ -243,6 +243,7 @@ const ready = ref(false);
const loading = ref(false);
const { fetchMore, getRandom } = useLazyRecipes(isOwnGroup.value ? null : groupSlug.value);
+const { savePosition, getSavedPage, restorePosition } = useScrollPosition();
const router = useRouter();
const queryFilter = computed(() => {
@@ -283,8 +284,29 @@ async function fetchRecipes(pageCount = 1) {
}
onMounted(async () => {
- await initRecipes();
- ready.value = true;
+ loading.value = true;
+ const savedPage = getSavedPage(route.path);
+
+ if (savedPage && savedPage > 2) {
+ page.value = 1;
+ hasMore.value = true;
+ const newRecipes = await fetchRecipes(savedPage);
+ if (newRecipes.length < perPage * savedPage) {
+ hasMore.value = false;
+ }
+ page.value = savedPage;
+ emit(REPLACE_RECIPES_EVENT, newRecipes);
+ ready.value = true;
+ restorePosition(route.path);
+ }
+ else {
+ await initRecipes();
+ ready.value = true;
+ if (savedPage) {
+ restorePosition(route.path);
+ }
+ }
+ loading.value = false;
});
let lastQuery: string | undefined = JSON.stringify(props.query);
@@ -337,6 +359,8 @@ const infiniteScroll = useThrottleFn(async () => {
emit(APPEND_RECIPES_EVENT, newRecipes);
}
+ savePosition(route.path, page.value);
+
loading.value = false;
}, 500);
diff --git a/frontend/composables/use-scroll-position.ts b/frontend/composables/use-scroll-position.ts
new file mode 100644
index 000000000..5f4a0755a
--- /dev/null
+++ b/frontend/composables/use-scroll-position.ts
@@ -0,0 +1,65 @@
+const scrollPositions = new Map();
+const pagePositions = new Map();
+
+export function useScrollPosition() {
+ const router = useRouter();
+
+ let observer: MutationObserver | null = null;
+ let timeout: ReturnType | null = null;
+ let fallback: ReturnType | null = null;
+
+ function savePosition(path: string, page: number) {
+ scrollPositions.set(path, document.documentElement.scrollTop);
+ pagePositions.set(path, page);
+ }
+
+ function getSavedPage(path: string): number | undefined {
+ return pagePositions.get(path);
+ }
+
+ function restorePosition(path: string) {
+ const savedPosition = scrollPositions.get(path);
+ if (!savedPosition) return;
+
+ observer?.disconnect();
+ if (timeout) clearTimeout(timeout);
+ if (fallback) clearTimeout(fallback);
+
+ fallback = setTimeout(() => {
+ if (timeout) clearTimeout(timeout);
+ observer?.disconnect();
+ document.documentElement.scrollTop = savedPosition;
+ }, 500);
+
+ observer = new MutationObserver(() => {
+ if (timeout) clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ if (fallback) clearTimeout(fallback);
+ observer?.disconnect();
+ document.documentElement.scrollTop = savedPosition;
+ }, 100);
+ });
+
+ observer.observe(document.body, {
+ childList: true,
+ subtree: true,
+ });
+ }
+
+ const unregisterBefore = router.beforeEach((to, from) => {
+ scrollPositions.set(from.path, document.documentElement.scrollTop);
+ });
+
+ onUnmounted(() => {
+ unregisterBefore();
+ observer?.disconnect();
+ if (timeout) clearTimeout(timeout);
+ if (fallback) clearTimeout(fallback);
+ });
+
+ return {
+ savePosition,
+ getSavedPage,
+ restorePosition,
+ };
+}