fix(client): better hover detection
This commit is contained in:
		
							parent
							
								
									bcf7530eef
								
							
						
					
					
						commit
						e804a299e0
					
				
					 4 changed files with 41 additions and 32 deletions
				
			
		|  | @ -19,10 +19,6 @@ | |||
| 				:reaction="notification.reaction ? notification.reaction.replace(/^:(\w+):$/, ':$1@.:') : notification.reaction" | ||||
| 				:custom-emojis="notification.note.emojis" | ||||
| 				:no-style="true" | ||||
| 				@touchstart.passive="onReactionMouseover" | ||||
| 				@mouseover="onReactionMouseover" | ||||
| 				@mouseleave="onReactionMouseleave" | ||||
| 				@touchend="onReactionMouseleave" | ||||
| 			/> | ||||
| 		</div> | ||||
| 	</div> | ||||
|  | @ -151,7 +147,7 @@ export default defineComponent({ | |||
| 			os.api('users/groups/invitations/reject', { invitationId: props.notification.invitation.id }); | ||||
| 		}; | ||||
| 
 | ||||
| 		const { onMouseover: onReactionMouseover, onMouseleave: onReactionMouseleave } = useTooltip((showing) => { | ||||
| 		useTooltip(reactionRef, (showing) => { | ||||
| 			os.popup(XReactionTooltip, { | ||||
| 				showing, | ||||
| 				reaction: props.notification.reaction ? props.notification.reaction.replace(/^:(\w+):$/, ':$1@.:') : props.notification.reaction, | ||||
|  | @ -170,8 +166,6 @@ export default defineComponent({ | |||
| 			rejectFollowRequest, | ||||
| 			acceptGroupInvitation, | ||||
| 			rejectGroupInvitation, | ||||
| 			onReactionMouseover, | ||||
| 			onReactionMouseleave, | ||||
| 			elRef, | ||||
| 			reactionRef, | ||||
| 		}; | ||||
|  |  | |||
|  | @ -6,10 +6,6 @@ | |||
| 	class="hkzvhatu _button" | ||||
| 	:class="{ reacted: note.myReaction == reaction, canToggle }" | ||||
| 	@click="toggleReaction()" | ||||
| 	@touchstart.passive="onMouseover" | ||||
| 	@mouseover="onMouseover" | ||||
| 	@mouseleave="onMouseleave" | ||||
| 	@touchend="onMouseleave" | ||||
| > | ||||
| 	<XReactionIcon :reaction="reaction" :custom-emojis="note.emojis"/> | ||||
| 	<span>{{ count }}</span> | ||||
|  | @ -90,7 +86,7 @@ export default defineComponent({ | |||
| 			if (!props.isInitial) anime(); | ||||
| 		}); | ||||
| 
 | ||||
| 		const { onMouseover, onMouseleave } = useTooltip(async (showing) => { | ||||
| 		useTooltip(buttonRef, async (showing) => { | ||||
| 			const reactions = await os.api('notes/reactions', { | ||||
| 				noteId: props.note.id, | ||||
| 				type: props.reaction, | ||||
|  | @ -113,8 +109,6 @@ export default defineComponent({ | |||
| 			buttonRef, | ||||
| 			canToggle, | ||||
| 			toggleReaction, | ||||
| 			onMouseover, | ||||
| 			onMouseleave, | ||||
| 		}; | ||||
| 	}, | ||||
| }); | ||||
|  |  | |||
|  | @ -3,10 +3,6 @@ | |||
| 	ref="buttonRef" | ||||
| 	class="eddddedb _button canRenote" | ||||
| 	@click="renote()" | ||||
| 	@touchstart.passive="onMouseover" | ||||
| 	@mouseover="onMouseover" | ||||
| 	@mouseleave="onMouseleave" | ||||
| 	@touchend="onMouseleave" | ||||
| > | ||||
| 	<i class="fas fa-retweet"></i> | ||||
| 	<p v-if="count > 0" class="count">{{ count }}</p> | ||||
|  | @ -42,7 +38,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 		const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id); | ||||
| 
 | ||||
| 		const { onMouseover, onMouseleave } = useTooltip(async (showing) => { | ||||
| 		useTooltip(buttonRef, async (showing) => { | ||||
| 			const renotes = await os.api('notes/renotes', { | ||||
| 				noteId: props.note.id, | ||||
| 				limit: 11 | ||||
|  | @ -87,8 +83,6 @@ export default defineComponent({ | |||
| 			buttonRef, | ||||
| 			canRenote, | ||||
| 			renote, | ||||
| 			onMouseover, | ||||
| 			onMouseleave, | ||||
| 		}; | ||||
| 	}, | ||||
| }); | ||||
|  |  | |||
|  | @ -1,8 +1,16 @@ | |||
| import { Ref, ref } from 'vue'; | ||||
| import { isScreenTouching, isTouchUsing } from './touch'; | ||||
| import { Ref, ref, watch } from 'vue'; | ||||
| 
 | ||||
| export function useTooltip(onShow: (showing: Ref<boolean>) => void) { | ||||
| export function useTooltip( | ||||
| 	elRef: Ref<HTMLElement | { $el: HTMLElement } | null | undefined>, | ||||
| 	onShow: (showing: Ref<boolean>) => void, | ||||
| ): void { | ||||
| 	let isHovering = false; | ||||
| 
 | ||||
| 	// iOS(Androidも?)では、要素をタップした直後に(おせっかいで)mouseoverイベントを発火させたりするため、それを無視するためのフラグ
 | ||||
| 	// 無視しないと、画面に触れてないのにツールチップが出たりし、ユーザビリティが損なわれる
 | ||||
| 	// TODO: 一度でもタップすると二度とマウスでツールチップ出せなくなるのをどうにかする 定期的にfalseに戻すとか...?
 | ||||
| 	let shouldIgnoreMouseover = false; | ||||
| 
 | ||||
| 	let timeoutId: number; | ||||
| 
 | ||||
| 	let changeShowingState: (() => void) | null; | ||||
|  | @ -11,11 +19,6 @@ export function useTooltip(onShow: (showing: Ref<boolean>) => void) { | |||
| 		close(); | ||||
| 		if (!isHovering) return; | ||||
| 
 | ||||
| 		// iOS(Androidも?)では、要素をタップした直後に(おせっかいで)mouseoverイベントを発火させたりするため、その対策
 | ||||
| 		// これが無いと、画面に触れてないのにツールチップが出たりしてしまう
 | ||||
| 		// TODO: タッチとマウス両方使っている環境では、マウス操作でツールチップ出せなくなるのをどうにかする
 | ||||
| 		if (isTouchUsing && !isScreenTouching) return; | ||||
| 
 | ||||
| 		const showing = ref(true); | ||||
| 		onShow(showing); | ||||
| 		changeShowingState = () => { | ||||
|  | @ -32,6 +35,7 @@ export function useTooltip(onShow: (showing: Ref<boolean>) => void) { | |||
| 
 | ||||
| 	const onMouseover = () => { | ||||
| 		if (isHovering) return; | ||||
| 		if (shouldIgnoreMouseover) return; | ||||
| 		isHovering = true; | ||||
| 		timeoutId = window.setTimeout(open, 300); | ||||
| 	}; | ||||
|  | @ -43,8 +47,31 @@ export function useTooltip(onShow: (showing: Ref<boolean>) => void) { | |||
| 		close(); | ||||
| 	}; | ||||
| 
 | ||||
| 	return { | ||||
| 		onMouseover, | ||||
| 		onMouseleave, | ||||
| 	const onTouchstart = () => { | ||||
| 		shouldIgnoreMouseover = true; | ||||
| 		if (isHovering) return; | ||||
| 		isHovering = true; | ||||
| 		timeoutId = window.setTimeout(open, 300); | ||||
| 	}; | ||||
| 
 | ||||
| 	const onTouchend = () => { | ||||
| 		if (!isHovering) return; | ||||
| 		isHovering = false; | ||||
| 		window.clearTimeout(timeoutId); | ||||
| 		close(); | ||||
| 	}; | ||||
| 
 | ||||
| 	const stop = watch(elRef, () => { | ||||
| 		if (elRef.value) { | ||||
| 			stop(); | ||||
| 			const el = elRef.value instanceof Element ? elRef.value : elRef.value.$el; | ||||
| 			el.addEventListener('mouseover', onMouseover, { passive: true }); | ||||
| 			el.addEventListener('mouseleave', onMouseleave, { passive: true }); | ||||
| 			el.addEventListener('touchstart', onTouchstart, { passive: true }); | ||||
| 			el.addEventListener('touchend', onTouchend, { passive: true }); | ||||
| 		} | ||||
| 	}, { | ||||
| 		immediate: true, | ||||
| 		flush: 'post', | ||||
| 	}); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue