Ver Fonte

Add scroll load functionality to thread page

sbkwgh há 8 anos atrás
pai
commit
1d66afb89c

+ 8 - 2
src/components/LoadingButton.vue

@@ -5,7 +5,7 @@
 		@click='event("click")'
 		@keydown='event("keydown")'
 	>
-		<div class='loading_button__icon'>
+		<div class='loading_button__icon' :class='{"loading_button__icon--dark": dark}'>
 			<span></span>
 			<span></span>
 			<span></span>
@@ -19,7 +19,7 @@
 <script>
 	export default {
 		name: 'LoadingButton',
-		props: ['loading'],
+		props: ['loading', 'dark'],
 		methods: {
 			event (name) {
 				if(this.loading) {
@@ -62,6 +62,12 @@
 
 			pointer-events: none;
 
+			@at-root #{&}--dark {
+				span {
+					background-color: $color__darkgray--primary !important;
+				}
+			}
+
 			span {
 				height: 0.5rem;
 				width: 0.5rem;

+ 66 - 0
src/components/ScrollLoad.vue

@@ -0,0 +1,66 @@
+<template>
+	<div class='scroll_load'>
+		<slot></slot>
+		<div class='scroll_load__button' :class='{"scroll_load__button--hidden": !show}'>
+			<loading-button
+				class='button'
+				:loading='loading'
+				:dark='true'
+				@click='$emit("load")'
+			>
+				Load more posts...
+			</loading-button>
+		</div>
+	</div>
+</template>
+
+<script>
+	import LoadingButton from './LoadingButton'
+	import throttle from 'lodash.throttle'
+
+	export default {
+		name: 'ScrollLoad',
+		props: ['loading', 'show'],
+		components: {
+			LoadingButton
+		},
+		methods: {
+			onScroll (e) {
+				if(document.body.scrollHeight - document.body.scrollTop - 150 <= document.body.clientHeight) {
+					if(!this.loading) {
+						this.$emit('load')
+					}
+				}
+			}
+		},
+		computed: {
+			throttledOnScroll () {
+				return throttle(this.onScroll, 200)
+			}
+		},
+		created () {
+			window.addEventListener('scroll', this.throttledOnScroll)
+		},
+		destroyed () {
+			window.removeEventListener('scroll', this.throttledOnScroll)
+		}
+	}
+</script>
+
+<style lang='scss' scoped>
+	.scroll_load {
+		@at-root #{&}__button {
+			max-height: 4rem;
+			overflow: hidden;
+			margin-bottom: 2rem;
+			text-align: center;
+			transition: all 0.2s;
+
+			@at-root #{&}--hidden {
+				max-height: 0;
+				margin-bottom: 0;
+				transition: all 0.2s;
+			}
+		}
+	}
+</style>

+ 5 - 0
src/components/ThreadPost.vue

@@ -90,6 +90,11 @@
 			background-color: $color__lightgray--darkest;
 		}
 
+		@at-root #{&}--last {
+			padding-bottom: 0.5rem;
+			border-bottom: thin solid $color__gray--primary;
+		}
+
 		@at-root #{&}__meta_data {
 			display: flex;
 			padding-top: 0.75rem;

+ 25 - 15
src/components/routes/Thread.vue

@@ -24,21 +24,29 @@
 		>
 		</input-editor>
 		<div class='posts'>
-			<thread-post
-				v-for='(post, index) in posts'
-				@reply='replyUser'
-				@goToPost='goToPost'
-				:post='post'
-				:show-reply='true'
-				:highlight='highlightedPostIndex === index'
-				ref='posts'
-			></thread-post>
+			<scroll-load
+				:loading='loadingPosts'
+				:show='$store.state.thread.nextURL !== null'
+				@load='loadNewPosts'
+			>
+				<thread-post
+					v-for='(post, index) in posts'
+					@reply='replyUser'
+					@goToPost='goToPost'
+					:post='post'
+					:show-reply='true'
+					:highlight='highlightedPostIndex === index'
+					:class='{"post--last": index === posts.length-1}'
+					ref='posts'
+				></thread-post>
+			</scroll-load>
 		</div>
 	</div>
 </template>
 
 <script>
 	import InputEditor from '../InputEditor'
