diff --git a/components/publish/PublishEmojiPicker.client.vue b/components/publish/PublishEmojiPicker.client.vue
index 3365dd2b..0529cbd3 100644
--- a/components/publish/PublishEmojiPicker.client.vue
+++ b/components/publish/PublishEmojiPicker.client.vue
@@ -1,5 +1,6 @@
@@ -40,7 +48,7 @@ watchEffect(() => {
-
+
diff --git a/composables/emojis.ts b/composables/emojis.ts
new file mode 100644
index 00000000..5e466c16
--- /dev/null
+++ b/composables/emojis.ts
@@ -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)
diff --git a/composables/push-notifications/types.ts b/composables/push-notifications/types.ts
index e6228b8a..bd649efb 100644
--- a/composables/push-notifications/types.ts
+++ b/composables/push-notifications/types.ts
@@ -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'
@@ -19,3 +19,8 @@ export interface CreatePushNotification {
export type PushNotificationRequest = Record
export type PushNotificationPolicy = Record
+
+export interface CustomEmojisInfo {
+ lastUpdate: number
+ emojis: Emoji[]
+}
diff --git a/constants/index.ts b/constants/index.ts
index 51ca5239..4209515a 100644
--- a/constants/index.ts
+++ b/constants/index.ts
@@ -13,6 +13,7 @@ export const STORAGE_KEY_ZEN_MODE = 'elk-zenmode'
export const STORAGE_KEY_LANG = 'elk-lang'
export const STORAGE_KEY_FONT_SIZE = 'elk-font-size'
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_NEWS_TIPS = 'elk-hide-explore-news-tips'
export const STORAGE_KEY_HIDE_EXPLORE_TAGS_TIPS = 'elk-hide-explore-tags-tips'