feat: support custom emoji

zio/stable
Anthony Fu 2022-11-21 15:14:07 +08:00
parent 193d1cf5c5
commit cefecb16a0
6 changed files with 48 additions and 26 deletions

View File

@ -14,9 +14,7 @@ defineProps<{
</NuxtLink>
</div>
<NuxtLink flex flex-col :to="`/@${account.acct}`">
<h4 font-bold>
{{ account.displayName }}
</h4>
<CommonRichContent font-bold :content="account.displayName" />
<p op35 text-sm>
@{{ account.acct }}
</p>

View File

@ -9,6 +9,6 @@ defineProps<{
<template>
<a :href="`/@${account.acct}`" flex gap-2 font-bold items-center>
<img :src="account.avatar" class="w-5 h-5 rounded">
{{ account.displayName }}
<CommonRichContent :content="account.displayName" />
</a>
</template>

View File

@ -1,3 +1,5 @@
import type { Emoji } from 'masto'
export default defineComponent({
props: {
content: {
@ -6,6 +8,15 @@ export default defineComponent({
},
},
setup(props) {
return () => contentToVNode(props.content)
const emojis = shallowRef<Record<string, Emoji>>({})
onMounted(() => {
const { server } = useAppCookies()
const { serverInfos } = useClientState()
if (server.value)
emojis.value = serverInfos.value[server.value].customEmojis || {}
})
return () => h('div', { class: 'rich-content' }, contentToVNode(props.content, undefined, emojis.value))
},
})

View File

@ -11,23 +11,3 @@ const { status } = defineProps<{
<CommonRichContent :content="status.content" />
</div>
</template>
<style lang="postcss">
.status-body {
a {
--at-apply: text-primary hover:underline;
.invisible {
--at-apply: hidden;
}
.ellipsis {
--at-apply: truncate overflow-hidden ws-nowrap;
}
}
b {
--at-apply: font-bold;
}
p {
--at-apply: my-2;
}
}
</style>

View File

@ -1,3 +1,4 @@
import type { Emoji } from 'masto'
import type { DefaultTreeAdapterMap } from 'parse5'
import { parseFragment } from 'parse5'
import type { VNode } from 'vue'
@ -33,7 +34,14 @@ export function defaultHandle(el: Element) {
export function contentToVNode(
content: string,
handle: (node: Element) => Element | undefined | null | void = defaultHandle,
customEmojis: Record<string, Emoji> = {},
): VNode {
content = content.replace(/:([\w-]+?):/g, (_, name) => {
const emoji = customEmojis[name]
if (emoji)
return `<img src="${emoji.url}" alt="${name}" class="custom-emoji" />`
return `:${name}:`
})
const tree = parseFragment(content)
return h(Fragment, tree.childNodes.map(n => treeToVNode(n, handle)))
}

View File

@ -27,4 +27,29 @@
/* Force vertical scrollbar to be always visible to avoid layout shift while loading the content */
html {
overflow-y: scroll;
}
}
.custom-emoji {
display: inline-block;
height: 1.2em;
width: 1.2em;
vertical-align: middle;
}
.rich-content {
a {
--at-apply: text-primary hover:underline;
.invisible {
--at-apply: hidden;
}
.ellipsis {
--at-apply: truncate overflow-hidden ws-nowrap;
}
}
b {
--at-apply: font-bold;
}
p {
--at-apply: my-2;
}
}