General.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // Copyright 2017-2020 @polkadot/app-settings authors & contributors
  2. // This software may be modified and distributed under the terms
  3. // of the Apache-2.0 license. See the LICENSE file for details.
  4. import { Option } from '@polkadot/apps-config/settings/types';
  5. import React, { useCallback, useEffect, useState, useMemo } from 'react';
  6. import { availableLanguages, availableSs58 } from '@polkadot/apps-config/settings';
  7. import { isLedgerCapable } from '@polkadot/react-api';
  8. import { Button, ButtonCancel, Dropdown } from '@polkadot/react-components';
  9. import uiSettings, { SettingsStruct } from '@polkadot/ui-settings';
  10. import { useTranslation } from './translate';
  11. import { createIdenticon, createOption, save, saveAndReload } from './util';
  12. import SelectUrl from './SelectUrl';
  13. interface Props {
  14. className?: string;
  15. isModalContent?: boolean;
  16. onClose: () => void;
  17. }
  18. const ledgerConnOptions = uiSettings.availableLedgerConn;
  19. function General ({ className, isModalContent, onClose }: Props): React.ReactElement<Props> {
  20. const { t } = useTranslation();
  21. // tri-state: null = nothing changed, false = no reload, true = reload required
  22. const [changed, setChanged] = useState<boolean | null>(null);
  23. const [settings, setSettings] = useState(uiSettings.get());
  24. const iconOptions = useMemo((): Option[] => {
  25. return uiSettings.availableIcons.map((o): Option => createIdenticon(t, o, ['default']));
  26. }, [t]);
  27. const prefixOptions = useMemo((): (Option | React.ReactNode)[] => {
  28. return availableSs58.map((o): Option | React.ReactNode => createOption(t, o, ['default']));
  29. }, [t]);
  30. const translateLanguages = useMemo((): Option[] => {
  31. return availableLanguages.map(({ text, value, withI18n }) => ({
  32. value,
  33. text: withI18n ? t(text as string) : text
  34. }));
  35. }, [t]);
  36. useEffect((): void => {
  37. const prev = uiSettings.get();
  38. const hasChanges = Object.entries(settings).some(([key, value]): boolean => (prev as any)[key] !== value);
  39. const needsReload = prev.apiUrl !== settings.apiUrl || prev.prefix !== settings.prefix;
  40. setChanged(
  41. hasChanges
  42. ? needsReload
  43. : null
  44. );
  45. }, [settings]);
  46. const _handleChange = useCallback(
  47. (key: keyof SettingsStruct) => <T extends string | number>(value: T): void =>
  48. setSettings((settings) => ({ ...settings, [key]: value })),
  49. []
  50. );
  51. const _saveAndReload = useCallback(
  52. (): void => saveAndReload(settings),
  53. [settings]
  54. );
  55. const _save = useCallback(
  56. (): void => {
  57. save(settings);
  58. setChanged(null);
  59. },
  60. [settings]
  61. );
  62. const { icon, i18nLang, ledgerConn, prefix, uiMode } = settings;
  63. return (
  64. <div className={className}>
  65. <SelectUrl onChange={_handleChange('apiUrl')} />
  66. {!isModalContent && (
  67. <>
  68. <div className='ui--row'>
  69. <Dropdown
  70. defaultValue={prefix}
  71. help={t('Override the default ss58 prefix for address generation')}
  72. label={t('address prefix')}
  73. onChange={_handleChange('prefix')}
  74. options={prefixOptions}
  75. />
  76. </div>
  77. <div className='ui--row'>
  78. <Dropdown
  79. defaultValue={icon}
  80. help={t('Override the default identity icon display with a specific theme')}
  81. label={t('default icon theme')}
  82. onChange={_handleChange('icon')}
  83. options={iconOptions}
  84. />
  85. </div>
  86. <div className='ui--row'>
  87. <Dropdown
  88. defaultValue={uiMode}
  89. help={t('Adjust the mode from basic (with a limited number of beginner-user-friendly apps) to full (with all basic & advanced apps available)')}
  90. label={t('interface operation mode')}
  91. onChange={_handleChange('uiMode')}
  92. options={uiSettings.availableUIModes}
  93. />
  94. </div>
  95. {isLedgerCapable() && (
  96. <div className='ui--row'>
  97. <Dropdown
  98. defaultValue={ledgerConn}
  99. help={t('Manage your connection to Ledger S')}
  100. label={t('manage hardware connections')}
  101. onChange={_handleChange('ledgerConn')}
  102. options={ledgerConnOptions}
  103. />
  104. </div>
  105. )}
  106. <div className='ui--row'>
  107. <Dropdown
  108. defaultValue={i18nLang}
  109. label={t('default interface language')}
  110. onChange={_handleChange('i18nLang')}
  111. options={translateLanguages}
  112. />
  113. </div>
  114. </>
  115. )}
  116. <Button.Group>
  117. {isModalContent && (
  118. <>
  119. <ButtonCancel onClick={onClose} />
  120. <Button.Or />
  121. </>
  122. )}
  123. <Button
  124. icon='save'
  125. isDisabled={changed === null}
  126. isPrimary={isModalContent}
  127. label={
  128. changed
  129. ? t('Save & Reload')
  130. : t('Save')
  131. }
  132. onClick={
  133. changed
  134. ? _saveAndReload
  135. : _save
  136. }
  137. />
  138. </Button.Group>
  139. </div>
  140. );
  141. }
  142. export default React.memo(General);