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 (12051B)


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