cubash-archive/frontend/src/components/routes/Index.vue

490 lines
12 KiB
Vue

<template>
<div class='route_container'>
<div class='forum_description' v-if='
$store.state.meta.showDescription &&
$store.state.meta.description
'>
{{$store.state.meta.description}}
</div>
<div>
<b-button class="is-info" @click="$router.push('/thread/new')">
{{postNewThreadText}}
</b-button>
<br>
<select-button class="is-hidden-desktop" v-model='selectedCategory' :options='categories'></select-button>
</div>
<br>
<div class='threads_main'>
<div class='threads_main__side_bar box'>
<div class='threads_main__side_bar__title'>
Categories:
</div>
<router-link
v-for='(category, $index) in categories'
:key='"category-link-" + $index'
class='threads_main__side_bar__menu_item'
:class='{"threads_main__side_bar__menu_item--selected": category.value === selectedCategory}'
:to='"/category/" + category.value'
>
<span
class='threads_main__side_bar__menu_item__border'
:style='{"background-color": category.color}'
></span>
<span
class='threads_main__side_bar__menu_item__text'
:style='{
"color": category.value === selectedCategory ? category.color : undefined
}'
>{{category.name}}</span>
</router-link>
<br />
<select-options
:options="filterOptions"
v-model='selectedFilterOption'
>
<button class='button button--thin_text'>
<i class="fas fa-sort-size-up" style='margin-right: 0.25rem;'></i>
Sort
</button>
</select-options>
<br/>
<div class='threads_main__side_bar__title'>
Leaderboard:
</div>
<div style="padding-right: 10px">
<div class="box" v-for='user in users' :key='"user-row" + user.username' v-show="user && !user.hidden">
<header class="content">
{{user.postCount}} <leaderpara v-if="user.postCount === 1">post</leaderpara> <leaderpara v-else>posts</leaderpara>
<p>
<router-link :to='"/user/" + user.username'>{{user.username}}</router-link>
</p>
</header>
</div>
<p name='fade' mode='out-in'>
<center><loading-message key='loading' v-if='loading'></loading-message></center>
<center><div class='overlay_message' v-if='!loading && !users.length'>
Something went wrong while loading the users, check your internet connection, or check the <a href="https://status.troplo.com">Service Status</a>
</div></center></p>
</div>
</div>
<transition name='fade' mode='out-in'>
<div
class='threads_main__threads'
v-if='!threads'
key='loading'
>
<thread-display-placeholder>
</thread-display-placeholder>
</div>
<scroll-load
key='threads'
class='threads_main__threads'
v-else-if='filteredThreads.length'
:loading='loading'
@loadNext='getThreads'
>
<template v-if='loadingNewer'>
<thread-display-placeholder v-for='n in newThreads' :key='"placeholder-upper-" + n'>
</thread-display-placeholder>
</template>
<div class='threads_main__load_new' v-if='newThreads' @click='getNewerThreads'>
Load {{newThreads}} new {{newThreads | pluralize('thread')}}
</div>
<thread-display
v-for='thread in filteredThreads'
:thread='thread'
:key='"thread-display-" + thread.id'
></thread-display>
<template v-if='loading'>
<thread-display-placeholder v-for='n in nextThreadsCount' :key='"placeholder-lower-" + n'>
</thread-display-placeholder>
</template>
</scroll-load>
<div key='no threads' v-else class='threads_main__threads overlay_message'>
<font-awesome-icon :icon='["fa", "exclamation-circle"]' />
No threads or posts.
</div>
</transition>
</div>
</div>
</template>
<script>
import ScrollLoad from '../ScrollLoad'
import ThreadDisplay from '../ThreadDisplay'
import ThreadDisplayPlaceholder from '../ThreadDisplayPlaceholder'
import SelectOptions from '../SelectOptions'
import SelectButton from "@/components/SelectButton";
import AjaxErrorHandler from '../../assets/js/errorHandler'
import logger from '../../assets/js/logger'
export default {
name: 'index',
components: {
ScrollLoad,
ThreadDisplay,
ThreadDisplayPlaceholder,
SelectOptions,
SelectButton
},
data () {
return {offset: 0,
users: [],
filterOptions: [
{name: 'New', value: 'NEW'},
{name: 'Most active', value: 'MOST_ACTIVE'},
{name: 'No replies', value: 'NO_REPLIES'}
],
selectedFilterOption: 'NEW',
nextURL: '',
nextThreadsCount: 0,
loading: false,
threads: '',
newThreads: 0,
loadingNewer: false
}
},
computed: {
filteredThreads () {
var categories = {};
var filter = this.selectedFilterOption
this.$store.state.meta.categories.forEach(category => {
categories[category.value] = category.name;
});
return this.threads.filter(thread => {
return (thread.Category.value === this.selectedCategory) || (this.selectedCategory === 'ALL');
}).map(thread => {
var _thread = Object.assign({}, thread);
_thread.category = categories[thread.Category.value];
return _thread;
}).sort((a, b) => {
if(filter === 'NEW') {
let aDate = new Date(a.Posts[0].createdAt)
let bDate = new Date(b.Posts[0].createdAt)
return bDate - aDate;
} else if(filter === 'MOST_ACTIVE') {
return b.postsCount - a.postsCount;
}
}).filter(thread => {
if(filter === 'NO_REPLIES' && thread.postsCount-1) {
return false
} else {
return true;
}
});
},
categories () {
return this.$store.getters.alphabetizedCategories
},
selectedCategory: {
set (val) {
let name = this.categories.find(c => c.value === val)
this.$store.dispatch('setTitle', name ? name.name : '')
this.$store.commit('setSelectedCategory', val)
},
get () {
return this.$store.state.category.selectedCategory
}
},
postNewThreadText () {
if(this.$store.state.username) {
return 'Post new thread'
} else {
return 'Login to post a thread'
}
}
},
methods: {
threadContent() {
if(this.thread.Posts[0]) {
return this.thread.Posts[0]
} else {
return {id: "0", content: 'Uninitialized Thread', createdAt: "2020-10-10T15:40:51.000Z", updatedAt: "2020-10-10T15:40:51.000Z"}
}
},
fetchLeaderboardData () {
if(this.offset === null) return;
let url = process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `users/leaderboard?
sort=postCount
&order=desc
&offset=0
`;
this.loading = true;
this.axios
.get(url)
.then(res => {
this.users.push(...res.data);
this.loading = /*loading =*/ false;
})
.catch(e => {
AjaxErrorHandler(this.$store)(e);
this.loading = /*loading =*/ false;
});
},
resetLeaderboard () {
this.offset = 0;
this.users = [];
this.fetchLeaderboardData();
},
navigateToThread (slug, id) {
this.$router.push('/thread/' + slug + '/' + id);
},
getThreads (initial) {
if(this.nextURL === null && !initial) return
let URL = process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'forums/category/' + this.selectedCategory
if(!initial) {
URL = this.nextURL || URL
}
this.loading = true
this.axios
.get(URL)
.then(res => {
this.loading = false
if(initial) {
this.threads = res.data.Threads
} else {
this.threads.push(...res.data.Threads)
}
this.nextURL = res.data.meta.nextURL
this.nextThreadsCount = res.data.meta.nextThreadsCount
})
.catch((e) => {
this.loading = false
AjaxErrorHandler(this.$store)(e)
})
},
getNewerThreads () {
this.loadingNewer = true
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'forums/category/' + this.selectedCategory + '?limit=' + this.newThreads)
.then(res => {
this.loadingNewer = false
this.newThreads = 0
this.threads.unshift(...res.data.Threads)
})
.catch((e) => {
this.loadingNewer = false
AjaxErrorHandler(this.$store)(e)
})
}
},
watch: {
selectedCategory (newValue) {
this.$router.push('/category/' + newValue.toLowerCase());
},
$route () {
this.selectedCategory = this.$route.path.split('/')[2].toUpperCase()
this.newThreads = 0
this.getThreads(true)
}
},
created () {
this.selectedCategory = this.$route.path.split('/')[2].toUpperCase()
this.getThreads(true)
this.$socket.emit('join', 'index')
this.$socket.on('new thread', data => {
if(data.value === this.selectedCategory || this.selectedCategory == 'ALL') {
this.newThreads++
}
})
if(this.$route.query.token) {
this.$store.commit('setToken', this.$route.query.token)
this.$store.commit('setAccountTabs', 0)
this.$store.commit('setAccountModalState', true)
}
if(this.$route.query.passkey) {
this.$store.commit('setPassKey', this.$route.query.passkey)
this.$store.commit('setAccountTabs', 0)
this.$store.commit('setAccountModalState', true)
}
this.fetchLeaderboardData()
logger('index')
},
destroyed () {
this.$socket.emit('leave', 'index')
this.$socket.off('new thread')
}
}
</script>
<style lang='scss' scoped>
@import '../../assets/scss/variables.scss';
.button {
margin-bottom: 0.75rem;
height: 3rem;
}
.forum_description {
padding: 1rem;
margin-bottom: 2rem;
border-radius: 0.25rem;
border: thin solid $color__gray--darker;
}
.threads_main {
display: flex;
}
.thread_sorting {
margin-bottom: 1rem;
display: flex;
justify-content: space-between;
@at-root #{&}__display {
padding-right: 0.5rem;
margin-right: 1.25rem;
width: 10rem;
}
@at-root #{&}__add_and_categories {
.select_button {
display: none;
}
}
}
.threads_main__side_bar {
width: 12rem;
height: 0%;
margin-right: 1rem;
border-radius: 0.25rem;
padding: 0.5rem 0 1rem 1rem;
position: sticky;
top: 4.5rem;
@at-root #{&}__title {
cursor: default;
font-weight: 500;
font-size: 1.125rem;
}
@at-root #{&}__menu_item {
cursor: pointer;
margin-top: 0.5rem;
position: relative;
display: grid;
grid-template-columns: 0.75rem auto;
grid-column-gap: 1rem;
text-decoration: none;
background-image: none;
font-weight: normal;
@at-root #{&}__border {
align-self: center;
display: inline-block;
height: 0.9rem;
width: 0.9rem;
border-radius: 0.25rem;
margin-top: 0.25rem;
transition: all 0.2s;
}
@at-root #{&}--selected {
font-weight: 600;
}
}
}
.threads_main__threads {
margin-left: 1rem;
width: calc(100% - 11rem);
}
.threads_main__load_new {
@extend .button;
font-size: 1.25rem;
margin: 0 0 1rem 0;
width: 100%;
font-weight: 300;
}
.thread {
padding: 0.5rem 0;
cursor: default;
text-align: left;
transition: background-color 0.2s;
&:hover {
}
td, th {
padding: 0.3rem 0.5rem;
}
@at-root #{&}--header {
&:hover {
}
th {
font-weight: 400;
padding-bottom: 0.25rem;
}
}
@at-root #{&}__section {
padding: 0 0.5rem;
}
@at-root #{&}__user {
display: inline-block;
}
@at-root #{&}__date {
display: inline-block;
}
}
@media (max-width: $breakpoint--tablet) {
.thread_sorting {
flex-direction: column-reverse;
align-items: left;
@at-root #{&}__add_and_categories {
width: 100%;
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
.select_button {
display: inline-block;
}
}
}
.threads_main__side_bar {
display: none;
}
.threads_main__threads {
width: 100%;
margin-left: 0;
}
}
@media (max-width: $breakpoint--tablet) and (min-width: $breakpoint--phone) {
.route_container {
padding: 1rem 2rem;
}
}
</style>