frontend/src/views/Forums.vue

286 lines
8.0 KiB
Vue

<template>
<div id="forums">
<section class="section">
<div class="container">
<div class="columns is-centered">
<div class="column is-3" v-if="!loadingCategory && categories.length">
<router-link to="/forums/create">
<b-button type="is-info">Create Thread</b-button>
</router-link>
<br /><br />
<b-menu>
<b-menu-list label="Categories">
<b-menu-item label="All" tag="router-link" to="/forums/all" :active="selectedCategory === 'ALL'"></b-menu-item> <b-menu-item :label="category.name" v-for="(category, $index) in categories" :active="selectedCategory === category.value"
:key="'category-link-' + $index" tag="router-link" :to="'/forums/' + category.value"></b-menu-item>
</b-menu-list>
</b-menu>
</div>
<div class="column is-3" v-if="loadingCategory">
<b-skeleton></b-skeleton>
<b-skeleton></b-skeleton>
<b-skeleton></b-skeleton>
<b-skeleton></b-skeleton>
</div>
<div
class="column is-3"
v-if="!loadingCategory && !categories.length"
>
<NoItems connection="true"></NoItems>
</div>
<div class="column is-9" v-if="!threads.length && !loadingThreads">
<div class="box">
<NoItems type="forum threads"></NoItems>
</div>
</div>
<div class="column is-9" v-if="threads.length">
<div class="content">
<article
class="thread_display box"
v-for="thread in threads"
:key="'thread-' + thread.id"
@click="goToThread(thread)"
>
<div style="width: calc(100% - 3rem)">
<div class="thread_display__header">
<span class="thread_display__name">
{{ thread.name }}
</span>
<div class="thread_display__meta_bar">
<div>
By
<span class="thread_display__username" ref="username">
{{ thread.User.username }}
</span>
in
<span class="thread_display__category" ref="category">
{{ thread.Category.name }}
</span>
&middot;
<span class="thread_display__date">
{{ thread.createdAt | formatDate }}
</span>
</div>
</div>
</div>
<div class="thread_display__replies_bar">
<div
class="thread_display__latest_reply"
v-if="thread.Posts.length === 2"
>
<font-awesome-icon :icon="['fa', 'reply']" fixed-width />
<span class="thread_display__latest_reply__text"
>Latest reply by &nbsp;</span
>
<span class="thread_display__username">{{
replyUsername
}}</span>
&middot;
<span class="thread_display__date">{{
thread.Posts[1].createdAt | formatDate
}}</span>
</div>
<span title="Replies to thread" v-if="thread.Posts[0]" class="is-hidden-mobile">
Replies: {{ thread.postsCount - 1 }}
</span>
<span title="Replies to thread" class="is-hidden-mobile" v-else> Replies: 0 </span>
</div>
<div class="thread_display__content">
{{ thread.Posts[0].plainText }}
</div>
</div>
</article>
</div>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
import AjaxErrorHandler from ".././assets/js/errorHandler";
import NoItems from "../components/NoItems";
export default {
name: "Forums",
props: ["username"],
components: {
NoItems,
},
data() {
return {
categories: [],
loadingCategory: true,
loadingThreads: true,
threads: [],
selectedCategory: "ALL",
};
},
methods: {
goToThread(thread) {
this.$router.push("/forums/thread/" + thread.id);
},
getThreads(initial) {
if (this.nextURL === null && !initial) return;
this.loadingThreads = true;
this.axios
.get(
process.env.VUE_APP_API_ENDPOINT +
process.env.VUE_APP_API_VERSION +
"/" +
"forums/category/" +
this.selectedCategory
)
.then((res) => {
this.loadingThreads = 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.loadingThreads = false;
AjaxErrorHandler(this.$store)(e);
});
},
},
watch: {
selectedCategory(newValue) {
this.$router.push("/forums/" + newValue.toLowerCase());
},
$route() {
this.selectedCategory = this.$route.path.split("/")[2].toUpperCase();
this.getThreads(true);
},
},
mounted() {
this.axios
.get(
process.env.VUE_APP_API_ENDPOINT +
process.env.VUE_APP_API_VERSION +
`/` +
"forums/category"
)
.then((res) => {
this.categories = res.data;
this.loadingCategory = false;
})
.catch((e) => {
this.loadingCategory = false;
let invalidId = e.response.data.errors.find((error) => {
return error.name === "accountDoesNotExist";
});
if (invalidId) {
this.$store.commit("set404Page", true);
} else {
AjaxErrorHandler(this.$store)(e);
}
});
this.getThreads(true);
},
};
</script>
<style lang='scss' scoped>
@import "../assets/scss/variables";
.thread_display {
cursor: pointer;
display: flex;
margin-bottom: 1rem;
padding: 0.75rem;
position: relative;
transition: background-color 0.2s, box-shadow 0.2s;
&:hover {
@extend .shadow_border--hover;
}
@at-root #{&}__icon {
margin-right: 0.5rem;
}
@at-root #{&}__username,
#{&}__category,
#{&}__date {
color: $color--text__primary;
}
@at-root #{&}__header {
display: flex;
justify-content: space-between;
}
@at-root #{&}__name {
font-weight: 500;
font-size: 1.25rem;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.5rem;
height: 1.5rem;
}
@at-root #{&}__meta_bar {
color: $color--gray__darkest;
flex-shrink: 0;
line-height: 1.5rem;
height: 1.5rem;
}
@at-root #{&}__replies_bar {
display: flex;
justify-content: space-between;
}
@at-root #{&}__latest_reply {
color: $color--text__secondary;
.fa {
color: $color--text__primary;
font-size: 0.75rem;
}
}
@at-root #{&}__replies {
width: 4rem;
text-align: right;
}
@at-root #{&}__content {
margin-top: 0.5rem;
word-break: break-all;
}
}
@media (max-width: 420px) {
.thread_display {
@at-root #{&}__header {
flex-direction: column;
}
@at-root #{&}__meta_bar {
font-size: 0.9rem;
margin-bottom: 0.25rem;
}
@at-root #{&}__replies_bar {
position: relative;
left: -3.25rem;
width: calc(100% + 3.25rem);
}
@at-root #{&}__latest_reply {
.fa {
margin-right: 0.25rem;
}
@at-root #{&}__text {
display: none;
}
}
}
}
</style>