Merge branch 'next-sb-normalizer' into 'next'

Next: Normalize SoapboxConfig, small bug fixes

See merge request soapbox-pub/soapbox-fe!1158
This commit is contained in:
Alex Gleason 2022-03-28 23:44:13 +00:00
commit 56e51e265b
14 changed files with 212 additions and 34 deletions

Binary file not shown.

Binary file not shown.

View file

@ -1,4 +1,3 @@
import { trimStart } from 'lodash';
import React from 'react'; import React from 'react';
import { Stack } from 'soapbox/components/ui'; import { Stack } from 'soapbox/components/ui';
@ -6,36 +5,22 @@ import { useSoapboxConfig } from 'soapbox/hooks';
import CryptoAddress from './crypto_address'; import CryptoAddress from './crypto_address';
import type { Map as ImmutableMap, List as ImmutableList } from 'immutable';
type Address = ImmutableMap<string, any>;
// 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 { interface ISiteWallet {
limit?: number, limit?: number,
} }
const SiteWallet: React.FC<ISiteWallet> = ({ limit }): JSX.Element => { const SiteWallet: React.FC<ISiteWallet> = ({ limit }): JSX.Element => {
const addresses: ImmutableList<Address> = const { cryptoAddresses } = useSoapboxConfig();
useSoapboxConfig().get('cryptoAddresses').map(normalizeAddress); const addresses = typeof limit === 'number' ? cryptoAddresses.take(limit) : cryptoAddresses;
const coinList = typeof limit === 'number' ? addresses.take(limit) : addresses;
return ( return (
<Stack space={4}> <Stack space={4}>
{coinList.map(coin => ( {addresses.map(address => (
<CryptoAddress <CryptoAddress
key={coin.get('ticker')} key={address.ticker}
address={coin.get('address')} address={address.address}
ticker={coin.get('ticker')} ticker={address.ticker}
note={coin.get('note')} note={address.note}
/> />
))} ))}
</Stack> </Stack>

View file

@ -1,9 +1,9 @@
import { getSoapboxConfig } from 'soapbox/actions/soapbox'; import { getSoapboxConfig } from 'soapbox/actions/soapbox';
import { useAppSelector } from 'soapbox/hooks'; 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 */ /** Get the Soapbox config from the store */
export const useSoapboxConfig = (): ImmutableMap<string, any> => { export const useSoapboxConfig = (): SoapboxConfig => {
return useAppSelector((state) => getSoapboxConfig(state)); return useAppSelector((state) => getSoapboxConfig(state));
}; };

View file

@ -7,3 +7,5 @@ export { MentionRecord, normalizeMention } from './mention';
export { NotificationRecord, normalizeNotification } from './notification'; export { NotificationRecord, normalizeNotification } from './notification';
export { PollRecord, PollOptionRecord, normalizePoll } from './poll'; export { PollRecord, PollOptionRecord, normalizePoll } from './poll';
export { StatusRecord, normalizeStatus } from './status'; export { StatusRecord, normalizeStatus } from './status';
export { SoapboxConfigRecord, normalizeSoapboxConfig } from './soapbox/soapbox_config';

View file

@ -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<string, any>({
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<string>(),
defaultSettings: ImmutableMap(),
extensions: ImmutableMap(),
greentext: false,
promoPanel: ImmutableMap({
items: ImmutableList<PromoPanelItem>(),
}),
navlinks: ImmutableMap({
homeFooter: ImmutableList<FooterItem>(),
}),
allowedEmoji: ImmutableList<string>([
'👍',
'❤️',
'😆',
'😮',
'😢',
'😩',
]),
verifiedIcon: '',
verifiedCanEditName: false,
displayFqn: true,
cryptoAddresses: ImmutableList<CryptoAddress>(),
cryptoDonatePanel: ImmutableMap({
limit: 1,
}),
aboutPages: ImmutableMap(),
betaPages: ImmutableMap(),
mobilePages: ImmutableMap(),
authenticatedProfile: true,
singleUserMode: false,
singleUserModeProfile: '',
}, 'SoapboxConfig');
type SoapboxConfigMap = ImmutableMap<string, any>;
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<string, any>) => {
return SoapboxConfigRecord(
ImmutableMap(fromJS(soapboxConfig)).withMutations(soapboxConfig => {
normalizeBrandColor(soapboxConfig);
normalizeAccentColor(soapboxConfig);
normalizeColors(soapboxConfig);
maybeAddMissingColors(soapboxConfig);
normalizeCryptoAddresses(soapboxConfig);
}),
);
};

View file

@ -0,0 +1,18 @@
import {
PromoPanelItemRecord,
FooterItemRecord,
CryptoAddressRecord,
SoapboxConfigRecord,
} from 'soapbox/normalizers/soapbox/soapbox_config';
type PromoPanelItem = ReturnType<typeof PromoPanelItemRecord>;
type FooterItem = ReturnType<typeof FooterItemRecord>;
type CryptoAddress = ReturnType<typeof CryptoAddressRecord>;
type SoapboxConfig = ReturnType<typeof SoapboxConfigRecord>;
export {
PromoPanelItem,
FooterItem,
CryptoAddress,
SoapboxConfig,
};

View file

@ -103,8 +103,8 @@ export default function(baseColor: string): TailwindColorObject {
50: 0.95, 50: 0.95,
100: 0.9, 100: 0.9,
200: 0.75, 200: 0.75,
300: 0.6, 300: 0.3,
400: 0.3, 400: 0.2,
600: 0.9, 600: 0.9,
700: 0.75, 700: 0.75,
800: 0.6, 800: 0.6,

View file

@ -1,8 +1,7 @@
import { hexToRgb } from './colors'; 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 { Rgb, Hsl, TailwindColorPalette, TailwindColorObject } from 'soapbox/types/colors';
import type { SoapboxConfig } from 'soapbox/types/soapbox';
// Taken from chromatism.js // Taken from chromatism.js
// https://github.com/graypegg/chromatism/blob/master/src/conversions/rgb.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 }); return hslToHex({ h: h - 15, s: 86, l: 44 });
}; };
const parseShades = (obj: Record<string, any>, color: string, shades: Record<string, any>) => { const parseShades = (obj: Record<string, any>, color: string, shades: Record<string, any>): void => {
if (!shades) return;
if (typeof shades === 'string') { if (typeof shades === 'string') {
const rgb = hexToRgb(shades); const rgb = hexToRgb(shades);
if (!rgb) return obj; if (!rgb) return;
const { r, g, b } = rgb; 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]); const rgb = hexToRgb(shades[shade]);
if (!rgb) return; if (!rgb) return;
@ -102,6 +104,6 @@ export const colorsToCss = (colors: TailwindColorPalette): string => {
}, ''); }, '');
}; };
export const generateThemeCss = (soapboxConfig: ImmutableMap<string, any>): string => { export const generateThemeCss = (soapboxConfig: SoapboxConfig): string => {
return colorsToCss(toTailwind(soapboxConfig).get('colors').toJS() as TailwindColorPalette); return colorsToCss(soapboxConfig.colors.toJS() as TailwindColorPalette);
}; };