pass 2 + emoji store separation
This commit is contained in:
parent
24ce2dc0a5
commit
4156b1597a
12 changed files with 372 additions and 119 deletions
|
|
@ -22,7 +22,8 @@ import {
|
|||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useAuthFlowStore } from 'src/stores/auth_flow'
|
||||
import { useI18nStore } from 'src/stores/i18n'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth'
|
||||
import App from '../App.vue'
|
||||
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
|
||||
|
|
@ -78,29 +79,29 @@ const getInstanceConfig = async ({ store }) => {
|
|||
const textlimit = data.max_toot_chars
|
||||
const vapidPublicKey = data.pleroma.vapid_public_key
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pleromaExtensionsAvailable',
|
||||
useInstanceStore().set({
|
||||
name: 'featureSet.pleromaExtensionsAvailable',
|
||||
value: data.pleroma,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'textlimit',
|
||||
value: textlimit,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'accountApprovalRequired',
|
||||
value: data.approval_required,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'birthdayRequired',
|
||||
value: !!data.pleroma?.metadata.birthday_required,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'birthdayMinAge',
|
||||
value: data.pleroma?.metadata.birthday_min_age || 0,
|
||||
})
|
||||
|
||||
if (vapidPublicKey) {
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'vapidPublicKey',
|
||||
value: vapidPublicKey,
|
||||
})
|
||||
|
|
@ -161,14 +162,14 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
|
|||
config = Object.assign({}, staticConfig, apiConfig)
|
||||
}
|
||||
|
||||
const copyInstanceOption = (name) => {
|
||||
const copyInstanceOption = (path) => {
|
||||
if (typeof config[name] !== 'undefined') {
|
||||
store.dispatch('setInstanceOption', { name, value: config[name] })
|
||||
useInstanceStore().set({ path, value: config[name] })
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(staticOrApiConfigDefault).forEach(copyInstanceOption)
|
||||
Object.keys(instanceDefaultConfig).forEach(copyInstanceOption)
|
||||
Object.keys(staticOrApiConfigDefault).map(k => `instanceIdentity.${k}`).forEach(copyInstanceOption)
|
||||
Object.keys(instanceDefaultConfig).map(k => `prefsStorage.${k}`).forEach(copyInstanceOption)
|
||||
|
||||
useAuthFlowStore().setInitialStrategy(config.loginMethod)
|
||||
}
|
||||
|
|
@ -178,7 +179,7 @@ const getTOS = async ({ store }) => {
|
|||
const res = await window.fetch('/static/terms-of-service.html')
|
||||
if (res.ok) {
|
||||
const html = await res.text()
|
||||
store.dispatch('setInstanceOption', { name: 'tos', value: html })
|
||||
useInstanceStore().set({ name: 'tos', value: html })
|
||||
} else {
|
||||
throw res
|
||||
}
|
||||
|
|
@ -192,7 +193,7 @@ const getInstancePanel = async ({ store }) => {
|
|||
const res = await preloadFetch('/instance/panel.html')
|
||||
if (res.ok) {
|
||||
const html = await res.text()
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'instanceSpecificPanelContent',
|
||||
value: html,
|
||||
})
|
||||
|
|
@ -227,7 +228,7 @@ const getStickers = async ({ store }) => {
|
|||
).sort((a, b) => {
|
||||
return a.meta.title.localeCompare(b.meta.title)
|
||||
})
|
||||
store.dispatch('setInstanceOption', { name: 'stickers', value: stickers })
|
||||
useInstanceStore().set({ name: 'stickers', value: stickers })
|
||||
} else {
|
||||
throw res
|
||||
}
|
||||
|
|
@ -248,7 +249,7 @@ const getAppSecret = async ({ store }) => {
|
|||
|
||||
const resolveStaffAccounts = ({ store, accounts }) => {
|
||||
const nicknames = accounts.map((uri) => uri.split('/').pop())
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'staffAccounts',
|
||||
value: nicknames,
|
||||
})
|
||||
|
|
@ -262,159 +263,163 @@ const getNodeInfo = async ({ store }) => {
|
|||
const data = await res.json()
|
||||
const metadata = data.metadata
|
||||
const features = metadata.features
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'name',
|
||||
useInstanceStore().set({
|
||||
path: 'name',
|
||||
value: metadata.nodeName,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'registrationOpen',
|
||||
useInstanceStore().set({
|
||||
path: 'registrationOpen',
|
||||
value: data.openRegistrations,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'mediaProxyAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.mediaProxyAvailable',
|
||||
value: features.includes('media_proxy'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'safeDM',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.safeDM',
|
||||
value: features.includes('safe_dm_mentions'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'shoutAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.shoutAvailable',
|
||||
value: features.includes('chat'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pleromaChatMessagesAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.pleromaChatMessagesAvailable',
|
||||
value: features.includes('pleroma_chat_messages'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pleromaCustomEmojiReactionsAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.pleromaCustomEmojiReactionsAvailable',
|
||||
value:
|
||||
features.includes('pleroma_custom_emoji_reactions') ||
|
||||
features.includes('custom_emoji_reactions'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pleromaBookmarkFoldersAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.pleromaBookmarkFoldersAvailable',
|
||||
value: features.includes('pleroma:bookmark_folders'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'gopherAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.gopherAvailable',
|
||||
value: features.includes('gopher'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pollsAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.pollsAvailable',
|
||||
value: features.includes('polls'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'editingAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.editingAvailable',
|
||||
value: features.includes('editing'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'pollLimits',
|
||||
value: metadata.pollLimits,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'mailerEnabled',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.mailerEnabled',
|
||||
value: metadata.mailerEnabled,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'quotingAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.quotingAvailable',
|
||||
value: features.includes('quote_posting'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'groupActorAvailable',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.groupActorAvailable',
|
||||
value: features.includes('pleroma:group_actors'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'blockExpiration',
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.blockExpiration',
|
||||
value: features.includes('pleroma:block_expiration'),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'localBubbleInstances',
|
||||
useInstanceStore().set({
|
||||
path: 'localBubbleInstances',
|
||||
value: metadata.localBubbleInstances ?? [],
|
||||
})
|
||||
useInstanceStore().set({
|
||||
path: 'featureSet.localBubble',
|
||||
value: (metadata.localBubbleInstances ?? []).length > 0,
|
||||
})
|
||||
|
||||
useInstanceStore().set({
|
||||
path: 'limits.pollLimits',
|
||||
value: metadata.pollLimits,
|
||||
})
|
||||
const uploadLimits = metadata.uploadLimits
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'uploadlimit',
|
||||
useInstanceStore().set({
|
||||
name: 'limits.uploadlimit',
|
||||
value: parseInt(uploadLimits.general),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'avatarlimit',
|
||||
useInstanceStore().set({
|
||||
name: 'limits.avatarlimit',
|
||||
value: parseInt(uploadLimits.avatar),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'backgroundlimit',
|
||||
useInstanceStore().set({
|
||||
name: 'limits.backgroundlimit',
|
||||
value: parseInt(uploadLimits.background),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'bannerlimit',
|
||||
useInstanceStore().set({
|
||||
name: 'limits.bannerlimit',
|
||||
value: parseInt(uploadLimits.banner),
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'fieldsLimits',
|
||||
useInstanceStore().set({
|
||||
name: 'limits.fieldsLimits',
|
||||
value: metadata.fieldsLimits,
|
||||
})
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'restrictedNicknames',
|
||||
value: metadata.restrictedNicknames,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'postFormats',
|
||||
useInstanceStore().set({
|
||||
name: 'featureSet.postFormats',
|
||||
value: metadata.postFormats,
|
||||
})
|
||||
|
||||
const suggestions = metadata.suggestions
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'suggestionsEnabled',
|
||||
useInstanceStore().set({
|
||||
name: 'featureSet.suggestionsEnabled',
|
||||
value: suggestions.enabled,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'suggestionsWeb',
|
||||
useInstanceStore().set({
|
||||
name: 'featureSet.suggestionsWeb',
|
||||
value: suggestions.web,
|
||||
})
|
||||
|
||||
const software = data.software
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'backendVersion',
|
||||
value: software.version,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'backendRepository',
|
||||
value: software.repository,
|
||||
})
|
||||
|
||||
const priv = metadata.private
|
||||
store.dispatch('setInstanceOption', { name: 'private', value: priv })
|
||||
useInstanceStore().set({ name: 'private', value: priv })
|
||||
|
||||
const frontendVersion = window.___pleromafe_commit_hash
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'frontendVersion',
|
||||
value: frontendVersion,
|
||||
})
|
||||
|
||||
const federation = metadata.federation
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'tagPolicyAvailable',
|
||||
useInstanceStore().set({
|
||||
name: 'featureSet.tagPolicyAvailable',
|
||||
value:
|
||||
typeof federation.mrf_policies === 'undefined'
|
||||
? false
|
||||
: metadata.federation.mrf_policies.includes('TagPolicy'),
|
||||
})
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'federationPolicy',
|
||||
value: federation,
|
||||
})
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'federating',
|
||||
value:
|
||||
typeof federation.enabled === 'undefined' ? true : federation.enabled,
|
||||
})
|
||||
|
||||
const accountActivationRequired = metadata.accountActivationRequired
|
||||
store.dispatch('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'accountActivationRequired',
|
||||
value: accountActivationRequired,
|
||||
})
|
||||
|
|
@ -526,7 +531,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
|
|||
typeof overrides.target !== 'undefined'
|
||||
? overrides.target
|
||||
: window.location.origin
|
||||
store.dispatch('setInstanceOption', { name: 'server', value: server })
|
||||
useInstanceStore().set({ name: 'server', value: server })
|
||||
|
||||
await setConfig({ store })
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ const EmojiInput = {
|
|||
},
|
||||
computed: {
|
||||
padEmoji() {
|
||||
return this.$store.getters.mergedConfig.padEmoji
|
||||
return useEmojiStore().mergedConfig.padEmoji
|
||||
},
|
||||
defaultCandidateIndex() {
|
||||
return this.$store.getters.mergedConfig.autocompleteSelect ? 0 : -1
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* suggest - generates a suggestor function to be used by emoji-input
|
||||
* data: object providing source information for specific types of suggestions:
|
||||
* data.emoji - optional, an array of all emoji available i.e.
|
||||
* (getters.standardEmojiList + state.instance.customEmoji)
|
||||
* (useEmojiStore().standardEmojiList + state.instance.customEmoji)
|
||||
* data.users - optional, an array of all known users
|
||||
* updateUsersList - optional, a function to search and append to users
|
||||
*
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { defineAsyncComponent } from 'vue'
|
|||
|
||||
import Popover from 'src/components/popover/popover.vue'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||
import { ensureFinalFallback } from '../../i18n/languages.js'
|
||||
import Checkbox from '../checkbox/checkbox.vue'
|
||||
import StillImage from '../still-image/still-image.vue'
|
||||
|
|
@ -359,7 +360,7 @@ const EmojiPicker = {
|
|||
if (this.hideCustomEmoji || this.hideCustomEmojiInPicker) {
|
||||
return {}
|
||||
}
|
||||
const emojis = this.$store.getters.groupedCustomEmojis
|
||||
const emojis = useEmojiStore().groupedCustomEmojis
|
||||
if (emojis.unpacked) {
|
||||
emojis.unpacked.text = this.$t('emoji.unpacked')
|
||||
}
|
||||
|
|
@ -369,7 +370,7 @@ const EmojiPicker = {
|
|||
return Object.keys(this.allCustomGroups)[0]
|
||||
},
|
||||
unicodeEmojiGroups() {
|
||||
return this.$store.getters.standardEmojiGroupList.map((group) => ({
|
||||
return useEmojiStore().standardEmojiGroupList.map((group) => ({
|
||||
id: `standard-${group.id}`,
|
||||
text: this.$t(`emoji.unicode_groups.${group.id}`),
|
||||
icon: UNICODE_EMOJI_GROUP_ICON[group.id],
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import Gallery from 'src/components/gallery/gallery.vue'
|
|||
import Popover from 'src/components/popover/popover.vue'
|
||||
import { pollFormToMasto } from 'src/services/poll/poll.service.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMediaViewerStore } from 'src/stores/media_viewer.js'
|
||||
import { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js'
|
||||
|
|
@ -259,8 +260,8 @@ const PostStatusForm = {
|
|||
emojiUserSuggestor() {
|
||||
return suggestor({
|
||||
emoji: [
|
||||
...this.$store.getters.standardEmojiList,
|
||||
...useInstanceStore().customEmoji,
|
||||
...useEmojiStore().standardEmojiList,
|
||||
...useEmojiStore().customEmoji,
|
||||
],
|
||||
store: this.$store,
|
||||
})
|
||||
|
|
@ -268,16 +269,16 @@ const PostStatusForm = {
|
|||
emojiSuggestor() {
|
||||
return suggestor({
|
||||
emoji: [
|
||||
...this.$store.getters.standardEmojiList,
|
||||
...useInstanceStore().customEmoji,
|
||||
...useEmojiStore().standardEmojiList,
|
||||
...useEmojiStore().customEmoji,
|
||||
],
|
||||
})
|
||||
},
|
||||
emoji() {
|
||||
return this.$store.getters.standardEmojiList || []
|
||||
return useEmojiStore().standardEmojiList || []
|
||||
},
|
||||
customEmoji() {
|
||||
return useInstanceStore().customEmoji || []
|
||||
return useEmojiStore().customEmoji || []
|
||||
},
|
||||
statusLength() {
|
||||
return this.newStatus.status.length
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import { set } from 'lodash'
|
|||
|
||||
import { useI18nStore } from 'src/stores/i18n.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||
import messages from '../i18n/messages'
|
||||
import localeService from '../services/locale/locale.service.js'
|
||||
import { applyConfig } from '../services/style_setter/style_setter.js'
|
||||
|
|
@ -43,24 +45,23 @@ export const instanceDefaultProperties = Object.keys(instanceDefaultConfig)
|
|||
const config = {
|
||||
state: { ...defaultState },
|
||||
getters: {
|
||||
defaultConfig(state, getters, rootState) {
|
||||
const { instance } = rootState
|
||||
defaultConfig() {
|
||||
return {
|
||||
...defaultState,
|
||||
...Object.fromEntries(
|
||||
instanceDefaultProperties.map((key) => [key, instance[key]]),
|
||||
instanceDefaultProperties.map((key) => [key, useInstanceStore()[key]]),
|
||||
),
|
||||
}
|
||||
},
|
||||
mergedConfig(state, getters, rootState, rootGetters) {
|
||||
const { defaultConfig } = rootGetters
|
||||
return {
|
||||
...defaultConfig,
|
||||
// Do not override with undefined
|
||||
...Object.fromEntries(
|
||||
Object.entries(state).filter(([, v]) => v !== undefined),
|
||||
),
|
||||
}
|
||||
mergedConfig(state) {
|
||||
const instancePrefs = useInstanceStore().prefsStorage
|
||||
const result = Object.fromEntries(
|
||||
Object.entries(defaultState).map(([k, v]) => [
|
||||
k,
|
||||
v ?? instancePrefs[k],
|
||||
]),
|
||||
)
|
||||
return result
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
|
|
@ -177,7 +178,7 @@ const config = {
|
|||
}
|
||||
case 'interfaceLanguage':
|
||||
messages.setLanguage(useI18nStore().i18n, value)
|
||||
dispatch('loadUnicodeEmojiData', value)
|
||||
useEmojiStore().loadUnicodeEmojiData(value)
|
||||
Cookies.set(
|
||||
BACKEND_LANGUAGE_COOKIE_NAME,
|
||||
localeService.internalToBackendLocaleMulti(value),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export const staticOrApiConfigDefault = {
|
|||
redirectRootLogin: '/main/friends',
|
||||
redirectRootNoLogin: '/main/all',
|
||||
hideSitename: false,
|
||||
nsfwCensorImage: undefined,
|
||||
nsfwCensorImage: null,
|
||||
showFeaturesPanel: true,
|
||||
showInstanceSpecificPanel: false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,12 @@ import api from './api.js'
|
|||
import chats from './chats.js'
|
||||
import config from './config.js'
|
||||
import drafts from './drafts.js'
|
||||
import instance from './instance.js'
|
||||
import notifications from './notifications.js'
|
||||
import profileConfig from './profileConfig.js'
|
||||
import statuses from './statuses.js'
|
||||
import users from './users.js'
|
||||
|
||||
export default {
|
||||
instance,
|
||||
statuses,
|
||||
notifications,
|
||||
users,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
import { declarations } from 'src/modules/config_declaration'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
import apiService from '../services/api/api.service.js'
|
||||
|
|
@ -702,7 +703,7 @@ const users = {
|
|||
useServerSideStorageStore().setServerSideStorage(user)
|
||||
commit('addNewUsers', [user])
|
||||
|
||||
dispatch('fetchEmoji')
|
||||
useEmojiStore().fetchEmoji()
|
||||
|
||||
getNotificationPermission().then((permission) =>
|
||||
useInterfaceStore().setNotificationPermission(permission),
|
||||
|
|
|
|||
246
src/stores/emoji.js
Normal file
246
src/stores/emoji.js
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
|
||||
import { ensureFinalFallback } from 'src/i18n/languages.js'
|
||||
|
||||
import { annotationsLoader } from 'virtual:pleroma-fe/emoji-annotations'
|
||||
|
||||
const defaultState = {
|
||||
// Custom emoji from server
|
||||
customEmoji: [],
|
||||
customEmojiFetched: false,
|
||||
|
||||
// Unicode emoji from bundle
|
||||
emoji: {},
|
||||
emojiFetched: false,
|
||||
unicodeEmojiAnnotations: {},
|
||||
}
|
||||
|
||||
const SORTED_EMOJI_GROUP_IDS = [
|
||||
'smileys-and-emotion',
|
||||
'people-and-body',
|
||||
'animals-and-nature',
|
||||
'food-and-drink',
|
||||
'travel-and-places',
|
||||
'activities',
|
||||
'objects',
|
||||
'symbols',
|
||||
'flags',
|
||||
]
|
||||
|
||||
const REGIONAL_INDICATORS = (() => {
|
||||
const start = 0x1f1e6
|
||||
const end = 0x1f1ff
|
||||
const A = 'A'.codePointAt(0)
|
||||
const res = new Array(end - start + 1)
|
||||
for (let i = start; i <= end; ++i) {
|
||||
const letter = String.fromCodePoint(A + i - start)
|
||||
res[i - start] = {
|
||||
replacement: String.fromCodePoint(i),
|
||||
imageUrl: false,
|
||||
displayText: 'regional_indicator_' + letter,
|
||||
displayTextI18n: {
|
||||
key: 'emoji.regional_indicator',
|
||||
args: { letter },
|
||||
},
|
||||
}
|
||||
}
|
||||
return res
|
||||
})()
|
||||
|
||||
const loadAnnotations = (lang) => {
|
||||
return annotationsLoader[lang]().then((k) => k.default)
|
||||
}
|
||||
|
||||
const injectAnnotations = (emoji, annotations) => {
|
||||
const availableLangs = Object.keys(annotations)
|
||||
|
||||
return {
|
||||
...emoji,
|
||||
annotations: availableLangs.reduce((acc, cur) => {
|
||||
acc[cur] = annotations[cur][emoji.replacement]
|
||||
return acc
|
||||
}, {}),
|
||||
}
|
||||
}
|
||||
|
||||
const injectRegionalIndicators = (groups) => {
|
||||
groups.symbols.push(...REGIONAL_INDICATORS)
|
||||
return groups
|
||||
}
|
||||
|
||||
export const useEmojiStore = defineStore('emoji', {
|
||||
state: () => ({ ...defaultState }),
|
||||
getters: {
|
||||
groupedCustomEmojis(state) {
|
||||
const packsOf = (emoji) => {
|
||||
const packs = emoji.tags
|
||||
.filter((k) => k.startsWith('pack:'))
|
||||
.map((k) => {
|
||||
const packName = k.slice(5) // remove 'pack:' prefix
|
||||
return {
|
||||
id: `custom-${packName}`,
|
||||
text: packName,
|
||||
}
|
||||
})
|
||||
|
||||
if (!packs.length) {
|
||||
return [
|
||||
{
|
||||
id: 'unpacked',
|
||||
},
|
||||
]
|
||||
} else {
|
||||
return packs
|
||||
}
|
||||
}
|
||||
|
||||
return this.customEmoji.reduce((res, emoji) => {
|
||||
packsOf(emoji).forEach(({ id: packId, text: packName }) => {
|
||||
if (!res[packId]) {
|
||||
res[packId] = {
|
||||
id: packId,
|
||||
text: packName,
|
||||
image: emoji.imageUrl,
|
||||
emojis: [],
|
||||
}
|
||||
}
|
||||
res[packId].emojis.push(emoji)
|
||||
})
|
||||
return res
|
||||
}, {})
|
||||
},
|
||||
standardEmojiList(state) {
|
||||
return SORTED_EMOJI_GROUP_IDS.map((groupId) =>
|
||||
(this.emoji[groupId] || []).map((k) =>
|
||||
injectAnnotations(k, this.unicodeEmojiAnnotations),
|
||||
),
|
||||
).reduce((a, b) => a.concat(b), [])
|
||||
},
|
||||
standardEmojiGroupList(state) {
|
||||
return SORTED_EMOJI_GROUP_IDS.map((groupId) => ({
|
||||
id: groupId,
|
||||
emojis: (this.emoji[groupId] || []).map((k) =>
|
||||
injectAnnotations(k, this.unicodeEmojiAnnotations),
|
||||
),
|
||||
}))
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async getStaticEmoji() {
|
||||
try {
|
||||
// See build/emojis_plugin for more details
|
||||
const values = (await import('/src/assets/emoji.json')).default
|
||||
|
||||
const emoji = Object.keys(values).reduce((res, groupId) => {
|
||||
res[groupId] = values[groupId].map((e) => ({
|
||||
displayText: e.slug,
|
||||
imageUrl: false,
|
||||
replacement: e.emoji,
|
||||
}))
|
||||
return res
|
||||
}, {})
|
||||
this.emoji = injectRegionalIndicators(emoji)
|
||||
} catch (e) {
|
||||
console.warn("Can't load static emoji\n", e)
|
||||
}
|
||||
},
|
||||
|
||||
loadUnicodeEmojiData(language) {
|
||||
const langList = ensureFinalFallback(language)
|
||||
|
||||
return Promise.all(
|
||||
langList.map(async (lang) => {
|
||||
if (!this.unicodeEmojiAnnotations[lang]) {
|
||||
try {
|
||||
const annotations = await loadAnnotations(lang)
|
||||
this.unicodeEmojiAnnotations[lang] = annotations
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Error loading unicode emoji annotations for ${lang}: `,
|
||||
e,
|
||||
)
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
},
|
||||
|
||||
async getCustomEmoji() {
|
||||
try {
|
||||
let res = await window.fetch('/api/v1/pleroma/emoji')
|
||||
if (!res.ok) {
|
||||
res = await window.fetch('/api/pleroma/emoji.json')
|
||||
}
|
||||
if (res.ok) {
|
||||
const result = await res.json()
|
||||
const values = Array.isArray(result)
|
||||
? Object.assign({}, ...result)
|
||||
: result
|
||||
const caseInsensitiveStrCmp = (a, b) => {
|
||||
const la = a.toLowerCase()
|
||||
const lb = b.toLowerCase()
|
||||
return la > lb ? 1 : la < lb ? -1 : 0
|
||||
}
|
||||
const noPackLast = (a, b) => {
|
||||
const aNull = a === ''
|
||||
const bNull = b === ''
|
||||
if (aNull === bNull) {
|
||||
return 0
|
||||
} else if (aNull && !bNull) {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
const byPackThenByName = (a, b) => {
|
||||
const packOf = (emoji) =>
|
||||
(emoji.tags.filter((k) => k.startsWith('pack:'))[0] || '').slice(
|
||||
5,
|
||||
)
|
||||
const packOfA = packOf(a)
|
||||
const packOfB = packOf(b)
|
||||
return (
|
||||
noPackLast(packOfA, packOfB) ||
|
||||
caseInsensitiveStrCmp(packOfA, packOfB) ||
|
||||
caseInsensitiveStrCmp(a.displayText, b.displayText)
|
||||
)
|
||||
}
|
||||
|
||||
const emoji = Object.entries(values)
|
||||
.map(([key, value]) => {
|
||||
const imageUrl = value.image_url
|
||||
return {
|
||||
displayText: key,
|
||||
imageUrl: imageUrl
|
||||
? useInstanceStore().server + imageUrl
|
||||
: value,
|
||||
tags: imageUrl
|
||||
? value.tags.sort((a, b) => (a > b ? 1 : 0))
|
||||
: ['utf'],
|
||||
replacement: `:${key}: `,
|
||||
}
|
||||
// Technically could use tags but those are kinda useless right now,
|
||||
// should have been "pack" field, that would be more useful
|
||||
})
|
||||
.sort(byPackThenByName)
|
||||
this.customEmoji = emoji
|
||||
} else {
|
||||
throw res
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Can't load custom emojis\n", e)
|
||||
}
|
||||
},
|
||||
fetchEmoji() {
|
||||
if (!this.customEmojiFetched) {
|
||||
this.getCustomEmoji().then(() => (this.customEmojiFetched = true))
|
||||
}
|
||||
if (!this.emojiFetched) {
|
||||
this.getStaticEmoji().then(() => (this.emojiFetched = true))
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -129,10 +129,10 @@ export const useInstanceStore = defineStore('instance', {
|
|||
},
|
||||
},
|
||||
actions: {
|
||||
set({ path, value }) {
|
||||
if (get(defaultState, path) === undefined)
|
||||
console.error(`Unknown instance option ${path}, value: ${value}`)
|
||||
set(this, path, value)
|
||||
set({ path, name, value }) {
|
||||
if (get(defaultState, path ?? name) === undefined)
|
||||
console.error(`Unknown instance option ${path ?? name}, value: ${value}`)
|
||||
set(this, path ?? name, value)
|
||||
switch (name) {
|
||||
case 'name':
|
||||
useInterfaceStore().setPageTitle()
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ export const useInterfaceStore = defineStore('interface', {
|
|||
},
|
||||
setPageTitle(option = '') {
|
||||
try {
|
||||
document.title = `${option} ${window.vuex.useInstanceStore().name}`
|
||||
document.title = `${option} ${useInstanceStore().name}`
|
||||
} catch (error) {
|
||||
console.error(`${error}`)
|
||||
}
|
||||
|
|
@ -222,14 +222,14 @@ export const useInterfaceStore = defineStore('interface', {
|
|||
async fetchPalettesIndex() {
|
||||
try {
|
||||
const value = await getResourcesIndex('/static/palettes/index.json')
|
||||
window.vuex.commit('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'palettesIndex',
|
||||
value,
|
||||
})
|
||||
return value
|
||||
} catch (e) {
|
||||
console.error('Could not fetch palettes index', e)
|
||||
window.vuex.commit('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'palettesIndex',
|
||||
value: { _error: e },
|
||||
})
|
||||
|
|
@ -258,11 +258,11 @@ export const useInterfaceStore = defineStore('interface', {
|
|||
'/static/styles/index.json',
|
||||
deserialize,
|
||||
)
|
||||
window.vuex.commit('setInstanceOption', { name: 'stylesIndex', value })
|
||||
useInstanceStore().set({ name: 'stylesIndex', value })
|
||||
return value
|
||||
} catch (e) {
|
||||
console.error('Could not fetch styles index', e)
|
||||
window.vuex.commit('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'stylesIndex',
|
||||
value: { _error: e },
|
||||
})
|
||||
|
|
@ -296,11 +296,11 @@ export const useInterfaceStore = defineStore('interface', {
|
|||
async fetchThemesIndex() {
|
||||
try {
|
||||
const value = await getResourcesIndex('/static/styles.json')
|
||||
window.vuex.commit('setInstanceOption', { name: 'themesIndex', value })
|
||||
useInstanceStore().set({ name: 'themesIndex', value })
|
||||
return value
|
||||
} catch (e) {
|
||||
console.error('Could not fetch themes index', e)
|
||||
window.vuex.commit('setInstanceOption', {
|
||||
useInstanceStore().set({
|
||||
name: 'themesIndex',
|
||||
value: { _error: e },
|
||||
})
|
||||
|
|
@ -386,14 +386,14 @@ export const useInterfaceStore = defineStore('interface', {
|
|||
}
|
||||
|
||||
const { style: instanceStyleName, palette: instancePaletteName } =
|
||||
window.vuex.useInstanceStore()
|
||||
useInstanceStore()
|
||||
|
||||
let {
|
||||
theme: instanceThemeV2Name,
|
||||
themesIndex,
|
||||
stylesIndex,
|
||||
palettesIndex,
|
||||
} = window.vuex.useInstanceStore()
|
||||
} = useInstanceStore()
|
||||
|
||||
const {
|
||||
style: userStyleName,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue