diff --git a/app/soapbox/actions/soapbox.js b/app/soapbox/actions/soapbox.js index 2871e2aae..bc3bdc82f 100644 --- a/app/soapbox/actions/soapbox.js +++ b/app/soapbox/actions/soapbox.js @@ -2,6 +2,7 @@ import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { createSelector } from 'reselect'; import { getHost } from 'soapbox/actions/instance'; +import { normalizeSoapboxConfig } from 'soapbox/normalizers'; import KVStore from 'soapbox/storage/kv_store'; import { getFeatures } from 'soapbox/utils/features'; @@ -33,79 +34,10 @@ const allowedEmojiRGI = ImmutableList([ '😩', ]); -const year = new Date().getFullYear(); - export const makeDefaultConfig = features => { return ImmutableMap({ - logo: '', - banner: '', - brandColor: '', // Empty - accentColor: '', - colors: ImmutableMap({ - gray: ImmutableMap({ - 50: '#f9fafb', - 100: '#f3f4f6', - 200: '#e5e7eb', - 300: '#d1d5db', - 400: '#9ca3af', - 500: '#6b7280', - 600: '#4b5563', - 700: '#374151', - 800: '#1f2937', - 900: '#111827', - }), - success: ImmutableMap({ - 50: '#f0fdf4', - 100: '#dcfce7', - 200: '#bbf7d0', - 300: '#86efac', - 400: '#4ade80', - 500: '#22c55e', - 600: '#16a34a', - 700: '#15803d', - 800: '#166534', - 900: '#14532d', - }), - danger: ImmutableMap({ - 50: '#fef2f2', - 100: '#fee2e2', - 200: '#fecaca', - 300: '#fca5a5', - 400: '#f87171', - 500: '#ef4444', - 600: '#dc2626', - 700: '#b91c1c', - 800: '#991b1b', - 900: '#7f1d1d', - }), - 'gradient-purple': '#b8a3f9', - 'gradient-blue': '#9bd5ff', - 'sea-blue': '#2feecc', - }), - customCss: ImmutableList(), - promoPanel: ImmutableMap({ - items: ImmutableList(), - }), - extensions: ImmutableMap(), - defaultSettings: ImmutableMap(), - copyright: `♥${year}. Copying is an act of love. Please copy and share.`, - navlinks: ImmutableMap({ - homeFooter: ImmutableList(), - }), allowedEmoji: features.emojiReactsRGI ? allowedEmojiRGI : allowedEmoji, - verifiedIcon: '', - verifiedCanEditName: false, displayFqn: Boolean(features.federating), - cryptoAddresses: ImmutableList(), - cryptoDonatePanel: ImmutableMap({ - limit: 1, - }), - aboutPages: ImmutableMap(), - betaPages: ImmutableMap(), - mobilePages: ImmutableMap(), - authenticatedProfile: true, - singleUserMode: false, - singleUserModeProfile: '', }); }; @@ -114,7 +46,7 @@ export const getSoapboxConfig = createSelector([ state => getFeatures(state.get('instance')), ], (soapbox, features) => { const defaultConfig = makeDefaultConfig(features); - return soapbox.mergeDeepWith((o, n) => o || n, defaultConfig); + return normalizeSoapboxConfig(soapbox).merge(defaultConfig); }); export function rememberSoapboxConfig(host) { diff --git a/app/soapbox/components/media_gallery.js b/app/soapbox/components/media_gallery.js index df1e4db42..2164031a8 100644 --- a/app/soapbox/components/media_gallery.js +++ b/app/soapbox/components/media_gallery.js @@ -157,8 +157,6 @@ class Item extends React.PureComponent { ); } else if (attachment.get('type') === 'image') { - const previewUrl = attachment.get('preview_url'); - const originalUrl = attachment.get('url'); const letterboxed = shouldLetterbox(attachment); @@ -169,7 +167,7 @@ class Item extends React.PureComponent { onClick={this.handleClick} target='_blank' > - + ); } else if (attachment.get('type') === 'gifv') { diff --git a/app/soapbox/features/chats/index.js b/app/soapbox/features/chats/index.js index d7e1036bc..ed8c35e60 100644 --- a/app/soapbox/features/chats/index.js +++ b/app/soapbox/features/chats/index.js @@ -8,7 +8,6 @@ import { fetchChats, launchChat } from 'soapbox/actions/chats'; import AccountSearch from 'soapbox/components/account_search'; import AudioToggle from 'soapbox/features/chats/components/audio_toggle'; -import ColumnHeader from '../../components/column_header'; import { Column } from '../../components/ui'; import ChatList from './components/chat_list'; @@ -47,11 +46,6 @@ class ChatIndex extends React.PureComponent { return ( - -
diff --git a/app/soapbox/features/crypto_donate/components/site_wallet.tsx b/app/soapbox/features/crypto_donate/components/site_wallet.tsx index af5964796..1e09292bd 100644 --- a/app/soapbox/features/crypto_donate/components/site_wallet.tsx +++ b/app/soapbox/features/crypto_donate/components/site_wallet.tsx @@ -1,4 +1,3 @@ -import { trimStart } from 'lodash'; import React from 'react'; import { Stack } from 'soapbox/components/ui'; @@ -6,36 +5,22 @@ import { useSoapboxConfig } from 'soapbox/hooks'; import CryptoAddress from './crypto_address'; -import type { Map as ImmutableMap, List as ImmutableList } from 'immutable'; - -type Address = ImmutableMap; - -// Address example: -// {"ticker": "btc", "address": "bc1q9cx35adpm73aq2fw40ye6ts8hfxqzjr5unwg0n", "note": "This is our main address"} -const normalizeAddress = (address: Address): Address => { - return address.update('ticker', '', ticker => { - return trimStart(ticker, '$').toLowerCase(); - }); -}; - interface ISiteWallet { limit?: number, } const SiteWallet: React.FC = ({ limit }): JSX.Element => { - const addresses: ImmutableList
= - useSoapboxConfig().get('cryptoAddresses').map(normalizeAddress); - - const coinList = typeof limit === 'number' ? addresses.take(limit) : addresses; + const { cryptoAddresses } = useSoapboxConfig(); + const addresses = typeof limit === 'number' ? cryptoAddresses.take(limit) : cryptoAddresses; return ( - {coinList.map(coin => ( + {addresses.map(address => ( ))} diff --git a/app/soapbox/features/soapbox_config/index.js b/app/soapbox/features/soapbox_config/index.js index 84d80eea3..16cff42f3 100644 --- a/app/soapbox/features/soapbox_config/index.js +++ b/app/soapbox/features/soapbox_config/index.js @@ -12,8 +12,8 @@ import { connect } from 'react-redux'; import { updateConfig } from 'soapbox/actions/admin'; import { uploadMedia } from 'soapbox/actions/media'; import snackbar from 'soapbox/actions/snackbar'; -import { makeDefaultConfig } from 'soapbox/actions/soapbox'; import Icon from 'soapbox/components/icon'; +import { Column } from 'soapbox/components/ui'; import { SimpleForm, FieldsGroup, @@ -26,10 +26,9 @@ import { } from 'soapbox/features/forms'; import ThemeToggle from 'soapbox/features/ui/components/theme_toggle'; import { isMobile } from 'soapbox/is_mobile'; -import { getFeatures } from 'soapbox/utils/features'; +import { normalizeSoapboxConfig } from 'soapbox/normalizers'; import Accordion from '../ui/components/accordion'; -import Column from '../ui/components/column'; import IconPickerDropdown from './components/icon_picker_dropdown'; import SitePreview from './components/site_preview'; @@ -71,11 +70,8 @@ const templates = { }; const mapStateToProps = state => { - const instance = state.get('instance'); - return { - soapbox: state.get('soapbox'), - features: getFeatures(instance), + initialData: state.soapbox, }; }; @@ -84,37 +80,36 @@ export default @connect(mapStateToProps) class SoapboxConfig extends ImmutablePureComponent { static propTypes = { - soapbox: ImmutablePropTypes.map.isRequired, - features: PropTypes.object.isRequired, + initialData: ImmutablePropTypes.map.isRequired, dispatch: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, }; state = { isLoading: false, - soapbox: this.props.soapbox, + data: this.props.initialData, jsonEditorExpanded: false, rawJSON: JSON.stringify(this.props.soapbox, null, 2), jsonValid: true, } setConfig = (path, value) => { - const { soapbox } = this.state; - const config = soapbox.setIn(path, value); - this.setState({ soapbox: config, jsonValid: true }); + const { data } = this.state; + const newData = data.setIn(path, value); + this.setState({ data: newData, jsonValid: true }); }; - putConfig = config => { - this.setState({ soapbox: config, jsonValid: true }); + putConfig = data => { + this.setState({ data, jsonValid: true }); }; getParams = () => { - const { soapbox } = this.state; + const { data } = this.state; return [{ group: ':pleroma', key: ':frontend_configurations', value: [{ - tuple: [':soapbox_fe', soapbox.toJS()], + tuple: [':soapbox_fe', data.toJS()], }], }]; } @@ -158,8 +153,8 @@ class SoapboxConfig extends ImmutablePureComponent { handleDeleteItem = path => { return e => { - const soapbox = this.state.soapbox.deleteIn(path); - this.setState({ soapbox }); + const data = this.state.data.deleteIn(path); + this.setState({ data }); }; }; @@ -195,20 +190,18 @@ class SoapboxConfig extends ImmutablePureComponent { } getSoapboxConfig = () => { - const { features } = this.props; - const { soapbox } = this.state; - return makeDefaultConfig(features).mergeDeep(soapbox); + return normalizeSoapboxConfig(this.state.data); } toggleJSONEditor = (value) => this.setState({ jsonEditorExpanded: value }); componentDidUpdate(prevProps, prevState) { - if (prevProps.soapbox !== this.props.soapbox) { - this.putConfig(this.props.soapbox); + if (prevProps.initialData !== this.props.initialData) { + this.putConfig(this.props.initialData); } - if (prevState.soapbox !== this.state.soapbox) { - this.setState({ rawJSON: JSON.stringify(this.state.soapbox, null, 2) }); + if (prevState.data !== this.state.data) { + this.setState({ rawJSON: JSON.stringify(this.state.data, null, 2) }); } if (prevState.rawJSON !== this.state.rawJSON) { @@ -226,7 +219,7 @@ class SoapboxConfig extends ImmutablePureComponent { const soapbox = this.getSoapboxConfig(); return ( - +
diff --git a/app/soapbox/hooks/useSoapboxConfig.ts b/app/soapbox/hooks/useSoapboxConfig.ts index 2e51e5923..92a1ddc21 100644 --- a/app/soapbox/hooks/useSoapboxConfig.ts +++ b/app/soapbox/hooks/useSoapboxConfig.ts @@ -1,9 +1,9 @@ import { getSoapboxConfig } from 'soapbox/actions/soapbox'; import { useAppSelector } from 'soapbox/hooks'; -import type { Map as ImmutableMap } from 'immutable'; +import type { SoapboxConfig } from 'soapbox/types/soapbox'; /** Get the Soapbox config from the store */ -export const useSoapboxConfig = (): ImmutableMap => { +export const useSoapboxConfig = (): SoapboxConfig => { return useAppSelector((state) => getSoapboxConfig(state)); }; diff --git a/app/soapbox/normalizers/index.ts b/app/soapbox/normalizers/index.ts index c4a34d66c..613de5331 100644 --- a/app/soapbox/normalizers/index.ts +++ b/app/soapbox/normalizers/index.ts @@ -7,3 +7,5 @@ export { MentionRecord, normalizeMention } from './mention'; export { NotificationRecord, normalizeNotification } from './notification'; export { PollRecord, PollOptionRecord, normalizePoll } from './poll'; export { StatusRecord, normalizeStatus } from './status'; + +export { SoapboxConfigRecord, normalizeSoapboxConfig } from './soapbox/soapbox_config'; diff --git a/app/soapbox/normalizers/soapbox/__tests__/soapbox_config-test.js b/app/soapbox/normalizers/soapbox/__tests__/soapbox_config-test.js new file mode 100644 index 000000000..f0a021586 --- /dev/null +++ b/app/soapbox/normalizers/soapbox/__tests__/soapbox_config-test.js @@ -0,0 +1,30 @@ +import { Record as ImmutableRecord } from 'immutable'; + +import { normalizeSoapboxConfig } from '../soapbox_config'; + +describe('normalizeSoapboxConfig()', () => { + it('adds base fields', () => { + const result = normalizeSoapboxConfig({}); + expect(result.brandColor).toBe(''); + expect(ImmutableRecord.isRecord(result)).toBe(true); + }); + + it('normalizes cryptoAddresses', () => { + const soapboxConfig = { + cryptoAddresses: [ + { ticker: '$BTC', address: 'bc1q9cx35adpm73aq2fw40ye6ts8hfxqzjr5unwg0n' }, + ], + }; + + const expected = { + cryptoAddresses: [ + { ticker: 'btc', address: 'bc1q9cx35adpm73aq2fw40ye6ts8hfxqzjr5unwg0n', note: '' }, + ], + }; + + const result = normalizeSoapboxConfig(soapboxConfig); + expect(result.cryptoAddresses.size).toBe(1); + expect(ImmutableRecord.isRecord(result.cryptoAddresses.get(0))).toBe(true); + expect(result.toJS()).toMatchObject(expected); + }); +}); diff --git a/app/soapbox/normalizers/soapbox/soapbox_config.ts b/app/soapbox/normalizers/soapbox/soapbox_config.ts new file mode 100644 index 000000000..3d0658851 --- /dev/null +++ b/app/soapbox/normalizers/soapbox/soapbox_config.ts @@ -0,0 +1,171 @@ +import { + Map as ImmutableMap, + List as ImmutableList, + Record as ImmutableRecord, + fromJS, +} from 'immutable'; +import { trimStart } from 'lodash'; + +import { toTailwind } from 'soapbox/utils/tailwind'; +import { generateAccent } from 'soapbox/utils/theme'; + +import type { + PromoPanelItem, + FooterItem, + CryptoAddress, +} from 'soapbox/types/soapbox'; + +const DEFAULT_COLORS = ImmutableMap({ + gray: ImmutableMap({ + 50: '#f9fafb', + 100: '#f3f4f6', + 200: '#e5e7eb', + 300: '#d1d5db', + 400: '#9ca3af', + 500: '#6b7280', + 600: '#4b5563', + 700: '#374151', + 800: '#1f2937', + 900: '#111827', + }), + success: ImmutableMap({ + 50: '#f0fdf4', + 100: '#dcfce7', + 200: '#bbf7d0', + 300: '#86efac', + 400: '#4ade80', + 500: '#22c55e', + 600: '#16a34a', + 700: '#15803d', + 800: '#166534', + 900: '#14532d', + }), + danger: ImmutableMap({ + 50: '#fef2f2', + 100: '#fee2e2', + 200: '#fecaca', + 300: '#fca5a5', + 400: '#f87171', + 500: '#ef4444', + 600: '#dc2626', + 700: '#b91c1c', + 800: '#991b1b', + 900: '#7f1d1d', + }), + 'gradient-purple': '#b8a3f9', + 'gradient-blue': '#9bd5ff', + 'sea-blue': '#2feecc', +}); + +export const PromoPanelItemRecord = ImmutableRecord({ + icon: '', + text: '', + url: '', +}); + +export const FooterItemRecord = ImmutableRecord({ + title: '', + url: '', +}); + +export const CryptoAddressRecord = ImmutableRecord({ + address: '', + note: '', + ticker: '', +}); + +export const SoapboxConfigRecord = ImmutableRecord({ + logo: '', + banner: '', + brandColor: '', // Empty + accentColor: '', + colors: ImmutableMap(), + copyright: `♥${new Date().getFullYear()}. Copying is an act of love. Please copy and share.`, + customCss: ImmutableList(), + defaultSettings: ImmutableMap(), + extensions: ImmutableMap(), + greentext: false, + promoPanel: ImmutableMap({ + items: ImmutableList(), + }), + navlinks: ImmutableMap({ + homeFooter: ImmutableList(), + }), + allowedEmoji: ImmutableList([ + '👍', + '❤️', + '😆', + '😮', + '😢', + '😩', + ]), + verifiedIcon: '', + verifiedCanEditName: false, + displayFqn: true, + cryptoAddresses: ImmutableList(), + cryptoDonatePanel: ImmutableMap({ + limit: 1, + }), + aboutPages: ImmutableMap(), + betaPages: ImmutableMap(), + mobilePages: ImmutableMap(), + authenticatedProfile: true, + singleUserMode: false, + singleUserModeProfile: '', +}, 'SoapboxConfig'); + +type SoapboxConfigMap = ImmutableMap; + +const normalizeCryptoAddress = (address: unknown): CryptoAddress => { + return CryptoAddressRecord(ImmutableMap(fromJS(address))).update('ticker', ticker => { + return trimStart(ticker, '$').toLowerCase(); + }); +}; + +const normalizeCryptoAddresses = (soapboxConfig: SoapboxConfigMap): SoapboxConfigMap => { + const addresses = ImmutableList(soapboxConfig.get('cryptoAddresses')); + return soapboxConfig.set('cryptoAddresses', addresses.map(normalizeCryptoAddress)); +}; + +const normalizeBrandColor = (soapboxConfig: SoapboxConfigMap): SoapboxConfigMap => { + const brandColor = soapboxConfig.get('brandColor') || soapboxConfig.getIn(['colors', 'primary', '500']) || ''; + return soapboxConfig.set('brandColor', brandColor); +}; + +const normalizeAccentColor = (soapboxConfig: SoapboxConfigMap): SoapboxConfigMap => { + const brandColor = soapboxConfig.get('brandColor'); + + const accentColor = soapboxConfig.get('accentColor') + || soapboxConfig.getIn(['colors', 'accent', '500']) + || (brandColor ? generateAccent(brandColor) : ''); + + return soapboxConfig.set('accentColor', accentColor); +}; + +const normalizeColors = (soapboxConfig: SoapboxConfigMap): SoapboxConfigMap => { + const colors = DEFAULT_COLORS.mergeDeep(soapboxConfig.get('colors')); + return toTailwind(soapboxConfig.set('colors', colors)); +}; + +const maybeAddMissingColors = (soapboxConfig: SoapboxConfigMap): SoapboxConfigMap => { + const colors = soapboxConfig.get('colors'); + + const missing = { + 'bg-shape-1': colors.getIn(['accent', '50']), + 'bg-shape-2': colors.getIn(['primary', '500']), + }; + + return soapboxConfig.set('colors', colors.mergeDeep(missing)); +}; + +export const normalizeSoapboxConfig = (soapboxConfig: Record) => { + return SoapboxConfigRecord( + ImmutableMap(fromJS(soapboxConfig)).withMutations(soapboxConfig => { + normalizeBrandColor(soapboxConfig); + normalizeAccentColor(soapboxConfig); + normalizeColors(soapboxConfig); + maybeAddMissingColors(soapboxConfig); + normalizeCryptoAddresses(soapboxConfig); + }), + ); +}; diff --git a/app/soapbox/types/soapbox.ts b/app/soapbox/types/soapbox.ts new file mode 100644 index 000000000..386734d3b --- /dev/null +++ b/app/soapbox/types/soapbox.ts @@ -0,0 +1,18 @@ +import { + PromoPanelItemRecord, + FooterItemRecord, + CryptoAddressRecord, + SoapboxConfigRecord, +} from 'soapbox/normalizers/soapbox/soapbox_config'; + +type PromoPanelItem = ReturnType; +type FooterItem = ReturnType; +type CryptoAddress = ReturnType; +type SoapboxConfig = ReturnType; + +export { + PromoPanelItem, + FooterItem, + CryptoAddress, + SoapboxConfig, +}; diff --git a/app/soapbox/utils/__tests__/colors-test.js b/app/soapbox/utils/__tests__/colors-test.js index 3e444620a..8677cb1c6 100644 --- a/app/soapbox/utils/__tests__/colors-test.js +++ b/app/soapbox/utils/__tests__/colors-test.js @@ -9,8 +9,8 @@ describe('tintify()', () => { expect(result).toEqual({ '100': '#e6f3fb', '200': '#c0e0f5', - '300': '#9bcdef', - '400': '#4fa8e4', + '300': '#4fa8e4', + '400': '#369be0', '50': '#f2f9fd', '500': '#0482d8', '600': '#0475c2', diff --git a/app/soapbox/utils/__tests__/tailwind-test.js b/app/soapbox/utils/__tests__/tailwind-test.js index 93f928b73..822408393 100644 --- a/app/soapbox/utils/__tests__/tailwind-test.js +++ b/app/soapbox/utils/__tests__/tailwind-test.js @@ -20,8 +20,8 @@ describe('toTailwind()', () => { 50: '#f2f9fd', 100: '#e6f3fb', 200: '#c0e0f5', - 300: '#9bcdef', - 400: '#4fa8e4', + 300: '#4fa8e4', + 400: '#369be0', 500: '#0482d8', 600: '#0475c2', 700: '#0362a2', @@ -53,7 +53,7 @@ describe('toTailwind()', () => { 100: '#e6f3fb', 200: '#c0e0f5', 300: '#ff0000', // <-- - 400: '#4fa8e4', + 400: '#369be0', 500: '#0482d8', 600: '#0475c2', 700: '#0362a2', @@ -77,8 +77,8 @@ describe('fromLegacyColors()', () => { 50: '#f2f9fd', 100: '#e6f3fb', 200: '#c0e0f5', - 300: '#9bcdef', - 400: '#4fa8e4', + 300: '#4fa8e4', + 400: '#369be0', 500: '#0482d8', 600: '#0475c2', 700: '#0362a2', @@ -90,8 +90,8 @@ describe('fromLegacyColors()', () => { 50: '#f3fbfd', 100: '#e7f7fa', 200: '#c3ecf4', - 300: '#9fe1ed', - 400: '#58cadf', + 300: '#58cadf', + 400: '#40c2da', 500: '#10b3d1', 600: '#0ea1bc', 700: '#0c869d', @@ -115,8 +115,8 @@ describe('fromLegacyColors()', () => { 50: '#f2f9fd', 100: '#e6f3fb', 200: '#c0e0f5', - 300: '#9bcdef', - 400: '#4fa8e4', + 300: '#4fa8e4', + 400: '#369be0', 500: '#0482d8', 600: '#0475c2', 700: '#0362a2', @@ -127,8 +127,8 @@ describe('fromLegacyColors()', () => { 50: '#f4fdf3', 100: '#eafae7', 200: '#caf4c3', - 300: '#aaed9f', - 400: '#6bdf58', + 300: '#6bdf58', + 400: '#55da40', 500: '#2bd110', 600: '#27bc0e', 700: '#209d0c', @@ -151,8 +151,8 @@ describe('expandPalette()', () => { 50: '#f2f9fd', 100: '#e6f3fb', 200: '#c0e0f5', - 300: '#9bcdef', - 400: '#4fa8e4', + 300: '#4fa8e4', + 400: '#369be0', 500: '#0482d8', 600: '#0475c2', 700: '#0362a2', @@ -171,8 +171,8 @@ describe('expandPalette()', () => { 50: '#f2f9fd', 100: '#e6f3fb', 200: '#c0e0f5', - 300: '#9bcdef', - 400: '#4fa8e4', + 300: '#4fa8e4', + 400: '#369be0', 500: '#0482d8', 600: '#0475c2', 700: '#0362a2', @@ -187,8 +187,8 @@ describe('expandPalette()', () => { 50: '#f2f9fd', 100: '#e6f3fb', 200: '#c0e0f5', - 300: '#9bcdef', - 400: '#4fa8e4', + 300: '#4fa8e4', + 400: '#369be0', 500: '#0482d8', 600: '#0475c2', 700: '#0362a2', @@ -199,8 +199,8 @@ describe('expandPalette()', () => { 50: '#f4fdf3', 100: '#eafae7', 200: '#caf4c3', - 300: '#aaed9f', - 400: '#6bdf58', + 300: '#6bdf58', + 400: '#55da40', 500: '#2bd110', 600: '#27bc0e', 700: '#209d0c', diff --git a/app/soapbox/utils/colors.ts b/app/soapbox/utils/colors.ts index c20c98830..796b2123a 100644 --- a/app/soapbox/utils/colors.ts +++ b/app/soapbox/utils/colors.ts @@ -103,8 +103,8 @@ export default function(baseColor: string): TailwindColorObject { 50: 0.95, 100: 0.9, 200: 0.75, - 300: 0.6, - 400: 0.3, + 300: 0.3, + 400: 0.2, 600: 0.9, 700: 0.75, 800: 0.6, diff --git a/app/soapbox/utils/theme.ts b/app/soapbox/utils/theme.ts index bb83b0111..ccf523b8b 100644 --- a/app/soapbox/utils/theme.ts +++ b/app/soapbox/utils/theme.ts @@ -1,8 +1,7 @@ import { hexToRgb } from './colors'; -import { toTailwind } from './tailwind'; -import type { Map as ImmutableMap } from 'immutable'; import type { Rgb, Hsl, TailwindColorPalette, TailwindColorObject } from 'soapbox/types/colors'; +import type { SoapboxConfig } from 'soapbox/types/soapbox'; // Taken from chromatism.js // https://github.com/graypegg/chromatism/blob/master/src/conversions/rgb.js @@ -69,16 +68,19 @@ export const generateAccent = (brandColor: string): string | null => { return hslToHex({ h: h - 15, s: 86, l: 44 }); }; -const parseShades = (obj: Record, color: string, shades: Record) => { +const parseShades = (obj: Record, color: string, shades: Record): void => { + if (!shades) return; + if (typeof shades === 'string') { const rgb = hexToRgb(shades); - if (!rgb) return obj; + if (!rgb) return; const { r, g, b } = rgb; - return obj[`--color-${color}`] = `${r} ${g} ${b}`; + obj[`--color-${color}`] = `${r} ${g} ${b}`; + return; } - return Object.keys(shades).forEach(shade => { + Object.keys(shades).forEach(shade => { const rgb = hexToRgb(shades[shade]); if (!rgb) return; @@ -102,6 +104,6 @@ export const colorsToCss = (colors: TailwindColorPalette): string => { }, ''); }; -export const generateThemeCss = (soapboxConfig: ImmutableMap): string => { - return colorsToCss(toTailwind(soapboxConfig).get('colors').toJS() as TailwindColorPalette); +export const generateThemeCss = (soapboxConfig: SoapboxConfig): string => { + return colorsToCss(soapboxConfig.colors.toJS() as TailwindColorPalette); };