hadith_ui/pages/haditha/favorites/index.vue
2025-05-03 17:12:25 +03:30

220 lines
5.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import hadithaApi from "@haditha/apis/hadithaApi";
import headLinks from "@haditha/json/haditha/headLinks";
import headMetas from "@haditha/json/haditha/headMetas";
import { useInfiniteScroll } from "@vueuse/core";
definePageMeta({
layout: false,
name: "hadithaFavorites",
});
useHead({
name: "hadithaFavorites",
title: `${import.meta.env.VITE_HADITH_PAGE_TITLE} | ذخیره ها`,
meta: [
{ name: "description", content: "کاوش با هوش مصنوعی در احادیث اسلامی" },
...headMetas,
],
bodyAttrs: {
class: import.meta.env.VITE_HADITH_SYSTEM,
},
link: headLinks,
});
// #region refs
const el = ref(null);
const httpService = useNuxtApp()["$http"];
const offset = useState("offset", () => 0);
const total = useState("total", () => 0);
const loading = useState("loading", () => false);
const hasMore = useState("hasMore", () => true);
// #endregion refs
// #region reactive
const state = reactive({
// list: new Array(5).fill(0),
pagination: {
page: 1,
pages: 1,
// offset: 0,
limit: 500,
},
});
// #endregion reactive
// #region methods
const getFavorites = async (dataType = "bookmark") => {
let url = repoUrl() + hadithaApi.favorite.getList;
url = url.replace("@data_type", dataType);
url = url.replace("@time_key", "all");
url = url.replace("@source", "main");
url = url.replace("@offset", offset.value);
url = url.replace("@limit", state.pagination.limit);
url = url.replace("@q", "none");
return await httpService.getRequest(url).then((res) => {
console.info(res);
total.value = res.hits?.total?.value ?? 0;
offset.value += state.pagination.limit;
return res;
});
};
const { data: favoriteList } = await useAsyncData(
"favorites",
() => getFavorites(),
{
transform: (data) => data.hits.hits,
}
);
// Client-side infinite scroll
useInfiniteScroll(
el,
async () => {
if (!hasMore.value || loading.value) return;
loading.value = true;
try {
await getFavorites().then((res) => {
const hits = res?.hits?.hits ?? [];
if (hits.length) {
// Use spread operator to create new array reference
favoriteList.value = [...favoriteList.value, ...hits];
} else {
hasMore.value = false;
}
});
} catch (error) {
hasMore.value = false;
// console.error("Error loading more items:", error);
// Consider setting hasMore.value = false if you want to stop on error
} finally {
loading.value = false;
}
},
{
distance: 100,
}
);
const updateList = (index) => {
favoriteList.value.splice(index, 1);
};
const HadithaLayout = defineAsyncComponent(() =>
import("@haditha/layouts/HadithaLayout.vue")
);
const NavigationMenu = defineAsyncComponent(() =>
import("@haditha/components/haditha/NavigationMenu.vue")
);
const SearchList = defineAsyncComponent(() =>
import("@haditha/components/haditha/search-page/SearchList.vue")
);
</script>
<template>
<HadithaLayout>
<div class="h-full flex flex-col justify-center">
<div class="bg-container h-full">
<navigation-menu></navigation-menu>
<div class="text-logo">
<div
v-show="favoriteList?.length"
class="search-box-container pb-0 flex justify-center"
>
<div class="search-list-contianer">
<div class="total">
<span>{{ total }}</span>
نتیجه
</div>
<div
ref="el"
class="search-list firefox-scrollbar hadithaFavorites"
>
<search-list
no-data-text="هنوز چیزی ذخیره نکرده‌اید!"
no-data-icon="/img/haditha/save.png"
:list="favoriteList"
@on-bookmard-removed="updateList"
></search-list>
</div>
</div>
</div>
<no-data
v-show="favoriteList?.length == 0"
class="h-full w-full flex flex-col justify-center items-center"
>
<img fit="auto" quality="80" src="/img/haditha/save.png" />
<p class="no-data-text">هنوز چیزی ذخیره نکرده‌اید!</p>
</no-data>
</div>
</div>
</div>
</HadithaLayout>
</template>
<style scoped>
.bg-container {
min-height: 100%;
/* height: 100dvh; */
background-size: cover;
background-repeat: no-repeat;
background: #f7fffd;
}
.text-logo {
height: 100%;
padding-top: 4.5em;
position: relative;
}
.search-box-container {
padding-top: 0.7em;
padding-bottom: 4em; /*64px */
&.pb-0 {
padding-bottom: 0 !important;
}
}
.search-list-contianer {
/*max-width: 41em; 656px*/
max-width: 75em; /*1200px*/
width: 100%;
margin: 0 1em;
.total {
padding: 0.5em 1.8em;
font-family: var(--font);
font-weight: 400;
font-size: 0.68rem; /*11px*/
line-height: 1rem;
letter-spacing: 0%;
text-align: right;
color: #b4c2cf;
}
.search-list {
padding: 1em 1.3em;
height: calc(100dvh - 16em);
overflow-y: auto;
&.hadithaFavorites {
height: calc(100dvh - 8em);
}
&:not(:last-child) {
border-bottom: 0.3px solid #d9d9d9;
}
}
}
</style>