<script setup lang="ts"> 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: "hadithaLibrary", }); useHead({ name: "hadithaLibrary", 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 = useTemplateRef<HTMLElement>("el"); const httpService = useNuxtApp()["$http"]; const total = ref(0); const currentPage = useState("currentPage", () => 0); // #endregion refs // #region reactive const state = reactive({ pagination: { limit: 10, page: 1, pages: 1, }, }); // #region methods const getLibraryList = async (dataType = "bookmark") => { let url = repoUrl() + hadithaApi.library.list; url = url.replace("@field_collapsed", "normal"); url = url.replace("@offset", currentPage.value); url = url.replace("@limit", state.pagination.limit); url = url.replace("@q", "none"); return await httpService.postRequest(url).then((res) => { total.value = res.hits.total.value ?? 0; currentPage.value += state.pagination.limit; return res; }); }; // Server-side initial load const { data: loadedItems } = await useAsyncData( "libraryList", () => getLibraryList(), { transform: (data) => data.hits.hits, getCachedData: (key) => { return useNuxtApp().payload.data[key] || useNuxtApp().static.data[key]; }, } ); // Client-side state const loading = ref(false); const hasMore = ref(true); // Client-side infinite scroll useInfiniteScroll( el, async () => { if (!hasMore.value || loading.value) return; loading.value = true; try { // const nextPage = page.value + 1; await getLibraryList().then((res) => { const hits = res.hits.hits; if (hits.length) { loadedItems.value.push(...hits); } else { hasMore.value = false; } }); } finally { loading.value = false; } }, { distance: 100 } ); // #endregion methods // components declaration const HadithaLayout = defineAsyncComponent( () => import("@haditha/layouts/HadithaLayout.vue") ); const NavigationMenu = defineAsyncComponent( () => import("@haditha/components/haditha/NavigationMenu.vue") ); const CardList = defineAsyncComponent( () => import("@haditha/components/haditha/CardList.vue") ); </script> <template> <HadithaLayout> <div class="search-box-container h-full flex flex-col justify-center"> <navigation-menu></navigation-menu> <div class="library-list-contianer"> <div class="page-header flex items-center"> <span class="title">کتابخانه</span> <img fit="auto" quality="80" src="/img/haditha/haditha-title.svg" /> </div> <div ref="el" class="library-list grid grid-cols-5 gap-x-28 gap-y-12"> <!-- Client-side loaded content --> <card-list v-if="loadedItems.length" no-data-text="هنوز چیزی ذخیره نکردهاید!" no-data-icon="/img/haditha/no-data.png" :list="loadedItems" ></card-list> </div> </div> </div> </HadithaLayout> </template> <style scoped> .search-box-container { padding-top: 8.3em; background: #f7fffd; .library-list-contianer { margin-top: 10em; max-width: 1200px; width: 100%; margin: 0 1em; margin-right: auto; margin-left: auto; .page-header { margin-bottom: 2em; .title { margin-left: 0.4em; font-family: IRANSansX; font-weight: 300; font-size: 24px; line-height: 36px; letter-spacing: 0%; text-align: center; color: var(--ui-color-two); } } .library-list { /* padding: 1em 1.3em; */ height: calc(100dvh - 13.5em); overflow-y: auto; } .no-data-text { font-family: IRANSansX; font-weight: 300; font-size: 16px; line-height: 24px; letter-spacing: 0%; text-align: center; } } } </style>