sakura/src/components/lists/postThumbList/PostThumbList.vue

169 lines
4.6 KiB
Vue

<template>
<div class="post-thumb-list__container" :ref="setListContainerRef">
<div class="post-thumb-card__wrapper" v-for="(post, index) in postList" :key="index">
<!-- <span>{{ index }}: {{ post.id }}</span> -->
<PostThumbCardIndex
:post="post"
:type="index % 2 ? 'normal' : 'reverse'"
></PostThumbCardIndex>
</div>
<div class="loader__wrapper" v-show="fetchStatus === 'pending'">
<BookLoader></BookLoader>
</div>
<div class="last-page__wrapper" v-show="isTheLastPage">no more</div>
<div class="navigation-bar__wrapper" v-show="!autoLoad">1 2 3 4 5 6 7</div>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, onMounted, Ref } from 'vue'
import {
useInjector,
useState,
useElementRef,
useReachElementSide,
useMessage,
useIntl,
} from '@/hooks'
import { posts } from '@/store'
import PostThumbCardIndex from '@/components/cards/postThumbCards/PostThumbCardIndex.vue'
import BookLoader from '@/components/loader/BookLoader.vue'
export default defineComponent({
components: { PostThumbCardIndex, BookLoader },
props: {
namespace: { type: String, default: 'homepage' },
page: { type: Number, default: 1 },
perPage: { type: Number, default: 10 },
autoLoad: { type: Boolean, default: true },
fetchParameters: {
type: Object,
default: () => {
return {}
},
},
},
setup(props) {
const addMessage = useMessage()
const intl = useIntl()
const [listContainerRef, setListContainerRef] = useElementRef()
const [fetchStatus, setFetchStatus] = useState('inite' as FetchingStatus)
const [currentPage, setCurrentPage] = useState(props.page)
const [postList, setPostList]: [Ref<Post[]>, (attr: any) => any] = useState([] as Post[])
const {
postsStore,
fetchPost,
getPostsList,
}: { postsStore: Ref<PostStore>; [key: string]: any } = useInjector(posts) // TODO: fix useInjector return type
const start = computed(() => (props.autoLoad ? 0 : (currentPage.value - 1) * props.perPage - 1))
const end = computed(() => currentPage.value * props.perPage - 1)
const totalPage = computed(() =>
postsStore.value.list[props.namespace]
? postsStore.value.list[props.namespace].pagination.totalPage
: NaN
)
const get = () => {
const newPostList = getPostsList({
state: postsStore,
namespace: props.namespace,
start: start.value,
end: end.value,
})
setPostList(newPostList.data)
}
const fetch = async () => {
if (fetchStatus.value !== 'cached') {
setFetchStatus('pending')
}
fetchPost({
state: postsStore,
namespace: props.namespace,
opts: {
page: currentPage.value,
perPage: props.perPage,
context: 'embed',
...props.fetchParameters,
},
addMessage,
})
.then(() => {
get()
setFetchStatus('success')
})
.catch(() => {
setFetchStatus('error')
})
}
const next = () => {
if (currentPage.value + 1 <= totalPage.value) {
setCurrentPage(currentPage.value + 1)
fetch()
}
}
const prev = () => {
if (currentPage.value - 1 > 0) {
setCurrentPage(currentPage.value - 1)
fetch()
}
}
const goto = (page: number) => {
if (page <= totalPage.value && page > 0) {
setCurrentPage(page)
fetch()
}
}
const isTheLastPage = computed(() => currentPage.value === totalPage.value)
useReachElementSide(listContainerRef, {
bottom: () => next(),
})
onMounted(() => {
// this will only work when set to cache post store
window.setTimeout(() => {
get()
if (postList.value.length > 0) {
setFetchStatus('cached')
const msg = intl.formatMessage({
id: 'messages.postList.cache.found',
defaultMessage: 'Fetching the latest post list...',
})
addMessage({
type: 'info',
title: msg,
})
}
}, 0) // postsStore injection may not be OK when mounted
fetch()
})
return { setListContainerRef, postList, fetchStatus, isTheLastPage }
},
})
</script>
<style lang="scss" scoped>
.post-thumb-list__container {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
> *:last-child {
padding-bottom: 24px;
}
> .post-thumb-card__wrapper {
width: auto;
padding-top: 24px;
}
> .loader__wrapper {
padding-top: 24px;
}
}
</style>