summaryrefslogtreecommitdiff
path: root/packages/frontend-embed/src/components/EmCustomEmoji.vue
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend-embed/src/components/EmCustomEmoji.vue')
-rw-r--r--packages/frontend-embed/src/components/EmCustomEmoji.vue101
1 files changed, 101 insertions, 0 deletions
diff --git a/packages/frontend-embed/src/components/EmCustomEmoji.vue b/packages/frontend-embed/src/components/EmCustomEmoji.vue
new file mode 100644
index 0000000000..e4149cf363
--- /dev/null
+++ b/packages/frontend-embed/src/components/EmCustomEmoji.vue
@@ -0,0 +1,101 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<img
+ v-if="errored && fallbackToImage"
+ :class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
+ src="/client-assets/dummy.png"
+ :title="alt"
+/>
+<span v-else-if="errored">:{{ customEmojiName }}:</span>
+<img
+ v-else
+ :class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
+ :src="url"
+ :alt="alt"
+ :title="alt"
+ decoding="async"
+ @error="errored = true"
+ @load="errored = false"
+/>
+</template>
+
+<script lang="ts" setup>
+import { computed, inject, ref } from 'vue';
+import { customEmojisMap } from '@/custom-emojis.js';
+
+import { DI } from '@/di.js';
+
+const mediaProxy = inject(DI.mediaProxy)!;
+
+const props = defineProps<{
+ name: string;
+ normal?: boolean;
+ noStyle?: boolean;
+ host?: string | null;
+ url?: string;
+ useOriginalSize?: boolean;
+ menu?: boolean;
+ menuReaction?: boolean;
+ fallbackToImage?: boolean;
+}>();
+
+const customEmojiName = computed(() => (props.name[0] === ':' ? props.name.substring(1, props.name.length - 1) : props.name).replace('@.', ''));
+const isLocal = computed(() => !props.host && (customEmojiName.value.endsWith('@.') || !customEmojiName.value.includes('@')));
+
+const rawUrl = computed(() => {
+ if (props.url) {
+ return props.url;
+ }
+ if (isLocal.value) {
+ return customEmojisMap.get(customEmojiName.value)?.url ?? null;
+ }
+ return props.host ? `/emoji/${customEmojiName.value}@${props.host}.webp` : `/emoji/${customEmojiName.value}.webp`;
+});
+
+const url = computed(() => {
+ if (rawUrl.value == null) return undefined;
+
+ const proxied =
+ (rawUrl.value.startsWith('/emoji/') || (props.useOriginalSize && isLocal.value))
+ ? rawUrl.value
+ : mediaProxy.getProxiedImageUrl(
+ rawUrl.value,
+ props.useOriginalSize ? undefined : 'emoji',
+ false,
+ true,
+ );
+ return proxied;
+});
+
+const alt = computed(() => `:${customEmojiName.value}:`);
+const errored = ref(url.value == null);
+</script>
+
+<style lang="scss" module>
+.root {
+ height: 2em;
+ vertical-align: middle;
+ transition: transform 0.2s ease;
+
+ &:hover {
+ transform: scale(1.2);
+ }
+}
+
+.normal {
+ height: 1.25em;
+ vertical-align: -0.25em;
+
+ &:hover {
+ transform: none;
+ }
+}
+
+.noStyle {
+ height: auto !important;
+}
+</style>