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:
commit
56e51e265b
14 changed files with 212 additions and 34 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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>
|
||||||
|
|
Binary file not shown.
|
@ -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));
|
||||||
};
|
};
|
||||||
|
|
|
@ -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';
|
||||||
|
|
BIN
app/soapbox/normalizers/soapbox/__tests__/soapbox_config-test.js
Normal file
BIN
app/soapbox/normalizers/soapbox/__tests__/soapbox_config-test.js
Normal file
Binary file not shown.
171
app/soapbox/normalizers/soapbox/soapbox_config.ts
Normal file
171
app/soapbox/normalizers/soapbox/soapbox_config.ts
Normal 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);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
18
app/soapbox/types/soapbox.ts
Normal file
18
app/soapbox/types/soapbox.ts
Normal 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,
|
||||||
|
};
|
Binary file not shown.
Binary file not shown.
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue