logo

pleroma-fe

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

style_setter.js (5953B)


  1. import { hex2rgb } from '../color_convert/color_convert.js'
  2. import { generatePreset } from '../theme_data/theme_data.service.js'
  3. import { init } from '../theme_data/theme_data_3.service.js'
  4. import { convertTheme2To3 } from '../theme_data/theme2_to_theme3.js'
  5. import { getCssRules } from '../theme_data/css_utils.js'
  6. import { defaultState } from '../../modules/config.js'
  7. import { chunk } from 'lodash'
  8. export const generateTheme = async (input, callbacks) => {
  9. const {
  10. onNewRule = (rule, isLazy) => {},
  11. onLazyFinished = () => {},
  12. onEagerFinished = () => {}
  13. } = callbacks
  14. let extraRules
  15. if (input.themeFileVersion === 1) {
  16. extraRules = convertTheme2To3(input)
  17. } else {
  18. const { theme } = generatePreset(input)
  19. extraRules = convertTheme2To3(theme)
  20. }
  21. // Assuming that "worst case scenario background" is panel background since it's the most likely one
  22. const themes3 = init(extraRules, extraRules[0].directives['--bg'].split('|')[1].trim())
  23. getCssRules(themes3.eager, themes3.staticVars).forEach(rule => {
  24. // Hacks to support multiple selectors on same component
  25. if (rule.match(/::-webkit-scrollbar-button/)) {
  26. const parts = rule.split(/[{}]/g)
  27. const newRule = [
  28. parts[0],
  29. ', ',
  30. parts[0].replace(/button/, 'thumb'),
  31. ', ',
  32. parts[0].replace(/scrollbar-button/, 'resizer'),
  33. ' {',
  34. parts[1],
  35. '}'
  36. ].join('')
  37. onNewRule(newRule, false)
  38. } else {
  39. onNewRule(rule, false)
  40. }
  41. })
  42. onEagerFinished()
  43. // Optimization - instead of processing all lazy rules in one go, process them in small chunks
  44. // so that UI can do other things and be somewhat responsive while less important rules are being
  45. // processed
  46. let counter = 0
  47. const chunks = chunk(themes3.lazy, 200)
  48. // let t0 = performance.now()
  49. const processChunk = () => {
  50. const chunk = chunks[counter]
  51. Promise.all(chunk.map(x => x())).then(result => {
  52. getCssRules(result.filter(x => x), themes3.staticVars).forEach(rule => {
  53. if (rule.match(/\.modal-view/)) {
  54. const parts = rule.split(/[{}]/g)
  55. const newRule = [
  56. parts[0],
  57. ', ',
  58. parts[0].replace(/\.modal-view/, '#modal'),
  59. ', ',
  60. parts[0].replace(/\.modal-view/, '.shout-panel'),
  61. ' {',
  62. parts[1],
  63. '}'
  64. ].join('')
  65. onNewRule(newRule, true)
  66. } else {
  67. onNewRule(rule, true)
  68. }
  69. })
  70. // const t1 = performance.now()
  71. // console.debug('Chunk ' + counter + ' took ' + (t1 - t0) + 'ms')
  72. // t0 = t1
  73. counter += 1
  74. if (counter < chunks.length) {
  75. setTimeout(processChunk, 0)
  76. } else {
  77. onLazyFinished()
  78. }
  79. })
  80. }
  81. return { lazyProcessFunc: processChunk }
  82. }
  83. export const applyTheme = async (input) => {
  84. const styleSheet = new CSSStyleSheet()
  85. const lazyStyleSheet = new CSSStyleSheet()
  86. const { lazyProcessFunc } = await generateTheme(
  87. input,
  88. {
  89. onNewRule (rule, isLazy) {
  90. if (isLazy) {
  91. lazyStyleSheet.insertRule(rule, 'index-max')
  92. } else {
  93. styleSheet.insertRule(rule, 'index-max')
  94. }
  95. },
  96. onEagerFinished () {
  97. document.adoptedStyleSheets = [styleSheet]
  98. },
  99. onLazyFinished () {
  100. document.adoptedStyleSheets = [styleSheet, lazyStyleSheet]
  101. }
  102. }
  103. )
  104. setTimeout(lazyProcessFunc, 0)
  105. return Promise.resolve()
  106. }
  107. const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale }) =>
  108. ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale })
  109. const defaultConfigColumns = configColumns(defaultState)
  110. export const applyConfig = (config) => {
  111. const columns = configColumns(config)
  112. if (columns === defaultConfigColumns) {
  113. return
  114. }
  115. const head = document.head
  116. const body = document.body
  117. body.classList.add('hidden')
  118. const rules = Object
  119. .entries(columns)
  120. .filter(([k, v]) => v)
  121. .map(([k, v]) => `--${k}: ${v}`).join(';')
  122. const styleEl = document.createElement('style')
  123. head.appendChild(styleEl)
  124. const styleSheet = styleEl.sheet
  125. styleSheet.toString()
  126. styleSheet.insertRule(`:root { ${rules} }`, 'index-max')
  127. body.classList.remove('hidden')
  128. }
  129. export const getThemes = () => {
  130. const cache = 'no-store'
  131. return window.fetch('/static/styles.json', { cache })
  132. .then((data) => data.json())
  133. .then((themes) => {
  134. return Object.entries(themes).map(([k, v]) => {
  135. let promise = null
  136. if (typeof v === 'object') {
  137. promise = Promise.resolve(v)
  138. } else if (typeof v === 'string') {
  139. promise = window.fetch(v, { cache })
  140. .then((data) => data.json())
  141. .catch((e) => {
  142. console.error(e)
  143. return null
  144. })
  145. }
  146. return [k, promise]
  147. })
  148. })
  149. .then((promises) => {
  150. return promises
  151. .reduce((acc, [k, v]) => {
  152. acc[k] = v
  153. return acc
  154. }, {})
  155. })
  156. }
  157. export const getPreset = (val) => {
  158. return getThemes()
  159. .then((themes) => themes[val] ? themes[val] : themes['pleroma-dark'])
  160. .then((theme) => {
  161. const isV1 = Array.isArray(theme)
  162. const data = isV1 ? {} : theme.theme
  163. if (isV1) {
  164. const bg = hex2rgb(theme[1])
  165. const fg = hex2rgb(theme[2])
  166. const text = hex2rgb(theme[3])
  167. const link = hex2rgb(theme[4])
  168. const cRed = hex2rgb(theme[5] || '#FF0000')
  169. const cGreen = hex2rgb(theme[6] || '#00FF00')
  170. const cBlue = hex2rgb(theme[7] || '#0000FF')
  171. const cOrange = hex2rgb(theme[8] || '#E3FF00')
  172. data.colors = { bg, fg, text, link, cRed, cBlue, cGreen, cOrange }
  173. }
  174. return { theme: data, source: theme.source }
  175. })
  176. }
  177. export const setPreset = (val) => getPreset(val).then(data => applyTheme(data))