Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop
This commit is contained in:
		
						commit
						c9e8399e0e
					
				
					 8 changed files with 64 additions and 39 deletions
				
			
		
							
								
								
									
										4
									
								
								.github/misskey/test.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/misskey/test.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -4,12 +4,12 @@ url: 'http://misskey.local' | |||
| port: 61812 | ||||
| 
 | ||||
| db: | ||||
|   host: localhost | ||||
|   host: 127.0.0.1 | ||||
|   port: 54312 | ||||
|   db: test-misskey | ||||
|   user: postgres | ||||
|   pass: '' | ||||
| redis: | ||||
|   host: localhost | ||||
|   host: 127.0.0.1 | ||||
|   port: 56312 | ||||
| id: aid | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| 	<div class="bqxuuuey"> | ||||
| 		<div class="reaction"> | ||||
| 			<XReactionIcon :reaction="reaction" :custom-emojis="emojis" class="icon" :no-style="true"/> | ||||
| 			<div class="name">{{ reaction.replace('@.', '') }}</div> | ||||
| 			<div class="name">{{ getReactionName(reaction) }}</div> | ||||
| 		</div> | ||||
| 		<div class="users"> | ||||
| 			<div v-for="u in users" :key="u.id" class="user"> | ||||
|  | @ -20,6 +20,7 @@ | |||
| import { } from 'vue'; | ||||
| import MkTooltip from './MkTooltip.vue'; | ||||
| import XReactionIcon from '@/components/MkReactionIcon.vue'; | ||||
| import { getEmojiName } from '@/scripts/emojilist'; | ||||
| 
 | ||||
