Przeglądaj źródła

Add placeholder loading components

sbkwgh 8 lat temu
rodzic
commit
8b524e979e

+ 4 - 42
src/components/ScrollLoad.vue

@@ -1,26 +1,6 @@
 <template>
 	<div class='scroll_load'>
-		<div class='scroll_load__button' :class='{"scroll_load__button--hidden": !showPrevious}'>
-			<loading-button
-				class='button'
-				:loading='loading'
-				:dark='true'
-				@click='$emit("loadPrevious")'
-			>
-				Load previous {{message}}
-			</loading-button>
-		</div>
 		<slot></slot>
-		<div class='scroll_load__button' :class='{"scroll_load__button--hidden": !showNext}'>
-			<loading-button
-				class='button'
-				:loading='loading'
-				:dark='true'
-				@click='$emit("loadNext")'
-			>
-				Load more {{message}}
-			</loading-button>
-		</div>
 	</div>
 </template>
 
@@ -30,17 +10,17 @@
 
 	export default {
 		name: 'ScrollLoad',
-		props: ['loading', 'showNext', 'showPrevious', 'message'],
+		props: ['loading'],
 		components: {
 			LoadingButton
 		},
 		methods: {
 			onScroll (e) {
-				if(document.body.scrollHeight - document.body.scrollTop - 150 <= document.body.clientHeight) {
+				if(document.body.scrollHeight - document.body.scrollTop - 300 <= document.body.clientHeight) {
 					if(!this.loading) {
 						this.$emit('loadNext')
 					}
-				} else if(document.body.scrollTop <= 200) {
+				} else if(document.body.scrollTop <= 150) {
 					if(!this.loading) {
 						this.$emit('loadPrevious')
 					}
@@ -59,22 +39,4 @@
 			window.removeEventListener('scroll', this.throttledOnScroll)
 		}
 	}
-</script>
-
-<style lang='scss' scoped>
-	.scroll_load {
-		@at-root #{&}__button {
-			max-height: 4rem;
-			overflow: hidden;
-			margin-top: 2rem;
-			text-align: center;
-			transition: all 0.2s;
-
-			@at-root #{&}--hidden {
-				max-height: 0;
-				margin-top: 0;
-				transition: all 0.2s;
-			}
-		}
-	}
-</style>
+</script>

+ 93 - 0
src/components/ThreadPostPlaceholder.vue

@@ -0,0 +1,93 @@
+<template>
+	<div
+		class='post_placeholder'
+	>
+		<div class='post_placeholder__meta_data'>
+			<div class='post_placeholder__avatar_icon'></div>
+			<div class='post_placeholder__bar post_placeholder__bar--thin post_placeholder__bar--33'></div>
+		</div>
+		<div class='post_placeholder__content'>
+			<div class='post_placeholder__bar post_placeholder__bar--58'></div>
+			<div class='post_placeholder__bar post_placeholder__bar--66'></div>
+			<div class='post_placeholder__bar post_placeholder__bar--50'></div>
+		</div>
+	</div>
+</template>
+
+<script>
+	export default { name: 'ThreadPostPlaceholder' }
+</script>
+
+<style lang='scss' scoped>
+	@import '../assets/scss/variables.scss';
+
+	@keyframes flash {
+		0% {
+			background-color: $color__gray--darker;
+		}
+		50% {
+			background-color: $color__lightgray--darkest;
+		}
+		75% {
+			background-color: $color__gray--primary;
+		}
+		to {
+			background-color: $color__gray--darker;
+		}
+	}
+
+	@mixin flash {
+		animation-name: flash;
+		animation-duration: 1s;
+		animation-iteration-count: infinite;
+		animation-timing-function: linear;
+	}
+
+	.post_placeholder {
+		position: relative;
+		border-bottom: thin solid $color__gray--primary;
+		transition: background-color 0.5s;
+		margin: 0.5rem 0;
+
+		@at-root #{&}--last {
+			padding-bottom: 0.5rem;
+			border-bottom: thin solid $color__gray--primary;
+		}
+
+		@at-root #{&}__meta_data {
+			display: flex;
+			padding-top: 0.75rem;
+			position: relative;
+			margin-left: 4rem;
+		}
+		@at-root #{&}__avatar_icon {
+			position: absolute;
+			left: -4rem;
+			height: 3rem;
+			width: 3rem;
+			border-radius: 100%;
+			background-color: $color__gray--primary;
+
+			@include flash;
+		}
+		@at-root #{&}__bar {
+			background-color: $color__gray--primary;
+			height: 1.5rem;
+			width: 75%;
+			margin-bottom: 0.5rem;
+
+			@include flash;
+
+			@at-root #{&}--thin {
+				height: 1rem;
+			}
+			@at-root #{&}--33 { width: 33%; }
+			@at-root #{&}--58 { width: 58%; }
+			@at-root #{&}--66 { width: 66%; }
+			@at-root #{&}--50 { width: 50%; }
+		}
+		@at-root #{&}__content {
+			padding: 0.5rem 0 0.5rem 4rem;
+		}
+	}
+</style>

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

@@ -25,13 +25,14 @@
 		</input-editor>
 		<div class='posts'>
 			<scroll-load
-				:loading='loadingPosts'
-				:showNext='$store.state.thread.nextURL !== null'
-				:showPrevious='$store.state.thread.previousURL !== null'
-				message='posts'
 				@loadNext='loadNextPosts'
 				@loadPrevious='loadPreviousPosts'
 			>
+				<thread-post-placeholder
+					v-if='$store.state.thread.loadingPosts === "previous"'
+					v-for='n in $store.state.thread.previousPostsCount'
+				>
+				</thread-post-placeholder>
 				<thread-post
 					v-for='(post, index) in posts'
 					@reply='replyUser'
@@ -42,6 +43,11 @@
 					:class='{"post--last": index === posts.length-1}'
 					ref='posts'
 				></thread-post>
+				<thread-post-placeholder
+					v-if='$store.state.thread.loadingPosts === "next"'
+					v-for='n in $store.state.thread.nextPostsCount'
+				>
+				</thread-post-placeholder>
 			</scroll-load>
 		</div>
 	</div>
@@ -51,6 +57,7 @@
 	import InputEditor from '../InputEditor'
 	import ScrollLoad from '../ScrollLoad'
 	import ThreadPost from '../ThreadPost'
+	import ThreadPostPlaceholder from '../ThreadPostPlaceholder'
 
 	import throttle from 'lodash.throttle'
 	import AjaxErrorHandler from '../../assets/js/errorHandler'
@@ -60,7 +67,8 @@
 		components: {
 			InputEditor,
 			ScrollLoad,
-			ThreadPost
+			ThreadPost,
+			ThreadPostPlaceholder
 		},
 		data () {
 			return {
@@ -84,8 +92,7 @@
 					this.$store.commit('setThreadEditorValue', val)
 				}
 			},
-			editorState () { return this.$store.state.thread.editor.show },
-			loadingPosts () { return this.$store.state.thread.loadingPosts },
+			editorState () { return this.$store.state.thread.editor.show }
 		},
 		methods: {
 			showEditor () {
@@ -132,7 +139,7 @@
 				this.$router.push({ params: { post_number: number } })
 				this.loadInitialPosts()
 			},
-			highlightPost (postNumber) {
+			scrollTo (postNumber, cb) {
 				for(var i = 0; i < this.posts.length; i++) {
 					let post = this.posts[i]
 
@@ -142,16 +149,21 @@
 							let header = this.$refs.title.getBoundingClientRect().height
 							window.scrollTo(0, postTop - header - 32)
 
-							this.highlightedPostIndex = i
-							
-							if(this.highlightedPostIndex === i) {
-								setTimeout(() => this.highlightedPostIndex = null, 3000)
-							}
+							if(cb) cb(i, post)
 						})
 
 						break;
 					}
 				}
+			},
+			highlightPost (postNumber) {
+				this.scrollTo(postNumber, (i) => {
+					this.highlightedPostIndex = i
+					
+					if(this.highlightedPostIndex === i) {
+						setTimeout(() => this.highlightedPostIndex = null, 3000)
+					}
+				})
 			}
 		},
 		watch: {

+ 10 - 3
src/components/routes/UserPosts.vue

@@ -3,9 +3,7 @@
 		<div class='user_posts__title'>Posts by {{username}}</div>
 		<scroll-load
 			:loading='loadingPosts'
-			:showNext='nextURL !== null'
 			@loadNext='loadNewPosts'
-			message='posts'
 			v-if='sortedPosts.length'
 		>
 			<thread-post
@@ -14,6 +12,10 @@
 				:show-thread='true'
 				:class='{"post--last": index === posts.length-1}'
 			></thread-post>
+			<thread-post-placeholder
+				v-if='loadingPosts'
+				v-for='n in nextPostsCount'
+			>
 		</scroll-load>
 		<template v-else>This user hasn't posted anything yet</template>
 	</div>
@@ -22,6 +24,7 @@
 <script>
 	import ScrollLoad from '../ScrollLoad'
 	import ThreadPost from '../ThreadPost'
+	import ThreadPostPlaceholder from '../ThreadPostPlaceholder'
 
 	import AjaxErrorHandler from '../../assets/js/errorHandler'
 
@@ -30,12 +33,14 @@
 		props: ['username'],
 		components: {
 			ThreadPost,
-			ScrollLoad
+			ScrollLoad,
+			ThreadPostPlaceholder
 		},
 		data () {
 			return {
 				posts: [],
 				loadingPosts: false,
+				nextPostsCount: 0,
 				nextURL: ''
 			}
 		},
@@ -63,6 +68,7 @@
 
 						this.posts.push(...filteredPosts)
 						this.nextURL = res.data.meta.nextURL
+						this.nextPostsCount = res.data.meta.nextPostsCount
 					})
 					.catch((e) => {
 						this.loadingPosts = false
@@ -77,6 +83,7 @@
 				.then(res => {
 					this.posts = res.data.Posts
 					this.nextURL = res.data.meta.nextURL
+					this.nextPostsCount = res.data.meta.nextPostsCount
 				})
 				.catch(AjaxErrorHandler(this.$store))
 		}

+ 25 - 5
src/store/modules/thread.js

@@ -14,7 +14,9 @@ const state = {
 	},
 	loadingPosts: false,
 	nextURL: '',
-	previousURL: ''
+	previousURL: '',
+	nextPostsCount: 10,
+	previousPostsCount: 0
 }
 
 const getters = {
@@ -65,6 +67,9 @@ const actions = {
 				commit('setThread', res.data)
 				commit('setNextURL', res.data.meta.nextURL)
 				commit('setPreviousURL', res.data.meta.previousURL)
+				commit('setNextURL', res.data.meta.nextURL)
+				commit('setPreviousURL', res.data.meta.previousURL)
+				commit('setPostCounts', res.data.meta)
 				commit('setPosts', res.data.Posts)
 
 				if(postNumber) {
@@ -75,11 +80,11 @@ const actions = {
 	loadPostsAsync ({ state, commit, rootState }, { vue, previous }) {
 		let URL
 
-		commit('setLoadingPostsState', true)
-
 		if(previous) {
+			commit('setLoadingPostsState', 'previous')
 			URL = state.previousURL
 		} else {
+			commit('setLoadingPostsState', 'next')
 			URL = state.nextURL
 		}
 
@@ -93,17 +98,28 @@ const actions = {
 					let filteredPosts =
 						res.data.Posts.filter(p => !currentPostsIds.includes(p.id))
 
+					commit('setLoadingPostsState', false)
+
 					if(previous) {
+						let last = filteredPosts.slice(-1)[0]
+
 						commit('prependPosts', filteredPosts)
 						commit('setPreviousURL', res.data.meta.previousURL)
+						
+						if(last) {
+							vue.scrollTo(last.postNumber)
+						}
 					} else {
 						commit('addPost', filteredPosts)
 						commit('setNextURL', res.data.meta.nextURL)
 					}
 
-					commit('setLoadingPostsState', false)
+					commit('setPostCounts', res.data.meta)
+				})
+				.catch((e) => {
+					console.log(e)
+					AjaxErrorHandler(vue.$store)
 				})
-				.catch(AjaxErrorHandler(vue.$store))
 		}
 	}
 }
@@ -160,6 +176,10 @@ const mutations = {
 	},
 	setPreviousURL (state, URL) {
 		state.previousURL = URL
+	},
+	setPostCounts (state, meta) {
+		state.previousPostsCount = meta.previousPostsCount
+		state.nextPostsCount = meta.nextPostsCount
 	}
 }