+	import ScrollLoad from '../ScrollLoad'
 	import ThreadPost from '../ThreadPost'
 
 	import throttle from 'lodash.throttle'
@@ -48,6 +56,7 @@
 		name: 'Thread',
 		components: {
 			InputEditor,
+			ScrollLoad,
 			ThreadPost
 		},
 		data () {
@@ -72,7 +81,8 @@
 					this.$store.commit('setThreadEditorValue', val)
 				}
 			},
-			editorState () { return this.$store.state.thread.editor.show }
+			editorState () { return this.$store.state.thread.editor.show },
+			loadingPosts () { return this.$store.state.thread.loadingPosts },
 		},
 		methods: {
 			showEditor () {
@@ -104,6 +114,9 @@
 			addPost () {
 				this.$store.dispatch('addPostAsync', this);
 			},
+			loadNewPosts () {
+				this.$store.dispatch('loadNewPostsAsync', this);
+			},
 			goToPost (postId) {
 				for(var i = 0; i < this.posts.length; i++) {
 					let post = this.posts[i]
@@ -143,7 +156,8 @@
 			this.axios
 				.get('/api/v1/thread/' + this.$route.params.id)
 				.then(res => {
-					this.$store.commit('setThreadName', res.data.name)
+					this.$store.commit('setThread', res.data)
+					this.$store.commit('setNextURL', res.data.meta.nextURL)
 					this.$store.commit('setPosts', res.data.Posts)
 				}).catch(AjaxErrorHandler(this.$store))
 		}
@@ -189,9 +203,5 @@
 
 	.posts {
 		width: 80%;
-
-		&:last-child {
-			border-bottom: thin solid $color__gray--primary;
-		}
 	}
 </style>

+ 40 - 4
src/store/modules/thread.js

@@ -2,6 +2,7 @@ import AjaxErrorHandler from '../../assets/js/errorHandler'
 
 const state = {
 	thread: '',
+	threadId: undefined,
 	posts: [],
 	reply: {
 		username: '',
@@ -10,7 +11,9 @@ const state = {
 	editor: {
 		show: false,
 		value: ''
-	}
+	},
+	loadingPosts: false,
+	nextURL: ''
 }
 
 const getters = {
@@ -46,6 +49,28 @@ const actions = {
 				});
 			})
 			.catch(AjaxErrorHandler(vue.$store))
+	},
+	loadNewPostsAsync ({ state, commit, rootState }, vue) {
+		commit('setLoadingPostsState', true)
+
+		let nextURL = state.nextURL
+
+		if(nextURL === null) {
+			commit('setLoadingPostsState', false)
+		} else {
+			vue.axios
+				.get(nextURL)
+				.then(res => {
+					let currentPostsIds = state.posts.map(p => p.id)
+					let filteredPosts =
+						res.data.Posts.filter(p => !currentPostsIds.includes(p.id))
+
+					commit('addPost', filteredPosts)
+					commit('setNextURL', res.data.meta.nextURL)
+					commit('setLoadingPostsState', false)
+				})
+				.catch(AjaxErrorHandler(vue.$store))
+		}
 	}
 }
 
@@ -55,7 +80,11 @@ const mutations = {
 		state.reply.id = payload.id;
 	},
 	addPost (state, post) {
-		state.posts.push(post);
+		if(Array.isArray(post)) {
+			state.posts.push(...post)
+		} else {
+			state.posts.push(post)
+		}
 	},
 	addReplyBubble (state, post) {
 		let repliedToPost = {}, index
@@ -79,11 +108,18 @@ const mutations = {
 	setThreadEditorState (state, value) {
 		state.editor.show = value
 	},
+	setLoadingPostsState (state, value) {
+		state.loadingPosts = value
+	},
 	setPosts (state, value) {
 		state.posts = value
 	},
-	setThreadName (state, value) {
-		state.thread = value
+	setThread (state, obj) {
+		state.thread = obj.name
+		state.threadId = obj.id
+	},
+	setNextURL (state, URL) {
+		state.nextURL = URL
 	}
 }