Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
21 KB
Referenced Files
None
Subscribers
None
diff --git a/build/emojis_plugin.js b/build/emojis_plugin.js
new file mode 100644
index 0000000000..aed52066d6
--- /dev/null
+++ b/build/emojis_plugin.js
@@ -0,0 +1,64 @@
+import { resolve } from 'node:path'
+import { access } from 'node:fs/promises'
+import { languages, langCodeToCldrName } from '../src/i18n/languages.js'
+
+const annotationsImportPrefix = '@kazvmoe-infra/unicode-emoji-json/annotations/'
+const specialAnnotationsLocale = {
+ ja_easy: 'ja'
+}
+
+const internalToAnnotationsLocale = (internal) => specialAnnotationsLocale[internal] || internal
+
+// This gets all the annotations that are accessible (whose language
+// can be chosen in the settings). Data for other languages are
+// discarded because there is no way for it to be fetched.
+const getAllAccessibleAnnotations = async (projectRoot) => {
+ const imports = (await Promise.all(
+ languages
+ .map(async lang => {
+ const destLang = internalToAnnotationsLocale(lang)
+ const importModule = `${annotationsImportPrefix}${destLang}.json`
+ const importFile = resolve(projectRoot, 'node_modules', importModule)
+ try {
+ await access(importFile)
+ return `'${lang}': () => import('${importModule}')`
+ } catch (e) {
+ return
+ }
+ })))
+ .filter(k => k)
+ .join(',\n')
+
+ return `
+export const annotationsLoader = {
+ ${imports}
+}
+`
+}
+
+const emojiAnnotationsId = 'virtual:pleroma-fe/emoji-annotations'
+const emojiAnnotationsIdResolved = '\0' + emojiAnnotationsId
+
+const emojisPlugin = () => {
+ let projectRoot
+ return {
+ name: 'emojis-plugin',
+ configResolved (conf) {
+ projectRoot = conf.root
+ },
+ resolveId (id) {
+ if (id === emojiAnnotationsId) {
+ return emojiAnnotationsIdResolved
+ }
+ return null
+ },
+ async load (id) {
+ if (id === emojiAnnotationsIdResolved) {
+ return await getAllAccessibleAnnotations(projectRoot)
+ }
+ return null
+ }
+ }
+}
+
+export default emojisPlugin
diff --git a/changelog.d/emoji-handling.remove b/changelog.d/emoji-handling.remove
new file mode 100644
index 0000000000..c9ead55452
--- /dev/null
+++ b/changelog.d/emoji-handling.remove
@@ -0,0 +1 @@
+Remove emoji annotations code for unused languages from final build
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 66c1f40f86..7987f7e634 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -1,413 +1,412 @@
import apiService from '../services/api/api.service.js'
import { instanceDefaultProperties } from './config.js'
-import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js'
+import { ensureFinalFallback } from '../i18n/languages.js'
import { useInterfaceStore } from 'src/stores/interface.js'
+// See build/emojis_plugin for more details
+import { annotationsLoader } from 'virtual:pleroma-fe/emoji-annotations'
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 REMOTE_INTERACTION_URL = '/main/ostatus'
const defaultState = {
// Stuff from apiConfig
name: 'Pleroma FE',
registrationOpen: true,
server: 'http://localhost:4040/',
textlimit: 5000,
themesIndex: undefined,
stylesIndex: undefined,
palettesIndex: undefined,
themeData: undefined, // used for theme editor v2
vapidPublicKey: undefined,
// Stuff from static/config.json
alwaysShowSubjectInput: true,
defaultAvatar: '/images/avi.png',
defaultBanner: '/images/banner.png',
background: '/static/aurora_borealis.jpg',
embeddedToS: true,
collapseMessageWithSubject: false,
greentext: false,
mentionLinkDisplay: 'short',
mentionLinkShowTooltip: true,
mentionLinkShowAvatar: false,
mentionLinkFadeDomain: true,
mentionLinkShowYous: false,
mentionLinkBoldenYou: true,
hideFilteredStatuses: false,
// bad name: actually hides posts of muted USERS
hideMutedPosts: false,
hideMutedThreads: true,
hideWordFilteredPosts: false,
hidePostStats: false,
hideBotIndication: false,
hideSitename: false,
hideUserStats: false,
muteBotStatuses: false,
muteSensitiveStatuses: false,
modalOnRepeat: false,
modalOnUnfollow: false,
modalOnBlock: true,
modalOnMute: false,
modalOnMuteConversation: false,
modalOnMuteDomain: true,
modalOnDelete: true,
modalOnLogout: true,
modalOnApproveFollow: false,
modalOnDenyFollow: false,
modalOnRemoveUserFromFollowers: false,
modalMobileCenter: false,
loginMethod: 'password',
logo: '/static/logo.svg',
logoMargin: '.2em',
logoMask: true,
logoLeft: false,
disableUpdateNotification: false,
minimalScopesMode: false,
nsfwCensorImage: undefined,
postContentType: 'text/plain',
redirectRootLogin: '/main/friends',
redirectRootNoLogin: '/main/all',
scopeCopy: true,
showFeaturesPanel: true,
showInstanceSpecificPanel: false,
sidebarRight: false,
subjectLineBehavior: 'email',
theme: 'pleroma-dark',
palette: null,
style: null,
emojiReactionsScale: 0.5,
textSize: '14px',
emojiSize: '2.2rem',
navbarSize: '3.5rem',
panelHeaderSize: '3.2rem',
forcedRoundness: -1,
fontsOverride: {},
virtualScrolling: true,
sensitiveByDefault: false,
conversationDisplay: 'linear',
conversationTreeAdvanced: false,
conversationOtherRepliesButton: 'below',
conversationTreeFadeAncestors: false,
showExtraNotifications: true,
showExtraNotificationsTip: true,
showChatsInExtraNotifications: true,
showAnnouncementsInExtraNotifications: true,
showFollowRequestsInExtraNotifications: true,
maxDepthInThread: 6,
autocompleteSelect: false,
closingDrawerMarksAsSeen: true,
unseenAtTop: false,
ignoreInactionableSeen: false,
unsavedPostAction: 'confirm',
autoSaveDraft: false,
useAbsoluteTimeFormat: false,
absoluteTimeFormatMinAge: '0d',
absoluteTime12h: '24h',
// Nasty stuff
customEmoji: [],
customEmojiFetched: false,
emoji: {},
emojiFetched: false,
unicodeEmojiAnnotations: {},
pleromaBackend: true,
postFormats: [],
restrictedNicknames: [],
safeDM: true,
knownDomains: [],
birthdayRequired: false,
birthdayMinAge: 0,
// Feature-set, apparently, not everything here is reported...
shoutAvailable: false,
pleromaChatMessagesAvailable: false,
pleromaCustomEmojiReactionsAvailable: false,
pleromaBookmarkFoldersAvailable: false,
gopherAvailable: false,
mediaProxyAvailable: false,
suggestionsEnabled: false,
suggestionsWeb: '',
quotingAvailable: false,
groupActorAvailable: false,
// Html stuff
instanceSpecificPanelContent: '',
tos: '',
// Version Information
backendVersion: '',
backendRepository: '',
frontendVersion: '',
pollsAvailable: false,
pollLimits: {
max_options: 4,
max_option_chars: 255,
min_expiration: 60,
max_expiration: 60 * 60 * 24
}
}
const loadAnnotations = (lang) => {
- const code = langCodeToCldrName(lang)
- return import(
- `../../node_modules/@kazvmoe-infra/unicode-emoji-json/annotations/${code}.json`
- )
+ 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
}
const instance = {
state: defaultState,
mutations: {
setInstanceOption (state, { name, value }) {
if (typeof value !== 'undefined') {
state[name] = value
}
},
setKnownDomains (state, domains) {
state.knownDomains = domains
},
setUnicodeEmojiAnnotations (state, { lang, annotations }) {
state.unicodeEmojiAnnotations[lang] = annotations
}
},
getters: {
instanceDefaultConfig (state) {
return instanceDefaultProperties
.map(key => [key, state[key]])
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
},
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 state.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 => (state.emoji[groupId] || []).map(k => injectAnnotations(k, state.unicodeEmojiAnnotations)))
.reduce((a, b) => a.concat(b), [])
},
standardEmojiGroupList (state) {
return SORTED_EMOJI_GROUP_IDS.map(groupId => ({
id: groupId,
emojis: (state.emoji[groupId] || []).map(k => injectAnnotations(k, state.unicodeEmojiAnnotations))
}))
},
instanceDomain (state) {
return new URL(state.server).hostname
},
remoteInteractionLink (state) {
const server = state.server.endsWith('/') ? state.server.slice(0, -1) : state.server
const link = server + REMOTE_INTERACTION_URL
return ({ statusId, nickname }) => {
if (statusId) {
return `${link}?status_id=${statusId}`
} else {
return `${link}?nickname=${nickname}`
}
}
}
},
actions: {
setInstanceOption ({ commit, dispatch }, { name, value }) {
commit('setInstanceOption', { name, value })
switch (name) {
case 'name':
useInterfaceStore().setPageTitle()
break
case 'shoutAvailable':
if (value) {
dispatch('initializeSocket')
}
break
}
},
async getStaticEmoji ({ commit }) {
try {
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
}, {})
commit('setInstanceOption', { name: 'emoji', value: injectRegionalIndicators(emoji) })
} catch (e) {
console.warn("Can't load static emoji\n", e)
}
},
loadUnicodeEmojiData ({ commit, state }, language) {
const langList = ensureFinalFallback(language)
return Promise.all(
langList
.map(async lang => {
if (!state.unicodeEmojiAnnotations[lang]) {
try {
const annotations = await loadAnnotations(lang)
commit('setUnicodeEmojiAnnotations', { lang, annotations })
} catch (e) {
console.warn(`Error loading unicode emoji annotations for ${lang}: `, e)
// ignore
}
}
}))
},
async getCustomEmoji ({ commit, state }) {
try {
const 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 ? state.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)
commit('setInstanceOption', { name: 'customEmoji', value: emoji })
} else {
throw (res)
}
} catch (e) {
console.warn("Can't load custom emojis\n", e)
}
},
fetchEmoji ({ dispatch, state }) {
if (!state.customEmojiFetched) {
state.customEmojiFetched = true
dispatch('getCustomEmoji')
}
if (!state.emojiFetched) {
state.emojiFetched = true
dispatch('getStaticEmoji')
}
},
async getKnownDomains ({ commit, rootState }) {
try {
const result = await apiService.fetchKnownDomains({
credentials: rootState.users.currentUser.credentials
})
commit('setKnownDomains', result)
} catch (e) {
console.warn("Can't load known domains\n", e)
}
}
}
}
export default instance
diff --git a/vite.config.js b/vite.config.js
index 2abf18e507..d57eb32abf 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,202 +1,204 @@
import { fileURLToPath } from 'node:url'
import { dirname, resolve } from 'node:path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import stylelint from 'vite-plugin-stylelint'
import eslint from 'vite-plugin-eslint2'
+import emojisPlugin from './build/emojis_plugin.js'
import { devSwPlugin, buildSwPlugin, swMessagesPlugin } from './build/sw_plugin.js'
import copyPlugin from './build/copy_plugin.js'
import { getCommitHash } from './build/commit_hash.js'
const localConfigPath = '<projectRoot>/config/local.json'
const getLocalDevSettings = async () => {
try {
const settings = (await import('./config/local.json')).default
if (settings.target && settings.target.endsWith('/')) {
// replacing trailing slash since it can conflict with some apis
// and that's how actual BE reports its url
settings.target = settings.target.replace(/\/$/, '')
}
console.info(`Using local dev server settings (${localConfigPath}):`)
console.info(JSON.stringify(settings, null, 2))
return settings
} catch (e) {
console.info(`Local dev server settings not found (${localConfigPath}), using default`, e)
return {}
}
}
const projectRoot = dirname(fileURLToPath(import.meta.url))
const getTransformSWSettings = (settings) => {
if ('transformSW' in settings) {
return settings.transformSW
} else {
console.info(
'`transformSW` is not present in your local settings.\n' +
'This option controls whether the service worker should be bundled and transformed into iife (immediately-invoked function expression) during development.\n' +
'If set to false, the service worker will be served as-is, as an ES Module.\n' +
'Some browsers (e.g. Firefox) does not support ESM service workers.\n' +
'To avoid surprises, it is defaulted to true, but this can be slow.\n' +
'If you are using a browser that supports ESM service workers, you can set this option to false.\n' +
`No matter your choice, you can set the transformSW option in ${localConfigPath} in to disable this message.`
)
return true
}
}
export default defineConfig(async ({ mode, command }) => {
const settings = await getLocalDevSettings()
const target = settings.target || 'http://localhost:4000/'
const transformSW = getTransformSWSettings(settings)
const proxy = {
'/api': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
ws: true
},
'/nodeinfo': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost'
},
'/socket': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
ws: true,
headers: {
'Origin': target
}
},
'/oauth': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost'
}
}
const swSrc = 'src/sw.js'
const swDest = 'sw-pleroma.js'
const alias = {
src: '/src',
components: '/src/components',
...(mode === 'test' ? { vue: 'vue/dist/vue.esm-bundler.js' } : {})
}
return {
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement(tag) {
if (tag === 'pinch-zoom') {
return true
}
return false
}
}
}
}),
vueJsx(),
devSwPlugin({ swSrc, swDest, transformSW, alias }),
buildSwPlugin({ swSrc, swDest }),
swMessagesPlugin(),
+ emojisPlugin(),
copyPlugin({
inUrl: '/static/ruffle',
inFs: resolve(projectRoot, 'node_modules/@ruffle-rs/ruffle')
}),
eslint({
lintInWorker: true,
lintOnStart: true,
cacheLocation: resolve(projectRoot, 'node_modules/.cache/eslintcache')
}),
stylelint({
lintInWorker: true,
lintOnStart: true,
cacheLocation: resolve(projectRoot, 'node_modules/.cache/stylelintcache')
})
],
optimizeDeps: {
// For unknown reasons, during vitest, vite will re-optimize the following
// deps, causing the test to reload, so add them here so that it will not
// reload during tests
include: [
'custom-event-polyfill',
'vue-i18n',
'@ungap/event-target',
'lodash.merge',
'body-scroll-lock',
'@kazvmoe-infra/pinch-zoom-element'
]
},
css: {
devSourcemap: true
},
resolve: {
alias
},
define: {
'process.env': JSON.stringify({
NODE_ENV: mode === 'test' ? 'testing' : command === 'serve' ? 'development' : 'production',
HAS_MODULE_SERVICE_WORKER: command === 'serve' && !transformSW
}),
'COMMIT_HASH': JSON.stringify(command === 'serve' ? 'DEV' : getCommitHash()),
'DEV_OVERRIDES': JSON.stringify(command === 'serve' ? settings : undefined),
'__VUE_OPTIONS_API__': true,
'__VUE_PROD_DEVTOOLS__': false,
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': false
},
build: {
sourcemap: true,
rollupOptions: {
input: {
main: 'index.html'
},
output: {
inlineDynamicImports: false,
entryFileNames (chunkInfo) {
const id = chunkInfo.facadeModuleId
if (id.endsWith(swSrc)) {
return swDest
} else {
return 'static/js/[name].[hash].js'
}
},
chunkFileNames () {
return 'static/js/[name].[hash].js'
},
assetFileNames (assetInfo) {
const name = assetInfo.names?.[0] || ''
if (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(name)) {
return 'static/img/[name].[hash][extname]'
} else if (/\.css$/.test(name)) {
return 'static/css/[name].[hash][extname]'
} else {
return 'static/misc/[name].[hash][extname]'
}
}
}
},
},
server: {
...(mode === 'test' ? {} : { proxy }),
port: Number(process.env.PORT) || 8080
},
preview: {
proxy
},
test: {
globals: true,
browser: {
enabled: true,
provider: 'playwright',
instances: [
{ browser: 'firefox' }
]
}
}
}
})

File Metadata

Mime Type
text/x-diff
Expires
Fri, Oct 30, 3:02 AM (1 d, 5 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1799293
Default Alt Text
(21 KB)

Event Timeline