feat: custom emojis, close #500

This commit is contained in:
Anthony Fu 2022-12-23 23:56:16 +01:00
parent 41ef187379
commit 597617b7e6
4 changed files with 75 additions and 8 deletions

View file

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Picker } from 'emoji-mart' import type { Picker } from 'emoji-mart'
import { updateCustomEmojis } from '~/composables/emojis'
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'select', code: string): void (e: 'select', code: string): void
@ -10,26 +11,33 @@ let picker = $ref<Picker>()
async function openEmojiPicker() { async function openEmojiPicker() {
if (!picker) { if (!picker) {
updateCustomEmojis()
const promise = import('@emoji-mart/data').then(r => r.default)
const { Picker } = await import('emoji-mart') const { Picker } = await import('emoji-mart')
picker = new Picker({ picker = new Picker({
data: () => import('@emoji-mart/data').then(r => r.default), data: () => promise,
onEmojiSelect(e: any) { onEmojiSelect(e: any) {
emit('select', e.native) emit('select', e.native || e.shortcodes)
}, },
theme: isDark.value ? 'dark' : 'light', theme: isDark.value ? 'dark' : 'light',
custom: customEmojisData.value,
}) })
// TODO: custom picker // TODO: custom picker
el?.appendChild(picker as any as HTMLElement) el?.appendChild(picker as any as HTMLElement)
} }
} }
watchEffect(() => { watch(isDark, () => {
if (!picker) picker?.update({
return
picker.update({
theme: isDark.value ? 'dark' : 'light', theme: isDark.value ? 'dark' : 'light',
}) })
}) })
watch(customEmojisData, () => {
picker?.update({
custom: customEmojisData.value,
})
})
</script> </script>
<template> <template>
@ -40,7 +48,7 @@ watchEffect(() => {
<div i-ri:emotion-line /> <div i-ri:emotion-line />
</button> </button>
<template #popper> <template #popper>
<div ref="el" /> <div ref="el" min-w-10 min-h-10 />
</template> </template>
</VDropdown> </VDropdown>
</template> </template>

53
composables/emojis.ts Normal file
View file

@ -0,0 +1,53 @@
import type { Emoji } from 'masto'
import type { CustomEmojisInfo } from './push-notifications/types'
import { STORAGE_KEY_CUSTOM_EMOJIS } from '~/constants'
const TTL = 1000 * 60 * 60 * 24 // 1 day
function getDefault(): CustomEmojisInfo {
return {
lastUpdate: 0,
emojis: [],
}
}
export const currentCustomEmojis = process.server
? computed(getDefault)
: useUserLocalStorage(STORAGE_KEY_CUSTOM_EMOJIS, getDefault)
export async function updateCustomEmojis() {
if (Date.now() - currentCustomEmojis.value.lastUpdate < TTL)
return
const masto = useMasto()
const emojis = await masto.customEmojis.fetchAll()
Object.assign(currentCustomEmojis.value, {
lastUpdate: Date.now(),
emojis,
})
}
function transformEmojiData(emojis: Emoji[]) {
const result = []
for (const emoji of emojis) {
if (!emoji.visibleInPicker)
continue
result.push({
id: emoji.shortcode,
native: ':emoji.shortcode:',
name: emoji.shortcode,
skins: [{ src: emoji.url || emoji.staticUrl }],
})
}
return result
}
export const customEmojisData = computed(() => currentCustomEmojis.value.emojis.length
? [{
id: 'custom',
name: `Custom emojis on ${currentServer.value}`,
emojis: transformEmojiData(currentCustomEmojis.value.emojis),
}]
: undefined)

View file

@ -1,4 +1,4 @@
import type { PushSubscription as MastoPushSubscription, PushSubscriptionAlerts, SubscriptionPolicy } from 'masto' import type { Emoji, PushSubscription as MastoPushSubscription, PushSubscriptionAlerts, SubscriptionPolicy } from 'masto'
import type { UserLogin } from '~/types' import type { UserLogin } from '~/types'
@ -19,3 +19,8 @@ export interface CreatePushNotification {
export type PushNotificationRequest = Record<string, boolean> export type PushNotificationRequest = Record<string, boolean>
export type PushNotificationPolicy = Record<string, SubscriptionPolicy> export type PushNotificationPolicy = Record<string, SubscriptionPolicy>
export interface CustomEmojisInfo {
lastUpdate: number
emojis: Emoji[]
}

View file

@ -13,6 +13,7 @@ export const STORAGE_KEY_ZEN_MODE = 'elk-zenmode'
export const STORAGE_KEY_LANG = 'elk-lang' export const STORAGE_KEY_LANG = 'elk-lang'
export const STORAGE_KEY_FONT_SIZE = 'elk-font-size' export const STORAGE_KEY_FONT_SIZE = 'elk-font-size'
export const STORAGE_KEY_FEATURE_FLAGS = 'elk-feature-flags' export const STORAGE_KEY_FEATURE_FLAGS = 'elk-feature-flags'
export const STORAGE_KEY_CUSTOM_EMOJIS = 'elk-custom-emojis'
export const STORAGE_KEY_HIDE_EXPLORE_POSTS_TIPS = 'elk-hide-explore-posts-tips' export const STORAGE_KEY_HIDE_EXPLORE_POSTS_TIPS = 'elk-hide-explore-posts-tips'
export const STORAGE_KEY_HIDE_EXPLORE_NEWS_TIPS = 'elk-hide-explore-news-tips' export const STORAGE_KEY_HIDE_EXPLORE_NEWS_TIPS = 'elk-hide-explore-news-tips'
export const STORAGE_KEY_HIDE_EXPLORE_TAGS_TIPS = 'elk-hide-explore-tags-tips' export const STORAGE_KEY_HIDE_EXPLORE_TAGS_TIPS = 'elk-hide-explore-tags-tips'