| defineProps<{ | ||||
| 	showing: boolean; | ||||
|  | @ -33,6 +34,14 @@ defineProps<{ | |||
| const emit = defineEmits<{ | ||||
| 	(ev: 'closed'): void; | ||||
| }>(); | ||||
| 
 | ||||
| function getReactionName(reaction: string): string { | ||||
| 	const trimLocal = reaction.replace('@.', ''); | ||||
| 	if (trimLocal.startsWith(':')) { | ||||
| 		return trimLocal; | ||||
| 	} | ||||
| 	return getEmojiName(reaction) ?? reaction; | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
|  |  | |||
|  | @ -1,17 +1,18 @@ | |||
| <template> | ||||
| <img v-if="customEmoji" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt" decoding="async"/> | ||||
| <img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" :alt="alt" :title="alt" decoding="async"/> | ||||
| <span v-else-if="char && useOsNativeEmojis">{{ char }}</span> | ||||
| <img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" decoding="async" @pointerenter="computeTitle"/> | ||||
| <span v-else-if="char && useOsNativeEmojis" :alt="alt" @pointerenter="computeTitle">{{ char }}</span> | ||||
| <span v-else>{{ emoji }}</span> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { computed, ref, watch } from 'vue'; | ||||
| import { computed } from 'vue'; | ||||
| import { CustomEmoji } from 'misskey-js/built/entities'; | ||||
| import { getStaticImageUrl } from '@/scripts/get-static-image-url'; | ||||
| import { char2filePath } from '@/scripts/twemoji-base'; | ||||
| import { defaultStore } from '@/store'; | ||||
| import { instance } from '@/instance'; | ||||
| import { getEmojiName } from '@/scripts/emojilist'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
| 	emoji: string; | ||||
|  | @ -22,20 +23,28 @@ const props = defineProps<{ | |||
| }>(); | ||||
| 
 | ||||
| const isCustom = computed(() => props.emoji.startsWith(':')); | ||||
| const char = computed(() => isCustom.value ? null : props.emoji); | ||||
| const char = computed(() => isCustom.value ? undefined : props.emoji); | ||||
| const useOsNativeEmojis = computed(() => defaultStore.state.useOsNativeEmojis && !props.isReaction); | ||||
| const ce = computed(() => props.customEmojis ?? instance.emojis ?? []); | ||||
| const customEmoji = computed(() => isCustom.value ? ce.value.find(x => x.name === props.emoji.substr(1, props.emoji.length - 2)) : null); | ||||
| const customEmoji = computed(() => isCustom.value ? ce.value.find(x => x.name === props.emoji.substr(1, props.emoji.length - 2)) : undefined); | ||||
| const url = computed(() => { | ||||
| 	if (char.value) { | ||||
| 		return char2filePath(char.value); | ||||
| 	} else { | ||||
| 		const rawUrl = (customEmoji.value as CustomEmoji).url; | ||||
| 		return defaultStore.state.disableShowingAnimatedImages | ||||
| 			? getStaticImageUrl(customEmoji.value.url) | ||||
| 			: customEmoji.value.url; | ||||
| 			? getStaticImageUrl(rawUrl) | ||||
| 			: rawUrl; | ||||
| 	} | ||||
| }); | ||||
| const alt = computed(() => customEmoji.value ? `:${customEmoji.value.name}:` : char.value); | ||||
| 
 | ||||
| function computeTitle(event: PointerEvent): void { | ||||
| 	const title = customEmoji.value | ||||
| 		? `:${customEmoji.value.name}:` | ||||
| 		: (getEmojiName(char.value as string) ?? char.value as string); | ||||
| 	(event.target as HTMLElement).title = title; | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ | |||
| 			</div> | ||||
| 		</div> | ||||
| 		<div v-if="!narrow || hideTitle" class="tabs"> | ||||
| 			<button v-for="tab in tabs" :ref="(el) => tabRefs[tab.key] = el" v-tooltip.noDelay="tab.title" class="tab _button" :class="{ active: tab.key != null && tab.key === props.tab }" @mousedown="(ev) => onTabMousedown(tab, ev)" @click="(ev) => onTabClick(tab, ev)"> | ||||
| 			<button v-for="tab in tabs" :ref="(el) => tabRefs[tab.key] = (el as HTMLElement)" v-tooltip.noDelay="tab.title" class="tab _button" :class="{ active: tab.key != null && tab.key === props.tab }" @mousedown="(ev) => onTabMousedown(tab, ev)" @click="(ev) => onTabClick(tab, ev)"> | ||||
| 				<i v-if="tab.icon" class="icon" :class="tab.icon"></i> | ||||
| 				<span v-if="!tab.iconOnly" class="title">{{ tab.title }}</span> | ||||
| 			</button> | ||||
|  | @ -37,34 +37,36 @@ | |||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { computed, onMounted, onUnmounted, ref, inject, watch, shallowReactive, nextTick, reactive } from 'vue'; | ||||
| import { onMounted, onUnmounted, ref, inject, watch, nextTick } from 'vue'; | ||||
| import tinycolor from 'tinycolor2'; | ||||
| import { popupMenu } from '@/os'; | ||||
| import { scrollToTop } from '@/scripts/scroll'; | ||||
| import { i18n } from '@/i18n'; | ||||
| import { globalEvents } from '@/events'; | ||||
| import { injectPageMetadata } from '@/scripts/page-metadata'; | ||||
| import { $i } from '@/account'; | ||||
| 
 | ||||
| type Tab = { | ||||
| 	key?: string | null; | ||||
| 	key: string; | ||||
| 	title: string; | ||||
| 	icon?: string; | ||||
| 	iconOnly?: boolean; | ||||
| 	onClick?: (ev: MouseEvent) => void; | ||||
| }; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
| const props = withDefaults(defineProps<{ | ||||
| 	tabs?: Tab[]; | ||||
| 	tab?: string; | ||||
| 	actions?: { | ||||
| 		text: string; | ||||
| 		icon: string; | ||||
| 		highlighted?: boolean; | ||||
| 		handler: (ev: MouseEvent) => void; | ||||
| 	}[]; | ||||
| 	thin?: boolean; | ||||
| 	displayMyAvatar?: boolean; | ||||
| }>(); | ||||
| }>(), { | ||||
| 	tabs: () => ([] as Tab[]) | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
| 	(ev: 'update:tab', key: string); | ||||
|  | @ -75,13 +77,12 @@ const metadata = injectPageMetadata(); | |||
| const hideTitle = inject('shouldOmitHeaderTitle', false); | ||||
| const thin_ = props.thin || inject('shouldHeaderThin', false); | ||||
| 
 | ||||
| const el = $ref<HTMLElement | null>(null); | ||||
| const tabRefs = {}; | ||||
| const el = $ref<HTMLElement | undefined>(undefined); | ||||
| const tabRefs: Record<string, HTMLElement | null> = {}; | ||||
| const tabHighlightEl = $ref<HTMLElement | null>(null); | ||||
| const bg = ref(null); | ||||
| const bg = ref<string | undefined>(undefined); | ||||
| let narrow = $ref(false); | ||||
| const height = ref(0); | ||||
| const hasTabs = $computed(() => props.tabs && props.tabs.length > 0); | ||||
| const hasTabs = $computed(() => props.tabs.length > 0); | ||||
| const hasActions = $computed(() => props.actions && props.actions.length > 0); | ||||
| const show = $computed(() => { | ||||
| 	return !hideTitle || hasTabs || hasActions; | ||||
|  | @ -100,7 +101,7 @@ const showTabsPopup = (ev: MouseEvent) => { | |||
| 			onTabClick(tab, ev); | ||||
| 		}, | ||||
| 	})); | ||||
| 	popupMenu(menu, ev.currentTarget ?? ev.target); | ||||
| 	popupMenu(menu, ev.currentTarget! as HTMLElement); | ||||
| }; | ||||
| 
 | ||||
| const preventDrag = (ev: TouchEvent) => { | ||||
|  | @ -108,7 +109,9 @@ const preventDrag = (ev: TouchEvent) => { | |||
| }; | ||||
| 
 | ||||
| const onClick = () => { | ||||
| 	scrollToTop(el, { behavior: 'smooth' }); | ||||
| 	if (el) { | ||||
| 		scrollToTop(el as HTMLElement, { behavior: 'smooth' }); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| function onTabMousedown(tab: Tab, ev: MouseEvent): void { | ||||
|  | @ -144,8 +147,8 @@ onMounted(() => { | |||
| 
 | ||||
| 	watch(() => [props.tab, props.tabs], () => { | ||||
| 		nextTick(() => { | ||||
| 			const tabEl = tabRefs[props.tab]; | ||||
| 			if (tabEl && tabHighlightEl) { | ||||
| 			const tabEl = props.tab ? tabRefs[props.tab] : undefined; | ||||
| 			if (tabEl && tabHighlightEl && tabEl.parentElement) { | ||||
| 				// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある | ||||
| 				// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4 | ||||
| 				const parentRect = tabEl.parentElement.getBoundingClientRect(); | ||||
|  | @ -161,11 +164,11 @@ onMounted(() => { | |||
| 	if (el && el.parentElement) { | ||||
| 		narrow = el.parentElement.offsetWidth < 500; | ||||
| 		ro = new ResizeObserver((entries, observer) => { | ||||
| 			if (el.parentElement && document.body.contains(el)) { | ||||
| 			if (el.parentElement && document.body.contains(el as HTMLElement)) { | ||||
| 				narrow = el.parentElement.offsetWidth < 500; | ||||
| 			} | ||||
| 		}); | ||||
| 		ro.observe(el.parentElement); | ||||
| 		ro.observe(el.parentElement as HTMLElement); | ||||
| 	} | ||||
| }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -96,9 +96,9 @@ onMounted(async () => { | |||
| 	&:global { | ||||
| 		> .pies { | ||||
| 			display: grid; | ||||
| 			grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); | ||||
| 			grid-gap: 16px; | ||||
| 			margin-bottom: 16px; | ||||
| 			grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); | ||||
| 			grid-gap: 12px; | ||||
| 			margin-bottom: 12px; | ||||
| 
 | ||||
| 			> .pie { | ||||
| 				position: relative; | ||||
|  | @ -126,8 +126,8 @@ onMounted(async () => { | |||
| 
 | ||||
| 		> .items { | ||||
| 			display: grid; | ||||
| 			grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); | ||||
| 			grid-gap: 16px; | ||||
| 			grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); | ||||
| 			grid-gap: 12px; | ||||
| 
 | ||||
| 			> .item { | ||||
| 				display: flex; | ||||
|  | @ -160,10 +160,10 @@ onMounted(async () => { | |||
| 				} | ||||
| 
 | ||||
| 				> .body { | ||||
| 					padding: 4px 0; | ||||
| 					padding: 2px 0; | ||||
| 
 | ||||
| 					> .value { | ||||
| 						font-size: 1.3em; | ||||
| 						font-size: 1.25em; | ||||
| 						font-weight: bold; | ||||
| 
 | ||||
| 						> .diff { | ||||
|  |  | |||
|  | @ -109,7 +109,7 @@ onUnmounted(() => { | |||
| 		> .charts { | ||||
| 			display: grid; | ||||
| 			grid-template-columns: 1fr 1fr; | ||||
| 			gap: 16px; | ||||
| 			gap: 12px; | ||||
| 
 | ||||
| 			> .chart { | ||||
| 				min-width: 0; | ||||
|  |  | |||
|  | @ -81,8 +81,8 @@ onMounted(async () => { | |||
| <style lang="scss" module> | ||||
| .root { | ||||
| 	display: grid; | ||||
| 	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); | ||||
| 	grid-gap: 16px; | ||||
| 	grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); | ||||
| 	grid-gap: 12px; | ||||
| 
 | ||||
| 	&:global { | ||||
| 		> .item { | ||||
|  | @ -130,10 +130,10 @@ onMounted(async () => { | |||
| 			} | ||||
| 
 | ||||
| 			> .body { | ||||
| 				padding: 4px 0; | ||||
| 				padding: 2px 0; | ||||
| 
 | ||||
| 				> .value { | ||||
| 					font-size: 1.3em; | ||||
| 					font-size: 1.25em; | ||||
| 					font-weight: bold; | ||||
| 
 | ||||
| 					> .diff { | ||||
|  |  | |||
|  | @ -11,3 +11,7 @@ export type UnicodeEmojiDef = { | |||
| import _emojilist from '../emojilist.json'; | ||||
| 
 | ||||
| export const emojilist = _emojilist as UnicodeEmojiDef[]; | ||||
| 
 | ||||
| export function getEmojiName(char: string): string | undefined { | ||||
| 	return emojilist.find(emo => emo.char === char)?.name; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue