<template lang="pug">
.page--list.f-col.between
    article.f-grow

        header.center.t-up
            .title.f-row
                h3 {{ type }}s&nbsp;
                span(v-if="sortBy")
                    h3 {{ sortBy.replace('-', ' ') }}

            h3(v-if="!loaded") loading...
            .content(
                v-else-if="allPagesLoaded && type && allPages[type]"
                v-html="allPages[type].content"
            )

        ul.posts.f-col(v-if="posts && loaded" :class="{ 'is-grid': grid }")
            template(v-for="(post, i) in posts" :key="post.slug")
                li.post.shadow(v-if="!post.inbetween" )
                    card(:content="post" :type="type" :wide="isWide")
                li.post.shadow.inbetween.t-up.f-row.w-max(v-else-if="post.inbetween" :id="post.slug")
                    p {{ post.slug }}
            
        //- Important: Do NOT remove this! Required for intersection observer
        footer
            p(v-if="loadingFetched") loading more {{ type }}...

    sidebar(v-if="sidebar" :type="`${type}`" layout="list")
</template>

<script>
import featuredImage from '@/components/featured-image'
import card from '@/components/card'
import sidebar from '@/components/sidebars/sidebar'
import { postTypeGetters, scrollTop, heroUtils } from './mixin-post-types'
import { postTypes, sortTypes, convertTitleCase } from '@/utils/helpers'

const TIMEOUT = 1
const INTERSECT_SELECTOR = '.page--list > article footer'
const wideTypes = ['event', 'exhibition', 'guide', 'post', 'publication']
const gridTypes = ['episode', 'artist', 'object', 'short']
const sansSidebarTypes = ['episode']

export default {
    components: { sidebar, featuredImage, card },
    mixins: [postTypeGetters, scrollTop, heroUtils],
    data() {
        return {
            page: 0,
            perPage: 21,
            keepFetching: true,
            loadingFetched: false,
            observer: null
        }
    },
    computed: {
        type() {
            return postTypes.includes(this.$route.params.type) ? this.$route.params.type : 'post'
        },
        sortBy() {
            return this.$route.params.sortBy
        },
        grid() {
            return gridTypes.includes(this.type)
        },
        isWide() {
            return wideTypes.includes(this.type)
        },
        sidebar() {
            return !sansSidebarTypes.includes(this.type)
        },
        pType() {
            if(!this.type) return
            return `${convertTitleCase(this.type)}s`
        },
        loaded() {
            if (!this.pType) return
            return this[`all${this.pType}Loaded`]
        },
        posts() {
            if (!this.pType) return
            return this[`all${this.pType}`]
        },
        shouldLoadAllAtOnce() {
            return this.type == 'episode' ||
                this.type == 'artist' && this.sortBy == sortTypes.material ||
                this.type == 'artist' && this.sortBy == sortTypes.episode
        }
    },
    methods: {
        async loadMorePosts(shouldClear) {
            if(!this.keepFetching) return console.warn('Nothing left to fetch...')

            const getPosts = async  (params, dispatchType) => {
                if(!this.type) throw `post type: ${this.type} not found...`
                console.log(`Getting more ${this.type} posts`)
                // We always grab all pages on hero init so no need to do it here
                return this.pType && this.type != 'page' ? await this.$store.dispatch(
                    `get${dispatchType}${this.pType}`,
                    { sortType: this.sortBy, params }
                ) : []
            }

            if(shouldClear) {
                this.$store.commit(`CLEAR_${this.pType.toUpperCase()}`)
                
                // Clear any state needed to track title inbetweens
                const hasInbetweens = ['artist']
                if(hasInbetweens.includes(this.type)) {
                    this.$store.commit(`CLEAR_${this.pType.toUpperCase()}_SEEN`)
                }
            }

            try {
                this.loadingFetched = true
                this.page++
                const res = await getPosts(
                    {
                        limit: this.shouldLoadAllAtOnce ? -1 : this.perPage,
                        page: this.page
                    },
                    this.shouldLoadAllAtOnce ? 'All' : 'More'
                )
                this.loadingFetched = false

                // Stop trying to load more posts
                if(res && !res.length || this.shouldLoadAllAtOnce) {
                    this.keepFetching = false
                    if(!res.length) console.warn(`Empty response for ${this.type}:`, res.length)
                    if(this.shouldLoadAllAtOnce) console.warn(`Fetched all responses for ${this.type}:`, res.length)
                }
            } catch (err) { console.error(err) }
        },
        async getPage(type) {
            await this._getAllIfNotLoaded('page', this.$store)
            if(!this.allPages) throw 'no pages in state'
            const page = this.allPages.filter(page => page.slug == `${type}s`)[0]
            if(!page) throw `No page: ${type} found. Cannot set hero.`
            return page
        },
        // _setHeroInfo(post) {} from mixin
        // _clearHero(store) {} from mixin
        async checkAndSetHero(type) {
            this._clearHero(this.$store)
            try {
                const page = await this.getPage(type)
                // We always set a hero no matter what
                // Because the hero component will deal
                // with how to render based on hero.url
                this.$store.commit('SET_HERO', this._setHeroInfo(page))
            } catch (err) { console.error(err) }
        },
        setIntersectionLoader() {
            // KeepFetching is UNSET for certain post types and sort types in `loadMorePosts()`
            if(!this.keepFetching) return console.warn('Cannot setup intersection handler keepFetching is set false')
            
            // Always unset before setting the intersection loader
            this.unsetIntersectionLoader()

            // console.warn('Setting interesection loader...')
            this.observer = new IntersectionObserver(entries => {
                entries.forEach(entry => {
                    if (!entry.isIntersecting || this.loadingFetched) return
                    setTimeout(this.loadMorePosts, TIMEOUT)
                })
            }, { threshold: 0.80 })
            this.observer.observe(document.querySelector(INTERSECT_SELECTOR))
        },
        unsetIntersectionLoader() {
            const footerEl = document.querySelector(INTERSECT_SELECTOR)
            try {
                if(!footerEl) throw `Cannot unset intersection handler missing el: ${footerEl}`
                if(!this.observer) return 
                this.observer.unobserve(footerEl)
                this.observer.disconnect()
            } catch (error) { console.error(error) }
        },
        initPostList() {
            this.page = 0
            this.keepFetching = true

            this.checkAndSetHero(this.type)

            // Clear any preloaded posts (from home, etc.)
            this.loadMorePosts(true)
            this.setIntersectionLoader()
        }
    },
    watch: {
        // This only fires navigating from a list page, to another list page
        type(newType) {
            if(!postTypes.includes(newType)) return console.warn('Type not valid...')

            // Ignore types with presorts so the sortBy watcher can handle them
            const ignore = ['event', 'exhibition', 'artist']
            if(ignore.includes(newType)) return
            this.initPostList()
        },
        sortBy(newSort) {
            if(!Object.values(sortTypes).includes(newSort)) return
            this.initPostList()
        }
    },
    mounted() {
        // This only fires navigating from a non-list page > list page
        this.initPostList()
    },
    beforeUnmount() {
        this.unsetIntersectionLoader()
    }
}
</script>

<style lang="postcss">
// prettier-ignore
@import '../sss/variables.sss'
@import '../sss/theme.sss'
.page--list 
    /* Puts the aside bar on top */
    flex-direction: column-reverse !important
    article
        > header
            padding: 1em
            > h1
                margin: 0
            > .content
                padding: 0
                width: 100%
            .title.f-row
                /* flex-direction: column  */
                justify-content: flex-start

        > footer
            padding: $ms-0

        /* posts not grid list */
        .posts
            list-style: none
            grid-gap: $ms--2
            .post
                width: 100%
                &.inbetween
                    grid-column: span 2
                    background: white
                    padding: 0.3em 0
                    font-size: 1.3em
                    > p
                        margin: 0

        /* posts in grid list */
        .posts.is-grid
            width: 100%
            min-width: 185px
            display: grid
            grid-template-columns: repeat(2, 1fr)
            align-items: start
            /* This is important for how the grid lines up to the page */
            justify-content: right 
            .post 
                min-width: 177px
                img
                    width: 100%

@media (min-width: $medium)
    .page--list
        &.f-col
            flex-direction: row !important
        > article
            margin: 0 $ms--2 0 0
            header 
                .title.f-row
                    flex-direction: row 
                    justify-content: center

            .posts.is-grid
                grid-template-columns: repeat(3, 1fr)
                .post.inbetween
                    grid-column: span 3
</style>
