forked from kaverti/website
484 lines
12 KiB
Vue
484 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.name === selectedCategory}'
|
|
:to='"/category/" + category.name'
|
|
>
|
|
<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.name === 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'
|
|
: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 threads'
|
|
: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>
|
|
</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'},
|
|
{name: 'Most active'},
|
|
{name: '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.name] = category.name;
|
|
});
|
|
return this.threads.filter(thread => {
|
|
return (thread.Category.name === this.selectedCategory) || (this.selectedCategory === 'ALL');
|
|
}).map(thread => {
|
|
var _thread = Object.assign({}, thread);
|
|
_thread.category = categories[thread.Category.name];
|
|
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.replies - a.replies;
|
|
}
|
|
}).filter(thread => {
|
|
if(filter === 'NO_REPLIES' && thread.replies) {
|
|
return false
|
|
} else {
|
|
return true;
|
|
}
|
|
});
|
|
},
|
|
categories () {
|
|
return this.$store.getters.alphabetizedCategories
|
|
},
|
|
selectedCategory: {
|
|
set (val) {
|
|
let name = this.categories.find(c => c.name === 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.name === 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>
|