Procházet zdrojové kódy

Hook up to server categories and new thread

sbkwgh před 8 roky
rodič
revize
d951b81490

+ 26 - 0
src/App.vue

@@ -108,6 +108,7 @@
 	import mapGetters from 'vuex'
 
 	import AjaxErrorHandler from './assets/js/errorHandler'
+	let { addFlexBoxChildren } = require('./assets/js/flexBoxGridCorrect')
 
 	export default {
 		name: 'app',
@@ -164,6 +165,23 @@
 			showAccountTab : {
 				get (val) { return this.$store.state.accountTabs },
 				set (index) { this.$store.commit('setAccountTabs', index) }
+			},
+			categories() {
+				return this.$store.state.meta.categories
+			}
+		},
+		watch: {
+			$route (to) {
+				if(to.path === '/') {
+					setTimeout(() => {
+						addFlexBoxChildren('.index_categories', 'index_category');
+					}, 50);
+				}
+			},
+			categories () {
+				setTimeout(() => {
+					addFlexBoxChildren('.index_categories', 'index_category');
+				}, 50);
 			}
 		},
 		methods: {
@@ -270,6 +288,14 @@
 		created () {
 			this.axios.get('/api/v1/settings')
 				.then(res => {
+					let usernameCookie = document.cookie
+						.split(';')
+						.map(c => c.split('='))
+						.filter(pair => pair[0].trim() === 'username')
+						.map(pair => pair[1])[0]
+
+					if(usernameCookie) this.$store.commit('setUsername', usernameCookie)
+
 					this.$store.commit('setForumName', res.data.forumName)
 				}).catch(err => {
 					if(err.response.data.errors[0].name === 'noSettings') {

+ 1 - 1
src/assets/js/errorHandler.js

@@ -3,7 +3,7 @@ module.exports = function(vuex) {
 		let errors = []
 
 		if(res.response === undefined) {
-			errors.push('It looks like you\'re offline')
+			errors.push('An error occured. Try again later')
 		} else {
 			res.response.data.errors.forEach(error => {
 				let param = error.parameter

+ 2 - 2
src/assets/js/flexBoxGridCorrect.js

@@ -78,7 +78,7 @@ var onResize = function(parentString, childClass) {
 	});
 }
 
-export {
-	onResize as default,
+module.exports = {
+	onResize,
 	addFlexBoxChildren
 }

+ 7 - 2
src/components/SelectButton.vue

@@ -1,9 +1,12 @@
 <template>
 	<div class='select_button'>
-		<div class='button' @click='toggleMenu'>
+		<div class='button' @click='toggleMenu' v-if='options.length'>
 			{{options[selectedIndex].name}}
 			<span class='button__icon fa fa-fw' :class='[hideMenu ? "fa-caret-down" : "fa-caret-up"]'></span>
 		</div>
+		<div class='button' v-else>
+			No options
+		</div>
 		<div class='select_button__options' :class='{"select_button__options--hidden": hideMenu}'>
 			<div
 				v-for='(option, index) in options'
@@ -76,6 +79,7 @@
 			border: 0.125rem solid $color__gray--primary;
 			margin-top: -0.125rem;
 			max-height: 20rem;
+			box-shadow: 0 0.12rem 0.125rem rgba(0,0,0,0.125);
 
 			transition: max-height 0.4s ease-out;
 
@@ -83,9 +87,10 @@
 
 			@at-root #{&}--hidden {
 				max-height: 0;
+				box-shadow: none;
 				border-color: transparent;
 				background-color: transparent;
-				transition: max-height 0.2s ease-out, border-color 0s ease-in 0.19s, background-color 0s ease-in 0.19s;
+				transition: max-height 0.2s ease-out, box-shadow 0.2s, border-color 0s ease-in 0.19s, background-color 0s ease-in 0.19s;
 			}
 		}
 

+ 36 - 11
src/components/routes/Category.vue

@@ -5,7 +5,7 @@
 				<select-button style='margin-right: 1rem' v-model='selectedCategory' :options='categories'></select-button>
 				<select-options :options='filterOptions' v-model='selectedFilterOption'></select-options>
 			</div>
-			<button class='button' @click='$router.push("/thread/new")'>Post new thread</button>
+			<button class='button' v-if='this.$store.state.username' @click='$router.push("/thread/new")'>Post new thread</button>
 		</div>
 		<table class='threads'>
 			<colgroup>
@@ -24,12 +24,12 @@
 			</thead>
 			<tbody>
 				<tr class='thread' v-for='thread in filteredThreads' @click='navigateToThread(thread.slug, thread.id)'>
-					<td>{{thread.title}}</td>
+					<td>{{thread.name}}</td>
 					<td>
-						<div>{{thread.latestPostUser}}</div>
-						<div>{{thread.latestPostDate | formatDate('time|date', ' - ') }}</div>
+						<div>{{thread.Posts[0].content | stripTags | truncate(100)}}</div>
+						<div>{{thread.Posts[0].createdAt | formatDate('time|date', ' - ') }}</div>
 					</td>
-					<td>{{thread.category}}</td>
+					<td>{{thread.Category.name}}</td>
 					<td>{{thread.replies}}</td>
 				</tr>
 				<tr class='thread' v-if='!filteredThreads.length' colspan='4'>
@@ -45,6 +45,8 @@
 	import TabView from '../TabView'
 	import SelectOptions from '../SelectOptions'
 
+	import AjaxErrorHandler from '../../assets/js/errorHandler'
+
 	export default {
 		name: 'index',
 		components: {
@@ -60,7 +62,6 @@
 					{name: 'No replies', value: 'NO_REPLIES'}
 				],
 				selectedFilterOption: 'NEW',
-				selectedCategory: this.$route.params.category.toUpperCase(),
 				threads: []
 			}
 		},
@@ -74,21 +75,24 @@
 				});
 
 				return this.threads.filter(thread => {
-					return (thread.category === this.selectedCategory) || (this.selectedCategory === 'ALL');
+					return (thread.Category.value === this.selectedCategory) || (this.selectedCategory === 'ALL');
 				}).map(thread => {
 					var _thread = Object.assign({}, thread);
-					_thread.category = categories[thread.category];
+					_thread.category = categories[thread.Category.value];
 
 					return _thread;
 				}).sort((a, b) => {
 					if(filter === 'NEW') {
-						return a.latestPostDate - b.latestPostDate;
+						let aDate = new Date(a.Posts[0].createdAt)
+						let bDate = new Date(b.Posts[0].createdAt)
+
+						return aDate - bDate;
 					} else if(filter === 'MOST_ACTIVE') {
-						return b.replies - a.replies;
+						return 0 //b.replies - a.replies;
 					}
 				}).filter(thread => {
 					if(filter === 'NO_REPLIES') {
-						return !thread.replies;
+						return 0//!thread.replies;
 					} else {
 						return true;
 					}
@@ -96,6 +100,14 @@
 			},
 			categories () {
 				return this.$store.state.meta.categories
+			},
+			selectedCategory: {
+				set (val) {
+					this.$store.commit('setSelectedCategory', val)
+				},
+				get () {
+					return this.$store.state.category.selectedCategory
+				}
 			}
 		},
 		methods: {
@@ -106,7 +118,20 @@
 		watch: {
 			selectedCategory (newValue) {
 				this.$router.push('/category/' + newValue.toLowerCase());
+			},
+			$route () {
+				this.selectedCategory = this.$route.path.split('/')[2].toUpperCase()
 			}
+		},
+		created () {
+			this.selectedCategory = this.$route.path.split('/')[2].toUpperCase()
+
+			this.axios
+				.get('/api/v1/category/' + this.selectedCategory)
+				.then(res => {
+					this.threads = res.data.Threads
+				})
+				.catch(AjaxErrorHandler(this.$store))
 		}
 	}
 </script>

+ 12 - 11
src/components/routes/Index.vue

@@ -9,8 +9,17 @@
 			>
 				<div class='index_category__name'>{{category.name}}</div>
 				<div>
-					<div class='index_category__latest_post'>Latest post here</div>
-					<div class='index_category__latest_post_date'>19:53</div>
+					<div class='index_category__latest_post'>
+						<template v-if='category.Threads && category.Threads.length'>
+							{{category.Threads[0].Posts[0].content | stripTags | truncate(100) }}
+						</template>
+						<template v-else>
+							No threads yet
+						</template>
+					</div>
+					<div class='index_category__latest_post_date' v-if='category.Threads && category.Threads.length'>
+						{{category.Threads[0].Posts[0].createdAt | formatDate('time|date', ' - ')}}
+					</div>
 				</div>
 			</div>
 		</div>
@@ -21,16 +30,8 @@
 </template>
 
 <script>
-	import addFlexBoxChildren from '../../assets/js/flexBoxGridCorrect'
-
 	export default {
-		name: 'index',
-		components: {},
-		computed: {},
-		methods: {},
-		mounted () {
-			addFlexBoxChildren('.index_categories', 'index_category');
-		}
+		name: 'index'
 	}
 </script>
 

+ 16 - 7
src/components/routes/Thread.vue

@@ -26,16 +26,16 @@
 		<div class='posts'>
 			<div class='post' v-for='post in posts'>
 				<div class='post__meta_data'>
-					<div class='post__avatar'>{{post.username[0]}}</div>
-					<div class='post__user'>{{post.username}}</div>
-					<span class='fa fa-long-arrow-right fa-fw' v-if='post.replyUsername'></span>
-					<div class='post__reply' v-if='post.replyUsername'>{{post.replyUsername}}</div>
-					<div class='post__date'>{{post.date | formatDate('time|date', ', ')}}</div>
+					<div class='post__avatar'>{{post.User.username[0]}}</div>
+					<div class='post__user'>{{post.User.username}}</div>
+					<span class='fa fa-long-arrow-right fa-fw' v-if='post.ReplyingTo'></span>
+					<div class='post__reply' v-if='post.ReplyingTo'>{{post.ReplyingTo.User.username}}</div>
+					<div class='post__date'>{{post.createdAt | formatDate('time|date', ', ')}}</div>
 				</div>
 				<div class='post__content' v-html='post.content'></div>
 				<div class='post__actions'>
 					<div class='post__action post__share'>Share</div>
-					<div class='post__action post__reply' @click='replyUser("id", post.username)'>Reply</div>
+					<div class='post__action post__reply' @click='replyUser(post.id, post.User.username)'>Reply</div>
 				</div>
 			</div>
 		</div>
@@ -46,6 +46,8 @@
 	import InputEditor from '../InputEditor'
 	import throttle from 'lodash.throttle'
 
+	import AjaxErrorHandler from '../../assets/js/errorHandler'
+
 	export default {
 		name: 'Thread',
 		components: {
@@ -103,7 +105,7 @@
 				this.$store.dispatch('addPostAsync');
 			}
 		},
-		mounted () {
+		created () {
 			var self = this;
 			var setHeader = function() {
 				if(!self.$refs.title) return;
@@ -117,6 +119,13 @@
 
 			setHeader();
 			document.addEventListener('scroll', throttle(setHeader, 200));
+
+			this.axios
+				.get('/api/v1/thread/' + this.$route.params.id)
+				.then(res => {
+					this.$store.commit('setThreadName', res.data.name)
+					this.$store.commit('setPosts', res.data.Posts)
+				}).catch(AjaxErrorHandler(this.$store))
 		}
 	}
 </script>

+ 41 - 4
src/components/routes/ThreadNew.vue

@@ -3,26 +3,39 @@
 		<div class='h1'>Post new thread</div>
 		<span class='select_button_text'>Post in category:</span>
 		<select-button v-model='selectedCategory' :options='categories'></select-button>
+		<div>
+			<span class='select_button_text'>Name of thread:</span>
+			<fancy-input placeholder='Thread name' v-model='name' style='display: inline-block;'></fancy-input>
+		</div>
 		<input-editor v-model='editor' :show='true' :hide-close='true' style='margin-top: 1rem'></input-editor>
-		<button class='button button--green submit'>Post thread</button>
+		<button class='button button--green submit' @click='postThread'>Post thread</button>
 	</div>
 </template>
 
 <script>
 	import InputEditor from '../InputEditor'
+	import FancyInput from '../FancyInput'
 	import SelectButton from '../SelectButton'
 
+	import AjaxErrorHandler from '../../assets/js/errorHandler'
 	
 	export default {
 		name: 'ThreadNew',
 		components: {
 			InputEditor,
-			SelectButton
+			SelectButton,
+			FancyInput
 		},
 		data () {
 			return {
 				selectedCategory: this.$store.state.category.selectedCategory,
-				editor: ''
+				editor: '',
+				name: '',
+
+				errors: {
+					content: '',
+					name: ''
+				}
 			}
 		},
 		computed: {
@@ -30,7 +43,31 @@
 				return this.$store.getters.categoriesWithoutAll
 			}
 		},
-		methods: {}
+		methods: {
+			postThread () {
+				this.axios.post('/api/v1/thread', {
+					name: this.name,
+					category: this.selectedCategory
+				}).then(res => {
+					return this.axios.post('/api/v1/post', {
+						threadId: res.data.id,
+						content: this.editor
+					})
+				}).then(res => {
+					this.$router.push('/thread/' + res.data.Thread.id)
+				}).catch(e => {
+					AjaxErrorHandler(this.$store)(e, (error, errors) => {
+						let param = error.parameter
+
+						if(this.errors[param] !== undefined) {
+							this.error[param] = error.message
+						} else {
+							errors.push(error.message)
+						}
+					})
+				})
+			}
+		}
 	}
 </script>
 

+ 20 - 1
src/main.js

@@ -18,7 +18,7 @@ import AdminDashboard from './components/routes/AdminDashboard'
 import AdminUsers from './components/routes/AdminUsers'
 import AdminSettings from './components/routes/AdminSettings'
 
-import onResize from './assets/js/flexBoxGridCorrect'
+let { onResize } = require('./assets/js/flexBoxGridCorrect.js')
 
 onResize('.index_categories', 'index_category');
 
@@ -43,6 +43,10 @@ const router = new VueRouter({
 })
 
 Vue.filter('formatDate', function (value, format = '', join = ' ') {
+	if(typeof value !== 'object') {
+		value = new Date(value)
+	}
+
 	//Add leading zero if under 10
 	function lz(num) {
 		if(num < 10) {
@@ -68,6 +72,21 @@ Vue.filter('formatDate', function (value, format = '', join = ' ') {
 	return format.split('|').map(formatSegment).join(join);
 });
 
+Vue.filter('stripTags', function (value) {
+	let div = document.createElement('div')
+	div.innerHTML = value
+
+	return div.textContent
+});
+
+Vue.filter('truncate', function (value, length) {
+	if(value <= length) {
+		return value
+	} else {
+		return value.slice(0, length) + '...'
+	}
+});
+
 new Vue({
 	el: '#app',
 	template: '<App/>',

+ 8 - 5
src/store/index.js

@@ -2,6 +2,7 @@ import Vue from 'vue'
 import Vuex from 'vuex'
 
 import thread from './modules/thread'
+import category from './modules/category'
 
 Vue.use(Vuex)
 
@@ -20,14 +21,16 @@ export default new Vuex.Store({
 	},
 	getters: {
 		categoriesWithoutAll (state) {
-			var categories = JSON.parse(JSON.stringify(state.meta.categories));
-			categories.shift();
+			let categories =
+				state.meta.categories
+				.filter(category => category.value !== 'ALL' )
+
 			categories.unshift({
 				name: 'Select a category',
 				disabled: true
-			});
+			})
 
-			return categories;
+			return categories.filter(category => category.value !== 'ALL' )
 		}
 	},
 	mutations: {
@@ -60,5 +63,5 @@ export default new Vuex.Store({
 			}
 		}
 	},
-	modules: { thread }
+	modules: { thread, category }
 })

+ 20 - 0
src/store/modules/category.js

@@ -0,0 +1,20 @@
+const state = {
+	selectedCategory: ''
+}
+
+const getters = {}
+
+const actions = {}
+
+const mutations = {
+	setSelectedCategory (state, val) {
+		state.selectedCategory = val
+	}
+}
+
+export default {
+	state,
+	getters,
+	actions,
+	mutations
+}

+ 6 - 0
src/store/modules/thread.js

@@ -53,6 +53,12 @@ const mutations = {
 	},
 	setThreadEditorState (state, value) {
 		state.editor.show = value
+	},
+	setPosts (state, value) {
+		state.posts = value
+	},
+	setThreadName (state, value) {
+		state.thread = value
 	}
 }