pl-fe: Allow to configure instance logo alignment

Signed-off-by: mkljczk <git@mkljczk.pl>
This commit is contained in:
mkljczk
2025-02-23 18:17:56 +01:00
parent 58f9a3a20e
commit 4b80a84977
8 changed files with 55 additions and 26 deletions

View File

@ -9,7 +9,6 @@ import ProfileDropdown from 'pl-fe/features/ui/components/profile-dropdown';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useFeatures } from 'pl-fe/hooks/use-features';
import { useInstance } from 'pl-fe/hooks/use-instance';
import { useLogo } from 'pl-fe/hooks/use-logo';
import { useOwnAccount } from 'pl-fe/hooks/use-own-account';
import { useRegistrationStatus } from 'pl-fe/hooks/use-registration-status';
import { useSettings } from 'pl-fe/hooks/use-settings';
@ -46,7 +45,6 @@ const SidebarNavigation = React.memo(() => {
const { isDeveloper } = useSettings();
const { account } = useOwnAccount();
const { isOpen } = useRegistrationStatus();
const logoSrc = useLogo();
const notificationCount = useAppSelector((state) => state.notifications.unread);
const followRequestsCount = useFollowRequestsCount().data || 0;
@ -159,9 +157,7 @@ const SidebarNavigation = React.memo(() => {
return (
<Stack space={4}>
{logoSrc && (
<SiteLogo className='h-12 w-auto cursor-pointer' />
)}
<SiteLogo className='h-12 w-auto cursor-pointer' />
{account && (
<Stack space={4}>

View File

@ -24,7 +24,7 @@ interface ISiteErrorBoundary {
/** Application-level error boundary. Fills the whole screen. */
const SiteErrorBoundary: React.FC<ISiteErrorBoundary> = ({ children }) => {
const { links, sentryDsn } = usePlFeConfig();
const logoSrc = useLogo();
const { src: logoSrc } = useLogo();
const textarea = useRef<HTMLTextAreaElement>(null);
const [error, setError] = useState<unknown>();

View File

@ -18,7 +18,7 @@ interface ISiteLogo extends React.ComponentProps<'img'> {
/** Display the most appropriate site logo based on the theme and configuration. */
const SiteLogo: React.FC<ISiteLogo> = ({ className, theme, ...rest }) => {
const intl = useIntl();
const logoSrc = useLogo();
const { src: logoSrc, alignment } = useLogo();
if (!logoSrc) return null;
@ -26,7 +26,7 @@ const SiteLogo: React.FC<ISiteLogo> = ({ className, theme, ...rest }) => {
// eslint-disable-next-line jsx-a11y/alt-text
<img
alt={intl.formatMessage(messages.logo)}
className={clsx('object-contain', className)}
className={clsx('object-contain', alignment === 'left' && 'w-fit', className)}
src={logoSrc}
{...rest}
/>

View File

@ -25,7 +25,7 @@ const EmbeddedStatus: React.FC<IEmbeddedStatus> = ({ params }) => {
const history = useHistory();
const getStatus = useCallback(makeGetStatus(), []);
const intl = useIntl();
const logoSrc = useLogo();
const { src: logoSrc } = useLogo();
const status = useAppSelector(state => getStatus(state, { id: params.statusId }));

View File

@ -15,6 +15,7 @@ import Form from 'pl-fe/components/ui/form';
import FormActions from 'pl-fe/components/ui/form-actions';
import FormGroup from 'pl-fe/components/ui/form-group';
import Input from 'pl-fe/components/ui/input';
import Select from 'pl-fe/components/ui/select';
import Streamfield from 'pl-fe/components/ui/streamfield';
import Textarea from 'pl-fe/components/ui/textarea';
import Toggle from 'pl-fe/components/ui/toggle';
@ -28,7 +29,6 @@ import toast from 'pl-fe/toast';
import CryptoAddressInput from './components/crypto-address-input';
import FooterLinkInput from './components/footer-link-input';
import PromoPanelInput from './components/promo-panel-input';
import SitePreview from './components/site-preview';
const messages = defineMessages({
heading: { id: 'column.plfe_config', defaultMessage: 'Front-end configuration' },
@ -180,17 +180,7 @@ const PlFeConfigEditor: React.FC = () => {
<Column label={intl.formatMessage(messages.heading)}>
<Form onSubmit={handleSubmit}>
<fieldset className='space-y-6' disabled={isLoading}>
<SitePreview plFe={plFe} />
<FormGroup
labelText={<FormattedMessage id='plfe_config.fields.logo_label' defaultMessage='Logo' />}
hintText={<FormattedMessage id='plfe_config.hints.logo' defaultMessage='SVG. At most 2 MB. Will be displayed to 50px height, maintaining aspect ratio' />}
>
<FileInput
onChange={handleFileChange('logo')}
accept='image/svg+xml,image/png'
/>
</FormGroup>
{/* <SitePreview plFe={plFe} /> */}
<CardHeader>
<CardTitle title={<FormattedMessage id='plfe_config.headings.theme' defaultMessage='Theme' />} />
@ -210,6 +200,45 @@ const PlFeConfigEditor: React.FC = () => {
/>
</List>
<FormGroup
labelText={<FormattedMessage id='plfe_config.fields.logo_label' defaultMessage='Logo' />}
hintText={<FormattedMessage id='plfe_config.hints.logo' defaultMessage='SVG or PNG. At most 2 MB. Will be displayed to 50px height, maintaining aspect ratio' />}
>
<FileInput
onChange={handleFileChange('logo')}
accept='image/svg+xml,image/png'
/>
</FormGroup>
<FormGroup
labelText={<FormattedMessage id='plfe_config.fields.logo_dark_label' defaultMessage='Logo (dark)' />}
hintText={<FormattedMessage id='plfe_config.hints.logo_dark' defaultMessage='SVG or PNG. At most 2 MB. Will be displayed when in dark mode' />}
>
<FileInput
onChange={handleFileChange('logoDarkMode')}
accept='image/svg+xml,image/png'
/>
</FormGroup>
{(data.logo || data.logoDarkMode) && (
<List>
<ListItem label={<FormattedMessage id='plfe_config.fields.logo_alignment' defaultMessage='Logo alignment' />}>
<Select
className='w-fit'
onChange={handleChange('logoAlignment', (e) => e.target.value)}
defaultValue={data.logoAlignment}
>
<option value='center'>
<FormattedMessage id='plfe_config.fields.logo_alignment.center' defaultMessage='Center' />
</option>
<option value='left'>
<FormattedMessage id='plfe_config.fields.logo_alignment.left' defaultMessage='Left' />
</option>
</Select>
</ListItem>
</List>
)}
<CardHeader>
<CardTitle title={<FormattedMessage id='plfe_config.headings.options' defaultMessage='Options' />} />
</CardHeader>

View File

@ -3,7 +3,7 @@ import { useSettings } from './use-settings';
import { useTheme } from './use-theme';
const useLogo = () => {
const { logo, logoDarkMode } = usePlFeConfig();
const { logo, logoDarkMode, logoAlignment } = usePlFeConfig();
const { demo } = useSettings();
const darkMode = ['dark', 'black'].includes(useTheme());
@ -13,9 +13,7 @@ const useLogo = () => {
? logoDarkMode
: logo || logoDarkMode;
if (demo) return null;
return src;
return { src: demo ? null : src, alignment: logoAlignment };
};
export { useLogo };

View File

@ -1198,6 +1198,10 @@
"plfe_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
"plfe_config.fields.edit_theme_label": "Edit theme",
"plfe_config.fields.home_footer_fields_label": "Home footer items",
"plfe_config.fields.logo_alignment": "Logo alignment",
"plfe_config.fields.logo_alignment.center": "Center",
"plfe_config.fields.logo_alignment.left": "Left",
"plfe_config.fields.logo_dark_label": "Logo (dark)",
"plfe_config.fields.logo_label": "Logo",
"plfe_config.fields.promo_panel_fields_label": "Promo panel items",
"plfe_config.fields.theme_label": "Default theme",
@ -1210,7 +1214,8 @@
"plfe_config.headings.theme": "Theme",
"plfe_config.hints.crypto_addresses": "Add cryptocurrency addresses so users of your site can donate to you. Order matters, and you must use lowercase ticker values.",
"plfe_config.hints.home_footer_fields": "You can have custom defined links displayed on the footer of your static pages",
"plfe_config.hints.logo": "SVG. At most 2 MB. Will be displayed to 50px height, maintaining aspect ratio",
"plfe_config.hints.logo": "SVG or PNG. At most 2 MB. Will be displayed to 50px height, maintaining aspect ratio",
"plfe_config.hints.logo_dark": "SVG or PNG. At most 2 MB. Will be displayed when in dark mode",
"plfe_config.hints.promo_panel_fields": "You can have custom defined links displayed on the right panel of the timelines page.",
"plfe_config.home_footer.meta_fields.label_placeholder": "Label",
"plfe_config.home_footer.meta_fields.url_placeholder": "URL",

View File

@ -41,6 +41,7 @@ const plFeConfigSchema = coerceObject({
appleAppId: v.fallback(v.nullable(v.string()), null),
logo: v.fallback(v.string(), ''),
logoDarkMode: v.fallback(v.nullable(v.string()), null),
logoAlignment: v.fallback(v.picklist(['left', 'center']), 'center'),
brandColor: v.fallback(v.string(), ''),
accentColor: v.fallback(v.string(), ''),
colors: v.fallback(v.nullable(v.objectWithRest(