Re-did list components

This commit is contained in:
2022-01-10 00:48:15 +01:00
parent 97c23fa895
commit 6615827b29
8 changed files with 278 additions and 180 deletions

View File

@@ -3,7 +3,7 @@
<LandingBanner />
<div v-for="list in lists">
<list-header :title="list.title" :link="'/list/' + list.route" />
<list-header :title="list.title" :link="list.route" />
<results-list :results="list.data" :shortList="true" />
<loader v-if="!list.data.length" />
@@ -36,22 +36,22 @@ export default {
return [
{
title: "Requests",
route: "request",
route: "/requests",
data: this.requests
},
{
title: "Now playing",
route: "now_playing",
route: "/list/now_playing",
data: this.nowplaying
},
{
title: "Upcoming",
route: "upcoming",
route: "/list/upcoming",
data: this.upcoming
},
{
title: "Popular",
route: "popular",
route: "/list/popular",
data: this.popular
}
];

View File

@@ -1,15 +1,20 @@
<template>
<header :class="{ 'sticky': sticky }">
<header :class="{ sticky: sticky }">
<h2>{{ title }}</h2>
<div v-if="info instanceof Array" class="flex flex-direction-column">
<span v-for="item in info" class="info">{{ item }}</span>
</div>
<span v-else class="info">{{ info }}</span>
<router-link v-if="link" :to="link" class='view-more' :aria-label="`View all ${title}`">
<router-link
v-if="link"
:to="link"
class="view-more"
:aria-label="`View all ${title}`"
>
View All
</router-link>
</header>
</header>
</template>
<script>
@@ -33,14 +38,13 @@ export default {
required: false
}
}
}
};
</script>
<style lang="scss" scoped>
@import './src/scss/variables';
@import './src/scss/media-queries';
@import './src/scss/main';
@import "./src/scss/variables";
@import "./src/scss/media-queries";
@import "./src/scss/main";
header {
width: 100%;
@@ -57,7 +61,7 @@ header {
position: sticky;
position: -webkit-sticky;
top: $header-size;
z-index: 4;
z-index: 1;
}
h2 {
@@ -72,16 +76,16 @@ header {
.view-more {
font-size: 0.9rem;
font-weight: 300;
letter-spacing: .5px;
letter-spacing: 0.5px;
color: $text-color-70;
text-decoration: none;
transition: color .5s ease;
transition: color 0.5s ease;
cursor: pointer;
&:after{
&:after {
content: " →";
}
&:hover{
&:hover {
color: $text-color;
}
}
@@ -89,18 +93,17 @@ header {
.info {
font-size: 13px;
font-weight: 300;
letter-spacing: .5px;
letter-spacing: 0.5px;
color: $text-color;
text-decoration: none;
text-align: right;
}
@include tablet-min {
padding-left: 1.25rem;;
padding-left: 1.25rem;
}
@include desktop-lg-min {
padding-left: 1.75rem;
}
}
</style>
</style>

View File

@@ -1,105 +1,24 @@
<template>
<div>
<list-header :title="listTitle" :info="info" :sticky="true" />
<results-list :results="results" v-if="results" />
<loader v-if="!results.length" />
<div v-if="page < totalPages" class="fullwidth-button">
<seasoned-button @click="loadMore">load more</seasoned-button>
</div>
</div>
<ResultsSection :title="listName" :apiFunction="getTmdbMovieListByName" />
</template>
<script>
import ListHeader from '@/components/ListHeader'
import ResultsList from '@/components/ResultsList'
import SeasonedButton from '@/components/ui/SeasonedButton'
import Loader from '@/components/ui/Loader'
import { getTmdbMovieListByName, getRequests } from '@/api'
import store from '@/store'
import ResultsSection from "./ResultsSection";
import { getTmdbMovieListByName } from "@/api";
export default {
components: { ListHeader, ResultsList, SeasonedButton, Loader },
data() {
return {
legalTmdbLists: [ 'now_playing', 'upcoming', 'popular' ],
results: [],
page: 1,
totalPages: 0,
totalResults: 0,
loading: true
}
},
components: { ResultsSection },
computed: {
listTitle() {
if (this.results.length === 0)
return ''
const routeListName = this.$route.params.name
console.log('routelistname', routeListName)
return routeListName.includes('_') ? routeListName.split('_').join(' ') : routeListName
},
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}`
listName() {
return this.$route.params.name;
}
},
methods: {
loadMore() {
console.log(this.$route)
this.loading = true;
this.page++
window.history.replaceState({}, 'search', `/#/${this.$route.fullPath}?page=${this.page}`)
this.init()
},
init() {
const routeListName = this.$route.params.name
if (routeListName === 'request') {
getRequests(this.page)
.then(results => {
this.results = this.results.concat(...results.results)
this.page = results.page
this.totalPages = results.total_pages
this.totalResults = results.total_results
})
} else if (this.legalTmdbLists.includes(routeListName)) {
getTmdbMovieListByName(routeListName, this.page)
.then(results => {
this.results = this.results.concat(...results.results)
this.page = results.page
this.totalPages = results.total_pages
this.totalResults = results.total_results
})
} else {
// TODO handle if list is not found
console.log('404 this is not a tmdb list')
}
this.loading = false
getTmdbMovieListByName(page) {
return getTmdbMovieListByName(this.listName, page);
}
},
created() {
if (this.results.length === 0)
this.init()
store.dispatch('documentTitle/updateTitle', `${this.$router.history.current.name} ${this.$route.params.name}`)
}
}
};
</script>
<style lang="scss" scoped>

View File

@@ -80,7 +80,7 @@ export default {
if (this.movie.poster != null) {
this.poster = "https://image.tmdb.org/t/p/w500" + this.movie.poster;
} else {
this.poster = "/no-image.png";
this.poster = "/assets/no-image.png";
}
},
mounted() {
@@ -117,7 +117,6 @@ export default {
padding: 10px;
width: 50%;
background-color: $background-color;
transition: background-color 0.5s ease;
@include tablet-min {
padding: 15px;

View File

@@ -0,0 +1,25 @@
<template>
<ResultsSection title="Requests" :apiFunction="getRequests" />
</template>
<script>
import ResultsSection from "./ResultsSection";
import { getRequests } from "@/api";
export default {
components: { ResultsSection },
methods: {
getRequests: getRequests
}
};
</script>
<style lang="scss" scoped>
.fullwidth-button {
width: 100%;
margin: 1rem 0;
padding-bottom: 2rem;
display: flex;
justify-content: center;
}
</style>

View File

@@ -24,12 +24,11 @@ export default {
default: false
}
}
}
};
</script>
<style lang="scss" scoped>
@import './src/scss/media-queries';
@import "./src/scss/media-queries";
.results {
display: flex;
@@ -41,31 +40,30 @@ export default {
&.shortList > li {
display: none;
&:nth-child(-n+4) {
&:nth-child(-n + 4) {
display: block;
}
}
}
@include tablet-min {
.results.shortList > li:nth-child(-n+6) {
.results.shortList > li:nth-child(-n + 6) {
display: block;
}
}
@include tablet-landscape-min {
.results.shortList > li:nth-child(-n+8) {
.results.shortList > li:nth-child(-n + 8) {
display: block;
}
}
@include desktop-min {
.results.shortList > li:nth-child(-n+10) {
.results.shortList > li:nth-child(-n + 10) {
display: block;
}
}
@include desktop-lg-min {
.results.shortList > li:nth-child(-n+16) {
.results.shortList > li:nth-child(-n + 16) {
display: block;
}
}
</style>
</style>

View File

@@ -0,0 +1,155 @@
<template>
<div>
<list-header :title="prettify(title)" :info="info" :sticky="true" />
<div v-if="!loadedPages.includes(1)" class="load-button">
<seasoned-button @click="loadLess" :fullWidth="true"
>load previous</seasoned-button
>
</div>
<results-list :results="results" v-if="results" />
<loader v-if="!results.length" />
<div v-if="page < totalPages" class="load-button">
<seasoned-button @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 Loader from "@/components/ui/Loader";
import store from "@/store";
export default {
props: {
apiFunction: {
type: Function,
required: true
},
title: {
type: String,
required: true
}
},
components: { ListHeader, ResultsList, SeasonedButton, Loader },
data() {
return {
results: [],
page: 1,
loadedPages: [],
totalPages: 0,
totalResults: 0,
loading: true
};
},
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: {
prettify(listName) {
return listName.includes("_") ? listName.split("_").join(" ") : listName;
},
loadMore() {
this.loading = true;
let maxPage = [...this.loadedPages].slice(-1)[0];
console.log("maxPage:", maxPage);
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}` : ""
}`
);
},
getPageFromQueryParams() {
return new URLSearchParams(window.location.search).get("page");
},
getListResults(front = false) {
this.apiFunction(this.page)
.then(results => {
if (front) this.results = results.results.concat(...this.results);
else this.results = this.results.concat(...results.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));
}
},
created() {
this.page = this.getPageFromQueryParams() || this.page;
if (this.results.length === 0) this.getListResults();
store.dispatch(
"documentTitle/updateTitle",
`${this.$router.history.current.name} ${this.$route.params.name}`
);
}
};
</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>

View File

@@ -1,61 +1,58 @@
import Vue from 'vue'
import VueRouter from 'vue-router';
import store from '@/store'
Vue.use(VueRouter)
import Vue from "vue";
import VueRouter from "vue-router";
import store from "@/store";
Vue.use(VueRouter);
let routes = [
{
name: 'home',
path: '/',
component: (resolve) => require(['./components/Home.vue'], resolve)
name: "home",
path: "/",
component: resolve => require(["./components/Home.vue"], resolve)
},
{
name: 'activity',
path: '/activity',
name: "activity",
path: "/activity",
meta: { requiresAuth: true },
component: (resolve) => require(['./components/ActivityPage.vue'], resolve)
component: resolve => require(["./components/ActivityPage.vue"], resolve)
},
{
name: 'profile',
path: '/profile',
name: "profile",
path: "/profile",
meta: { requiresAuth: true },
component: (resolve) => require(['./components/Profile.vue'], resolve)
component: resolve => require(["./components/Profile.vue"], resolve)
},
{
name: 'list',
path: '/list/:name',
component: (resolve) => require(['./components/ListPage.vue'], resolve)
name: "list",
path: "/list/:name",
component: resolve => require(["./components/ListPage.vue"], resolve)
},
{
name: 'request',
path: '/request/all',
components: {
'request-router-view': require('./components/ListPage.vue')
}
name: "request",
path: "/requests",
component: resolve => require(["./components/RequestPage.vue"], resolve)
},
{
name: 'search',
path: '/search',
component: (resolve) => require(['./components/Search.vue'], resolve)
name: "search",
path: "/search",
component: resolve => require(["./components/Search.vue"], resolve)
},
{
name: 'register',
path: '/register',
component: (resolve) => require(['./components/Register.vue'], resolve)
name: "register",
path: "/register",
component: resolve => require(["./components/Register.vue"], resolve)
},
{
name: 'settings',
path: '/settings',
name: "settings",
path: "/settings",
meta: { requiresAuth: true },
component: (resolve) => require(['./components/Settings.vue'], resolve)
component: resolve => require(["./components/Settings.vue"], resolve)
},
{
name: 'signin',
path: '/signin',
alias: '/login',
component: (resolve) => require(['./components/Signin.vue'], resolve)
name: "signin",
path: "/signin",
alias: "/login",
component: resolve => require(["./components/Signin.vue"], resolve)
},
// {
// name: 'user-requests',
@@ -65,50 +62,52 @@ let routes = [
// }
// },
{
name: '404',
path: '/404',
component: (resolve) => require(['./components/404.vue'], resolve)
name: "404",
path: "/404",
component: resolve => require(["./components/404.vue"], resolve)
},
{
name: 'logout',
path: '/logout',
name: "logout",
path: "/logout",
component: {
template: '<div></div>',
template: "<div></div>",
created() {
localStorage.clear();
this.$router.push({ name: 'home' });
this.$router.push({ name: "home" });
}
}
},
{
path: '*',
redirect: '/'
path: "*",
redirect: "/"
},
{
path: '/request',
redirect: '/'
path: "/request",
redirect: "/"
}
];
const router = new VueRouter({
mode: 'history',
base: '/',
const router = new VueRouter({
mode: "history",
base: "/",
routes,
linkActiveClass: 'is-active'
linkActiveClass: "is-active"
});
router.beforeEach((to, from, next) => {
store.dispatch('documentTitle/updateTitle', to.name)
store.dispatch("documentTitle/updateTitle", to.name);
// Toggle mobile nav
if(document.querySelector('.nav__hamburger--active')){
document.querySelector('.nav__hamburger').classList.remove('nav__hamburger--active');
document.querySelector('.nav__list').classList.remove('nav__list--active');
if (document.querySelector(".nav__hamburger--active")) {
document
.querySelector(".nav__hamburger")
.classList.remove("nav__hamburger--active");
document.querySelector(".nav__list").classList.remove("nav__list--active");
}
if (to.matched.some(record => record.meta.requiresAuth)) {
if (localStorage.getItem('token') == null) {
next({ path: '/signin' });
if (localStorage.getItem("token") == null) {
next({ path: "/signin" });
}
}