logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://anongit.hacktivis.me/git/pleroma-fe.git/

instance.js (12071B)


  1. import apiService from '../services/api/api.service.js'
  2. import { instanceDefaultProperties } from './config.js'
  3. import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js'
  4. import { useInterfaceStore } from 'src/stores/interface.js'
  5. const SORTED_EMOJI_GROUP_IDS = [
  6. 'smileys-and-emotion',
  7. 'people-and-body',
  8. 'animals-and-nature',
  9. 'food-and-drink',
  10. 'travel-and-places',
  11. 'activities',
  12. 'objects',
  13. 'symbols',
  14. 'flags'
  15. ]
  16. const REGIONAL_INDICATORS = (() => {
  17. const start = 0x1F1E6
  18. const end = 0x1F1FF
  19. const A = 'A'.codePointAt(0)
  20. const res = new Array(end - start + 1)
  21. for (let i = start; i <= end; ++i) {
  22. const letter = String.fromCodePoint(A + i - start)
  23. res[i - start] = {
  24. replacement: String.fromCodePoint(i),
  25. imageUrl: false,
  26. displayText: 'regional_indicator_' + letter,
  27. displayTextI18n: {
  28. key: 'emoji.regional_indicator',
  29. args: { letter }
  30. }
  31. }
  32. }
  33. return res
  34. })()
  35. const REMOTE_INTERACTION_URL = '/main/ostatus'
  36. const defaultState = {
  37. // Stuff from apiConfig
  38. name: 'Pleroma FE',
  39. registrationOpen: true,
  40. server: 'http://localhost:4040/',
  41. textlimit: 5000,
  42. themesIndex: undefined,
  43. stylesIndex: undefined,
  44. palettesIndex: undefined,
  45. themeData: undefined, // used for theme editor v2
  46. vapidPublicKey: undefined,
  47. // Stuff from static/config.json
  48. alwaysShowSubjectInput: true,
  49. defaultAvatar: '/images/avi.png',
  50. defaultBanner: '/images/banner.png',
  51. background: '/static/aurora_borealis.jpg',
  52. embeddedToS: true,
  53. collapseMessageWithSubject: false,
  54. greentext: false,
  55. useAtIcon: false,
  56. mentionLinkDisplay: 'short',
  57. mentionLinkShowTooltip: true,
  58. mentionLinkShowAvatar: false,
  59. mentionLinkFadeDomain: true,
  60. mentionLinkShowYous: false,
  61. mentionLinkBoldenYou: true,
  62. hideFilteredStatuses: false,
  63. // bad name: actually hides posts of muted USERS
  64. hideMutedPosts: false,
  65. hideMutedThreads: true,
  66. hideWordFilteredPosts: false,
  67. hidePostStats: false,
  68. hideBotIndication: false,
  69. hideSitename: false,
  70. hideUserStats: false,
  71. muteBotStatuses: false,
  72. muteSensitiveStatuses: false,
  73. modalOnRepeat: false,
  74. modalOnUnfollow: false,
  75. modalOnBlock: true,
  76. modalOnMute: false,
  77. modalOnMuteConversation: false,
  78. modalOnMuteDomain: true,
  79. modalOnDelete: true,
  80. modalOnLogout: true,
  81. modalOnApproveFollow: false,
  82. modalOnDenyFollow: false,
  83. modalOnRemoveUserFromFollowers: false,
  84. modalMobileCenter: false,
  85. loginMethod: 'password',
  86. logo: '/static/logo.svg',
  87. logoMargin: '.2em',
  88. logoMask: true,
  89. logoLeft: false,
  90. disableUpdateNotification: false,
  91. minimalScopesMode: false,
  92. nsfwCensorImage: undefined,
  93. postContentType: 'text/plain',
  94. redirectRootLogin: '/main/friends',
  95. redirectRootNoLogin: '/main/all',
  96. scopeCopy: true,
  97. showFeaturesPanel: true,
  98. showInstanceSpecificPanel: false,
  99. sidebarRight: false,
  100. subjectLineBehavior: 'email',
  101. theme: 'pleroma-dark',
  102. palette: null,
  103. style: null,
  104. emojiReactionsScale: 0.5,
  105. textSize: '14px',
  106. emojiSize: '2.2rem',
  107. navbarSize: '3.5rem',
  108. panelHeaderSize: '3.2rem',
  109. forcedRoundness: -1,
  110. fontsOverride: {},
  111. virtualScrolling: true,
  112. sensitiveByDefault: false,
  113. conversationDisplay: 'linear',
  114. conversationTreeAdvanced: false,
  115. conversationOtherRepliesButton: 'below',
  116. conversationTreeFadeAncestors: false,
  117. showExtraNotifications: true,
  118. showExtraNotificationsTip: true,
  119. showChatsInExtraNotifications: true,
  120. showAnnouncementsInExtraNotifications: true,
  121. showFollowRequestsInExtraNotifications: true,
  122. maxDepthInThread: 6,
  123. autocompleteSelect: false,
  124. closingDrawerMarksAsSeen: true,
  125. unseenAtTop: false,
  126. ignoreInactionableSeen: false,
  127. unsavedPostAction: 'confirm',
  128. autoSaveDraft: false,
  129. useAbsoluteTimeFormat: false,
  130. absoluteTimeFormatMinAge: '0d',
  131. absoluteTime12h: '24h',
  132. // Nasty stuff
  133. customEmoji: [],
  134. customEmojiFetched: false,
  135. emoji: {},
  136. emojiFetched: false,
  137. unicodeEmojiAnnotations: {},
  138. pleromaBackend: true,
  139. postFormats: [],
  140. restrictedNicknames: [],
  141. safeDM: true,
  142. knownDomains: [],
  143. birthdayRequired: false,
  144. birthdayMinAge: 0,
  145. // Feature-set, apparently, not everything here is reported...
  146. shoutAvailable: false,
  147. pleromaChatMessagesAvailable: false,
  148. pleromaCustomEmojiReactionsAvailable: false,
  149. pleromaBookmarkFoldersAvailable: false,
  150. gopherAvailable: false,
  151. mediaProxyAvailable: false,
  152. suggestionsEnabled: false,
  153. suggestionsWeb: '',
  154. quotingAvailable: false,
  155. groupActorAvailable: false,
  156. // Html stuff
  157. instanceSpecificPanelContent: '',
  158. tos: '',
  159. // Version Information
  160. backendVersion: '',
  161. backendRepository: '',
  162. frontendVersion: '',
  163. pollsAvailable: false,
  164. pollLimits: {
  165. max_options: 4,
  166. max_option_chars: 255,
  167. min_expiration: 60,
  168. max_expiration: 60 * 60 * 24
  169. }
  170. }
  171. const loadAnnotations = (lang) => {
  172. return import(
  173. /* webpackChunkName: "emoji-annotations/[request]" */
  174. `@kazvmoe-infra/unicode-emoji-json/annotations/${langCodeToCldrName(lang)}.json`
  175. )
  176. .then(k => k.default)
  177. }
  178. const injectAnnotations = (emoji, annotations) => {
  179. const availableLangs = Object.keys(annotations)
  180. return {
  181. ...emoji,
  182. annotations: availableLangs.reduce((acc, cur) => {
  183. acc[cur] = annotations[cur][emoji.replacement]
  184. return acc
  185. }, {})
  186. }
  187. }
  188. const injectRegionalIndicators = groups => {
  189. groups.symbols.push(...REGIONAL_INDICATORS)
  190. return groups
  191. }
  192. const instance = {
  193. state: defaultState,
  194. mutations: {
  195. setInstanceOption (state, { name, value }) {
  196. if (typeof value !== 'undefined') {
  197. state[name] = value
  198. }
  199. },
  200. setKnownDomains (state, domains) {
  201. state.knownDomains = domains
  202. },
  203. setUnicodeEmojiAnnotations (state, { lang, annotations }) {
  204. state.unicodeEmojiAnnotations[lang] = annotations
  205. }
  206. },
  207. getters: {
  208. instanceDefaultConfig (state) {
  209. return instanceDefaultProperties
  210. .map(key => [key, state[key]])
  211. .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
  212. },
  213. groupedCustomEmojis (state) {
  214. const packsOf = emoji => {
  215. const packs = emoji.tags
  216. .filter(k => k.startsWith('pack:'))
  217. .map(k => {
  218. const packName = k.slice(5) // remove 'pack:' prefix
  219. return {
  220. id: `custom-${packName}`,
  221. text: packName
  222. }
  223. })
  224. if (!packs.length) {
  225. return [{
  226. id: 'unpacked'
  227. }]
  228. } else {
  229. return packs
  230. }
  231. }
  232. return state.customEmoji
  233. .reduce((res, emoji) => {
  234. packsOf(emoji).forEach(({ id: packId, text: packName }) => {
  235. if (!res[packId]) {
  236. res[packId] = ({
  237. id: packId,
  238. text: packName,
  239. image: emoji.imageUrl,
  240. emojis: []
  241. })
  242. }
  243. res[packId].emojis.push(emoji)
  244. })
  245. return res
  246. }, {})
  247. },
  248. standardEmojiList (state) {
  249. return SORTED_EMOJI_GROUP_IDS
  250. .map(groupId => (state.emoji[groupId] || []).map(k => injectAnnotations(k, state.unicodeEmojiAnnotations)))
  251. .reduce((a, b) => a.concat(b), [])
  252. },
  253. standardEmojiGroupList (state) {
  254. return SORTED_EMOJI_GROUP_IDS.map(groupId => ({
  255. id: groupId,
  256. emojis: (state.emoji[groupId] || []).map(k => injectAnnotations(k, state.unicodeEmojiAnnotations))
  257. }))
  258. },
  259. instanceDomain (state) {
  260. return new URL(state.server).hostname
  261. },
  262. remoteInteractionLink (state) {
  263. const server = state.server.endsWith('/') ? state.server.slice(0, -1) : state.server
  264. const link = server + REMOTE_INTERACTION_URL
  265. return ({ statusId, nickname }) => {
  266. if (statusId) {
  267. return `${link}?status_id=${statusId}`
  268. } else {
  269. return `${link}?nickname=${nickname}`
  270. }
  271. }
  272. }
  273. },
  274. actions: {
  275. setInstanceOption ({ commit, dispatch }, { name, value }) {
  276. commit('setInstanceOption', { name, value })
  277. switch (name) {
  278. case 'name':
  279. useInterfaceStore().setPageTitle()
  280. break
  281. case 'shoutAvailable':
  282. if (value) {
  283. dispatch('initializeSocket')
  284. }
  285. break
  286. }
  287. },
  288. async getStaticEmoji ({ commit }) {
  289. try {
  290. const values = (await import(/* webpackChunkName: 'emoji' */ '../../static/emoji.json')).default
  291. const emoji = Object.keys(values).reduce((res, groupId) => {
  292. res[groupId] = values[groupId].map(e => ({
  293. displayText: e.slug,
  294. imageUrl: false,
  295. replacement: e.emoji
  296. }))
  297. return res
  298. }, {})
  299. commit('setInstanceOption', { name: 'emoji', value: injectRegionalIndicators(emoji) })
  300. } catch (e) {
  301. console.warn("Can't load static emoji\n", e)
  302. }
  303. },
  304. loadUnicodeEmojiData ({ commit, state }, language) {
  305. const langList = ensureFinalFallback(language)
  306. return Promise.all(
  307. langList
  308. .map(async lang => {
  309. if (!state.unicodeEmojiAnnotations[lang]) {
  310. try {
  311. const annotations = await loadAnnotations(lang)
  312. commit('setUnicodeEmojiAnnotations', { lang, annotations })
  313. } catch (e) {
  314. console.warn(`Error loading unicode emoji annotations for ${lang}: `, e)
  315. // ignore
  316. }
  317. }
  318. }))
  319. },
  320. async getCustomEmoji ({ commit, state }) {
  321. try {
  322. const res = await window.fetch('/api/pleroma/emoji.json')
  323. if (res.ok) {
  324. const result = await res.json()
  325. const values = Array.isArray(result) ? Object.assign({}, ...result) : result
  326. const caseInsensitiveStrCmp = (a, b) => {
  327. const la = a.toLowerCase()
  328. const lb = b.toLowerCase()
  329. return la > lb ? 1 : (la < lb ? -1 : 0)
  330. }
  331. const noPackLast = (a, b) => {
  332. const aNull = a === ''
  333. const bNull = b === ''
  334. if (aNull === bNull) {
  335. return 0
  336. } else if (aNull && !bNull) {
  337. return 1
  338. } else {
  339. return -1
  340. }
  341. }
  342. const byPackThenByName = (a, b) => {
  343. const packOf = emoji => (emoji.tags.filter(k => k.startsWith('pack:'))[0] || '').slice(5)
  344. const packOfA = packOf(a)
  345. const packOfB = packOf(b)
  346. return noPackLast(packOfA, packOfB) || caseInsensitiveStrCmp(packOfA, packOfB) || caseInsensitiveStrCmp(a.displayText, b.displayText)
  347. }
  348. const emoji = Object.entries(values).map(([key, value]) => {
  349. const imageUrl = value.image_url
  350. return {
  351. displayText: key,
  352. imageUrl: imageUrl ? state.server + imageUrl : value,
  353. tags: imageUrl ? value.tags.sort((a, b) => a > b ? 1 : 0) : ['utf'],
  354. replacement: `:${key}: `
  355. }
  356. // Technically could use tags but those are kinda useless right now,
  357. // should have been "pack" field, that would be more useful
  358. }).sort(byPackThenByName)
  359. commit('setInstanceOption', { name: 'customEmoji', value: emoji })
  360. } else {
  361. throw (res)
  362. }
  363. } catch (e) {
  364. console.warn("Can't load custom emojis\n", e)
  365. }
  366. },
  367. fetchEmoji ({ dispatch, state }) {
  368. if (!state.customEmojiFetched) {
  369. state.customEmojiFetched = true
  370. dispatch('getCustomEmoji')
  371. }
  372. if (!state.emojiFetched) {
  373. state.emojiFetched = true
  374. dispatch('getStaticEmoji')
  375. }
  376. },
  377. async getKnownDomains ({ commit, rootState }) {
  378. try {
  379. const result = await apiService.fetchKnownDomains({
  380. credentials: rootState.users.currentUser.credentials
  381. })
  382. commit('setKnownDomains', result)
  383. } catch (e) {
  384. console.warn("Can't load known domains\n", e)
  385. }
  386. }
  387. }
  388. }
  389. export default instance