Refactor reaction-viewer (#4171)
* Refactor reaction-viewer * code style * fix
This commit is contained in:
		
							parent
							
								
									f44ce535fa
								
							
						
					
					
						commit
						d5205d7328
					
				
					 2 changed files with 155 additions and 144 deletions
				
			
		|  | @ -0,0 +1,147 @@ | |||
| <template> | ||||
| <span | ||||
| 	class="reaction" | ||||
| 	:class="{ reacted: note.myReaction == reaction }" | ||||
| 	@click="toggleReaction(reaction)" | ||||
| 	v-if="count > 0" | ||||
| 	v-particle="!isMe" | ||||
| > | ||||
| 	<mk-reaction-icon :reaction="reaction" ref="icon"/> | ||||
| 	<span>{{ count }}</span> | ||||
| </span> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import Icon from './reaction-icon.vue'; | ||||
| import anime from 'animejs'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	props: { | ||||
| 		reaction: { | ||||
| 			type: String, | ||||
| 			required: true, | ||||
| 		}, | ||||
| 		count: { | ||||
| 			type: Number, | ||||
| 			required: true, | ||||
| 		}, | ||||
| 		note: { | ||||
| 			type: Object, | ||||
| 			required: true, | ||||
| 		}, | ||||
| 		canToggle: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: true, | ||||
| 		}, | ||||
| 	}, | ||||
| 	computed: { | ||||
| 		isMe(): boolean { | ||||
| 			return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId; | ||||
| 		}, | ||||
| 	}, | ||||
| 	watch: { | ||||
| 		count() { | ||||
| 			this.anime(); | ||||
| 		}, | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		toggleReaction() { | ||||
| 			if (this.isMe) return; | ||||
| 			if (!this.canToggle) return; | ||||
| 
 | ||||
| 			const oldReaction = this.note.myReaction; | ||||
| 			if (oldReaction) { | ||||
| 				this.$root.api('notes/reactions/delete', { | ||||
| 					noteId: this.note.id | ||||
| 				}).then(() => { | ||||
| 					if (oldReaction !== this.reaction) { | ||||
| 						this.$root.api('notes/reactions/create', { | ||||
| 							noteId: this.note.id, | ||||
| 							reaction: this.reaction | ||||
| 						}); | ||||
| 					} | ||||
| 				}); | ||||
| 			} else { | ||||
| 				this.$root.api('notes/reactions/create', { | ||||
| 					noteId: this.note.id, | ||||
| 					reaction: this.reaction | ||||
| 				}); | ||||
| 			} | ||||
| 		}, | ||||
| 		anime() { | ||||
| 			if (this.$store.state.device.reduceMotion) return; | ||||
| 			if (document.hidden) return; | ||||
| 
 | ||||
| 			this.$nextTick(() => { | ||||
| 				const rect = this.$refs.icon.$el.getBoundingClientRect(); | ||||
| 
 | ||||
| 				const x = rect.left; | ||||
| 				const y = rect.top; | ||||
| 
 | ||||
| 				const icon = new Icon({ | ||||
| 					parent: this, | ||||
| 					propsData: { | ||||
| 						reaction: this.reaction | ||||
| 					} | ||||
| 				}).$mount(); | ||||
| 
 | ||||
| 				icon.$el.style.position = 'absolute'; | ||||
| 				icon.$el.style.zIndex = 100; | ||||
| 				icon.$el.style.top = (y + window.scrollY) + 'px'; | ||||
| 				icon.$el.style.left = (x + window.scrollX) + 'px'; | ||||
| 				icon.$el.style.fontSize = window.getComputedStyle(this.$refs.icon.$el).fontSize; | ||||
| 
 | ||||
| 				document.body.appendChild(icon.$el); | ||||
| 
 | ||||
| 				anime({ | ||||
| 					targets: icon.$el, | ||||
| 					opacity: [1, 0], | ||||
| 					translateY: [0, -64], | ||||
| 					duration: 1000, | ||||
| 					easing: 'linear', | ||||
| 					complete: () => { | ||||
| 						icon.destroyDom(); | ||||
| 					} | ||||
| 				}); | ||||
| 			}); | ||||
| 		}, | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="stylus" scoped> | ||||
| .reaction | ||||
| 	display inline-block | ||||
| 	height 32px | ||||
| 	margin 2px | ||||
| 	padding 0 6px | ||||
| 	border-radius 4px | ||||
| 	cursor pointer | ||||
| 
 | ||||
| 	* | ||||
| 		user-select none | ||||
| 		pointer-events none | ||||
| 
 | ||||
| 	&.reacted | ||||
| 		background var(--primary) | ||||
| 
 | ||||
| 		> span | ||||
| 			color var(--primaryForeground) | ||||
| 
 | ||||
| 	&:not(.reacted) | ||||
| 		background var(--reactionViewerButtonBg) | ||||
| 
 | ||||
| 		&:hover | ||||
| 			background var(--reactionViewerButtonHoverBg) | ||||
| 
 | ||||
| 	> .mk-reaction-icon | ||||
| 		font-size 1.4em | ||||
| 
 | ||||
| 	> span | ||||
| 		font-size 1.1em | ||||
| 		line-height 32px | ||||
| 		vertical-align middle | ||||
| 		color var(--text) | ||||
| </style> | ||||
|  | @ -1,133 +1,31 @@ | |||
| <template> | ||||
| <div class="mk-reactions-viewer" :class="{ isMe }"> | ||||
| 	<template v-if="reactions"> | ||||
| 		<span :class="{ reacted: note.myReaction == 'like' }" @click="toggleReaction('like')" v-if="reactions.like" v-particle="!isMe"><mk-reaction-icon reaction="like" ref="like"/><span>{{ reactions.like }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'love' }" @click="toggleReaction('love')" v-if="reactions.love" v-particle="!isMe"><mk-reaction-icon reaction="love" ref="love"/><span>{{ reactions.love }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'laugh' }" @click="toggleReaction('laugh')" v-if="reactions.laugh" v-particle="!isMe"><mk-reaction-icon reaction="laugh" ref="laugh"/><span>{{ reactions.laugh }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'hmm' }" @click="toggleReaction('hmm')" v-if="reactions.hmm" v-particle="!isMe"><mk-reaction-icon reaction="hmm" ref="hmm"/><span>{{ reactions.hmm }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'surprise' }" @click="toggleReaction('surprise')" v-if="reactions.surprise" v-particle="!isMe"><mk-reaction-icon reaction="surprise" ref="surprise"/><span>{{ reactions.surprise }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'congrats' }" @click="toggleReaction('congrats')" v-if="reactions.congrats" v-particle="!isMe"><mk-reaction-icon reaction="congrats" ref="congrats"/><span>{{ reactions.congrats }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'angry' }" @click="toggleReaction('angry')" v-if="reactions.angry" v-particle="!isMe"><mk-reaction-icon reaction="angry" ref="angry"/><span>{{ reactions.angry }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'confused' }" @click="toggleReaction('confused')" v-if="reactions.confused" v-particle="!isMe"><mk-reaction-icon reaction="confused" ref="confused"/><span>{{ reactions.confused }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'rip' }" @click="toggleReaction('rip')" v-if="reactions.rip" v-particle="!isMe"><mk-reaction-icon reaction="rip" ref="rip"/><span>{{ reactions.rip }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'pudding' }" @click="toggleReaction('pudding')" v-if="reactions.pudding" v-particle="!isMe"><mk-reaction-icon reaction="pudding" ref="pudding"/><span>{{ reactions.pudding }}</span></span> | ||||
| 	</template> | ||||
| 	<x-reaction v-for="(count, reaction) in reactions" :reaction="reaction" :count="count" :note="note" :key="reaction"/> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import Icon from './reaction-icon.vue'; | ||||
| import anime from 'animejs'; | ||||
| import XReaction from './reactions-viewer.reaction.vue'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	components: { | ||||
| 		XReaction | ||||
| 	}, | ||||
| 	props: { | ||||
| 		note: { | ||||
| 			type: Object, | ||||
| 			required: true | ||||
| 		} | ||||
| 		}, | ||||
| 	}, | ||||
| 	computed: { | ||||
| 		reactions(): any { | ||||
| 			return this.note.reactionCounts; | ||||
| 		}, | ||||
| 		isMe(): boolean { | ||||
| 			return this.$store.getters.isSignedIn && (this.$store.state.i.id === this.note.userId); | ||||
| 		} | ||||
| 			return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId; | ||||
| 		}, | ||||
| 	}, | ||||
| 	watch: { | ||||
| 		'reactions.like'() { | ||||
| 			this.anime('like'); | ||||
| 		}, | ||||
| 		'reactions.love'() { | ||||
| 			this.anime('love'); | ||||
| 		}, | ||||
| 		'reactions.laugh'() { | ||||
| 			this.anime('laugh'); | ||||
| 		}, | ||||
| 		'reactions.hmm'() { | ||||
| 			this.anime('hmm'); | ||||
| 		}, | ||||
| 		'reactions.surprise'() { | ||||
| 			this.anime('surprise'); | ||||
| 		}, | ||||
| 		'reactions.congrats'() { | ||||
| 			this.anime('congrats'); | ||||
| 		}, | ||||
| 		'reactions.angry'() { | ||||
| 			this.anime('angry'); | ||||
| 		}, | ||||
| 		'reactions.confused'() { | ||||
| 			this.anime('confused'); | ||||
| 		}, | ||||
| 		'reactions.rip'() { | ||||
| 			this.anime('rip'); | ||||
| 		}, | ||||
| 		'reactions.pudding'() { | ||||
| 			this.anime('pudding'); | ||||
| 		} | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		toggleReaction(reaction: string) { | ||||
| 			if (this.isMe) return; | ||||
| 
 | ||||
| 			const oldReaction = this.note.myReaction; | ||||
| 			if (oldReaction) { | ||||
| 				this.$root.api('notes/reactions/delete', { | ||||
| 					noteId: this.note.id | ||||
| 				}).then(() => { | ||||
| 					if (oldReaction !== reaction) { | ||||
| 						this.$root.api('notes/reactions/create', { | ||||
| 							noteId: this.note.id, | ||||
| 							reaction: reaction | ||||
| 						}); | ||||
| 					} | ||||
| 				}); | ||||
| 			} else { | ||||
| 				this.$root.api('notes/reactions/create', { | ||||
| 					noteId: this.note.id, | ||||
| 					reaction: reaction | ||||
| 				}); | ||||
| 			} | ||||
| 		}, | ||||
| 		anime(reaction: string) { | ||||
| 			if (this.$store.state.device.reduceMotion) return; | ||||
| 			if (document.hidden) return; | ||||
| 
 | ||||
| 			this.$nextTick(() => { | ||||
| 				const rect = this.$refs[reaction].$el.getBoundingClientRect(); | ||||
| 
 | ||||
| 				const x = rect.left; | ||||
| 				const y = rect.top; | ||||
| 
 | ||||
| 				const icon = new Icon({ | ||||
| 					parent: this, | ||||
| 					propsData: { | ||||
| 						reaction: reaction | ||||
| 					} | ||||
| 				}).$mount(); | ||||
| 
 | ||||
| 				icon.$el.style.position = 'absolute'; | ||||
| 				icon.$el.style.zIndex = 100; | ||||
| 				icon.$el.style.top = (y + window.scrollY) + 'px'; | ||||
| 				icon.$el.style.left = (x + window.scrollX) + 'px'; | ||||
| 				icon.$el.style.fontSize = window.getComputedStyle(this.$refs[reaction].$el).fontSize; | ||||
| 
 | ||||
| 				document.body.appendChild(icon.$el); | ||||
| 
 | ||||
| 				anime({ | ||||
| 					targets: icon.$el, | ||||
| 					opacity: [1, 0], | ||||
| 					translateY: [0, -64], | ||||
| 					duration: 1000, | ||||
| 					easing: 'linear', | ||||
| 					complete: () => { | ||||
| 						icon.destroyDom(); | ||||
| 					} | ||||
| 				}); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
|  | @ -144,38 +42,4 @@ export default Vue.extend({ | |||
| 
 | ||||
| 			&:hover | ||||
| 				background var(--reactionViewerButtonBg) !important | ||||
| 
 | ||||
| 	> span | ||||
| 		display inline-block | ||||
| 		height 32px | ||||
| 		margin 2px | ||||
| 		padding 0 6px | ||||
| 		border-radius 4px | ||||
| 		cursor pointer | ||||
| 
 | ||||
| 		* | ||||
| 			user-select none | ||||
| 			pointer-events none | ||||
| 
 | ||||
| 		&.reacted | ||||
| 			background var(--primary) | ||||
| 
 | ||||
| 			> span | ||||
| 				color var(--primaryForeground) | ||||
| 
 | ||||
| 		&:not(.reacted) | ||||
| 			background var(--reactionViewerButtonBg) | ||||
| 
 | ||||
| 			&:hover | ||||
| 				background var(--reactionViewerButtonHoverBg) | ||||
| 
 | ||||
| 		> .mk-reaction-icon | ||||
| 			font-size 1.4em | ||||
| 
 | ||||
| 		> span | ||||
| 			font-size 1.1em | ||||
| 			line-height 32px | ||||
| 			vertical-align middle | ||||
| 			color var(--text) | ||||
| 
 | ||||
| </style> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue