189 lines
4.7 KiB
Vue
189 lines
4.7 KiB
Vue
<template>
|
|
<div ref="resultSection">
|
|
<list-header v-bind="{ title, info, shortList }" :sticky="true" />
|
|
|
|
<div
|
|
v-if="!loadedPages.includes(1) && loading == false"
|
|
class="load-button"
|
|
>
|
|
<seasoned-button @click="loadLess" :fullWidth="true"
|
|
>load previous</seasoned-button
|
|
>
|
|
</div>
|
|
|
|
<results-list v-bind="{ results, shortList }" />
|
|
<loader v-if="loading" />
|
|
|
|
<div v-if="!shortList && page != totalPages" class="load-button">
|
|
<seasoned-button ref="loadMoreButton" @click="loadMore" :fullWidth="true"
|
|
>load more</seasoned-button
|
|
>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import ListHeader from "@/components/ListHeader";
|
|
import ResultsList from "@/components/ResultsList";
|
|
import SeasonedButton from "@/components/ui/SeasonedButton";
|
|
import store from "@/store";
|
|
import { getTmdbMovieListByName } from "@/api";
|
|
import Loader from "@/components/ui/Loader";
|
|
|
|
export default {
|
|
props: {
|
|
apiFunction: {
|
|
type: Function,
|
|
required: true
|
|
},
|
|
title: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
shortList: {
|
|
type: Boolean,
|
|
required: false,
|
|
default: false
|
|
}
|
|
},
|
|
components: { ListHeader, ResultsList, SeasonedButton, Loader },
|
|
data() {
|
|
return {
|
|
results: [],
|
|
page: 1,
|
|
loadedPages: [],
|
|
totalPages: -1,
|
|
totalResults: 0,
|
|
loading: true,
|
|
autoLoad: false,
|
|
observer: undefined
|
|
};
|
|
},
|
|
computed: {
|
|
info() {
|
|
if (this.results.length === 0) return [null, null];
|
|
return [this.pageCount, this.resultCount];
|
|
},
|
|
resultCount() {
|
|
const loadedResults = this.results.length;
|
|
const totalResults = this.totalResults < 10000 ? this.totalResults : "∞";
|
|
return `${loadedResults} of ${totalResults} results`;
|
|
},
|
|
pageCount() {
|
|
return `Page ${this.page} of ${this.totalPages}`;
|
|
}
|
|
},
|
|
methods: {
|
|
loadMore() {
|
|
if (!this.autoLoad) {
|
|
this.autoLoad = true;
|
|
}
|
|
|
|
this.loading = true;
|
|
let maxPage = [...this.loadedPages].slice(-1)[0];
|
|
|
|
if (maxPage == NaN) return;
|
|
this.page = maxPage + 1;
|
|
this.getListResults();
|
|
},
|
|
loadLess() {
|
|
this.loading = true;
|
|
const minPage = this.loadedPages[0];
|
|
if (minPage === 1) return;
|
|
|
|
this.page = minPage - 1;
|
|
this.getListResults(true);
|
|
},
|
|
updateQueryParams() {
|
|
let params = new URLSearchParams(window.location.search);
|
|
if (params.has("page")) {
|
|
params.set("page", this.page);
|
|
} else if (this.page > 1) {
|
|
params.append("page", this.page);
|
|
}
|
|
|
|
window.history.replaceState(
|
|
{},
|
|
"search",
|
|
`${window.location.protocol}//${window.location.hostname}${
|
|
window.location.port ? `:${window.location.port}` : ""
|
|
}${window.location.pathname}${
|
|
params.toString().length ? `?${params}` : ""
|
|
}`
|
|
);
|
|
},
|
|
getPageFromUrl() {
|
|
return new URLSearchParams(window.location.search).get("page");
|
|
},
|
|
getListResults(front = false) {
|
|
this.apiFunction(this.page)
|
|
.then(results => {
|
|
if (!front) this.results = this.results.concat(...results.results);
|
|
else this.results = results.results.concat(...this.results);
|
|
this.page = results.page;
|
|
this.loadedPages.push(this.page);
|
|
this.loadedPages = this.loadedPages.sort();
|
|
this.totalPages = results.total_pages;
|
|
this.totalResults = results.total_results;
|
|
})
|
|
.then(this.updateQueryParams)
|
|
.finally(() => (this.loading = false));
|
|
},
|
|
setupAutoloadObserver() {
|
|
this.observer = new IntersectionObserver(this.handleButtonIntersection, {
|
|
root: this.$refs.resultSection.$el,
|
|
rootMargin: "0px",
|
|
threshold: 1.0
|
|
});
|
|
|
|
this.observer.observe(this.$refs.loadMoreButton.$el);
|
|
},
|
|
handleButtonIntersection(entries) {
|
|
entries.map(entry =>
|
|
entry.isIntersecting && this.autoLoad ? this.loadMore() : null
|
|
);
|
|
}
|
|
},
|
|
created() {
|
|
this.page = this.getPageFromUrl() || this.page;
|
|
if (this.results.length === 0) this.getListResults();
|
|
|
|
store.dispatch(
|
|
"documentTitle/updateTitle",
|
|
`${this.$router.history.current.name} ${this.$route.params.name}`
|
|
);
|
|
},
|
|
mounted() {
|
|
if (!this.shortList) {
|
|
this.setupAutoloadObserver();
|
|
}
|
|
},
|
|
beforeDestroy() {
|
|
this.observer = undefined;
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import "src/scss/media-queries";
|
|
|
|
.load-button {
|
|
display: flex;
|
|
width: 100%;
|
|
justify-content: center;
|
|
margin: 2rem 0;
|
|
|
|
@include mobile {
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
&:last-of-type {
|
|
margin-bottom: 4rem;
|
|
|
|
@include mobile {
|
|
margin-bottom: 2rem;
|
|
}
|
|
}
|
|
}
|
|
</style>
|