1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import * as React from 'react';
    
  11. import {useCallback, useContext, useEffect, useMemo, useRef} from 'react';
    
  12. import {SettingsModalContext} from './SettingsModalContext';
    
  13. import Button from '../Button';
    
  14. import ButtonIcon from '../ButtonIcon';
    
  15. import TabBar from '../TabBar';
    
  16. import {StoreContext} from '../context';
    
  17. import {
    
  18.   useLocalStorage,
    
  19.   useModalDismissSignal,
    
  20.   useSubscription,
    
  21. } from '../hooks';
    
  22. import ComponentsSettings from './ComponentsSettings';
    
  23. import DebuggingSettings from './DebuggingSettings';
    
  24. import GeneralSettings from './GeneralSettings';
    
  25. import ProfilerSettings from './ProfilerSettings';
    
  26. 
    
  27. import styles from './SettingsModal.css';
    
  28. 
    
  29. type TabID = 'general' | 'components' | 'profiler';
    
  30. 
    
  31. export default function SettingsModal(_: {}): React.Node {
    
  32.   const {isModalShowing, setIsModalShowing} = useContext(SettingsModalContext);
    
  33.   const store = useContext(StoreContext);
    
  34.   const {profilerStore} = store;
    
  35. 
    
  36.   // Updating preferences while profiling is in progress could break things (e.g. filtering)
    
  37.   // Explicitly disallow it for now.
    
  38.   const isProfilingSubscription = useMemo(
    
  39.     () => ({
    
  40.       getCurrentValue: () => profilerStore.isProfiling,
    
  41.       subscribe: (callback: Function) => {
    
  42.         profilerStore.addListener('isProfiling', callback);
    
  43.         return () => profilerStore.removeListener('isProfiling', callback);
    
  44.       },
    
  45.     }),
    
  46.     [profilerStore],
    
  47.   );
    
  48.   const isProfiling = useSubscription<boolean>(isProfilingSubscription);
    
  49.   if (isProfiling && isModalShowing) {
    
  50.     setIsModalShowing(false);
    
  51.   }
    
  52. 
    
  53.   if (!isModalShowing) {
    
  54.     return null;
    
  55.   }
    
  56. 
    
  57.   return <SettingsModalImpl />;
    
  58. }
    
  59. 
    
  60. function SettingsModalImpl(_: {}) {
    
  61.   const {setIsModalShowing} = useContext(SettingsModalContext);
    
  62.   const dismissModal = useCallback(
    
  63.     () => setIsModalShowing(false),
    
  64.     [setIsModalShowing],
    
  65.   );
    
  66. 
    
  67.   const [selectedTabID, selectTab] = useLocalStorage<TabID>(
    
  68.     'React::DevTools::selectedSettingsTabID',
    
  69.     'general',
    
  70.   );
    
  71. 
    
  72.   const modalRef = useRef<HTMLDivElement | null>(null);
    
  73.   useModalDismissSignal(modalRef, dismissModal);
    
  74. 
    
  75.   useEffect(() => {
    
  76.     if (modalRef.current !== null) {
    
  77.       modalRef.current.focus();
    
  78.     }
    
  79.   }, [modalRef]);
    
  80. 
    
  81.   let view = null;
    
  82.   switch (selectedTabID) {
    
  83.     case 'components':
    
  84.       view = <ComponentsSettings />;
    
  85.       break;
    
  86.     // $FlowFixMe[incompatible-type] is this missing in TabID?
    
  87.     case 'debugging':
    
  88.       view = <DebuggingSettings />;
    
  89.       break;
    
  90.     case 'general':
    
  91.       view = <GeneralSettings />;
    
  92.       break;
    
  93.     case 'profiler':
    
  94.       view = <ProfilerSettings />;
    
  95.       break;
    
  96.     default:
    
  97.       break;
    
  98.   }
    
  99. 
    
  100.   return (
    
  101.     <div className={styles.Background}>
    
  102.       <div className={styles.Modal} ref={modalRef}>
    
  103.         <div className={styles.Tabs}>
    
  104.           <TabBar
    
  105.             currentTab={selectedTabID}
    
  106.             id="Settings"
    
  107.             selectTab={selectTab}
    
  108.             tabs={tabs}
    
  109.             type="settings"
    
  110.           />
    
  111.           <div className={styles.Spacer} />
    
  112.           <Button onClick={dismissModal} title="Close settings dialog">
    
  113.             <ButtonIcon type="close" />
    
  114.           </Button>
    
  115.         </div>
    
  116.         <div className={styles.Content}>{view}</div>
    
  117.       </div>
    
  118.     </div>
    
  119.   );
    
  120. }
    
  121. 
    
  122. const tabs = [
    
  123.   {
    
  124.     id: 'general',
    
  125.     icon: 'settings',
    
  126.     label: 'General',
    
  127.   },
    
  128.   {
    
  129.     id: 'debugging',
    
  130.     icon: 'bug',
    
  131.     label: 'Debugging',
    
  132.   },
    
  133.   {
    
  134.     id: 'components',
    
  135.     icon: 'components',
    
  136.     label: 'Components',
    
  137.   },
    
  138.   {
    
  139.     id: 'profiler',
    
  140.     icon: 'profiler',
    
  141.     label: 'Profiler',
    
  142.   },
    
  143. ];