ghost-portal/packages/page-settings/src/General.tsx
2021-01-03 11:33:39 +01:00

159 lines
4.6 KiB
TypeScript

// Copyright 2017-2021 @polkadot/app-settings authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Option } from '@polkadot/apps-config/settings/types';
import type { SettingsStruct } from '@polkadot/ui-settings/types';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { createLanguages, createSs58 } from '@polkadot/apps-config';
import { isLedgerCapable } from '@polkadot/react-api';
import { Button, Dropdown } from '@polkadot/react-components';
import { settings } from '@polkadot/ui-settings';
import { useTranslation } from './translate';
import { createIdenticon, createOption, save, saveAndReload } from './util';
interface Props {
className?: string;
}
const ledgerConnOptions = settings.availableLedgerConn;
function General ({ className = '' }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
// tri-state: null = nothing changed, false = no reload, true = reload required
const [changed, setChanged] = useState<boolean | null>(null);
const [state, setSettings] = useState((): SettingsStruct => {
const values = settings.get();
return { ...values, uiTheme: values.uiTheme === 'dark' ? 'dark' : 'light' };
});
const iconOptions = useMemo(
() => settings.availableIcons
.map((o): Option => createIdenticon(o, ['default']))
.concat(createIdenticon({ info: 'robohash', text: 'RoboHash', value: 'robohash' })),
[]
);
const prefixOptions = useMemo(
() => createSs58(t).map((o): Option | React.ReactNode => createOption(o, ['default'])),
[t]
);
const themeOptions = useMemo(
() => [
{ text: t('Light theme (default)'), value: 'light' },
{ text: t('Dark theme (experimental, work-in-progress)'), value: 'dark' }
],
[t]
);
const translateLanguages = useMemo(
() => createLanguages(t),
[t]
);
useEffect((): void => {
const prev = settings.get() as unknown as Record<string, unknown>;
const hasChanges = Object.entries(state).some(([key, value]) => prev[key] !== value);
const needsReload = prev.apiUrl !== state.apiUrl || prev.prefix !== state.prefix;
setChanged(
hasChanges
? needsReload
: null
);
}, [state]);
const _handleChange = useCallback(
(key: keyof SettingsStruct) => <T extends string | number>(value: T) =>
setSettings((state) => ({ ...state, [key]: value })),
[]
);
const _saveAndReload = useCallback(
() => saveAndReload(state),
[state]
);
const _save = useCallback(
(): void => {
save(state);
setChanged(null);
},
[state]
);
const { i18nLang, icon, ledgerConn, prefix, uiTheme } = state;
return (
<div className={className}>
<div className='ui--row'>
<Dropdown
defaultValue={prefix}
help={t<string>('Override the default ss58 prefix for address generation')}
label={t<string>('address prefix')}
onChange={_handleChange('prefix')}
options={prefixOptions}
/>
</div>
<div className='ui--row'>
<Dropdown
defaultValue={icon}
help={t<string>('Override the default identity icon display with a specific theme')}
label={t<string>('default icon theme')}
onChange={_handleChange('icon')}
options={iconOptions}
/>
</div>
{isLedgerCapable() && (
<div className='ui--row'>
<Dropdown
defaultValue={ledgerConn}
help={t<string>('Manage your connection to Ledger S')}
label={t<string>('manage hardware connections')}
onChange={_handleChange('ledgerConn')}
options={ledgerConnOptions}
/>
</div>
)}
<div className='ui--row'>
<Dropdown
defaultValue={uiTheme}
label={t<string>('default interface theme')}
onChange={_handleChange('uiTheme')}
options={themeOptions}
/>
</div>
<div className='ui--row'>
<Dropdown
defaultValue={i18nLang}
label={t<string>('default interface language')}
onChange={_handleChange('i18nLang')}
options={translateLanguages}
/>
</div>
<Button.Group>
<Button
icon='save'
isDisabled={changed === null}
label={
changed
? t<string>('Save & Reload')
: t<string>('Save')
}
onClick={
changed
? _saveAndReload
: _save
}
/>
</Button.Group>
</div>
);
}
export default React.memo(General);