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 {Fragment, useContext} from 'react';
    
  12. import {ModalDialog} from '../ModalDialog';
    
  13. import {ProfilerContext} from './ProfilerContext';
    
  14. import TabBar from '../TabBar';
    
  15. import ClearProfilingDataButton from './ClearProfilingDataButton';
    
  16. import CommitFlamegraph from './CommitFlamegraph';
    
  17. import CommitRanked from './CommitRanked';
    
  18. import RootSelector from './RootSelector';
    
  19. import {Timeline} from 'react-devtools-timeline/src/Timeline';
    
  20. import SidebarEventInfo from './SidebarEventInfo';
    
  21. import RecordToggle from './RecordToggle';
    
  22. import ReloadAndProfileButton from './ReloadAndProfileButton';
    
  23. import ProfilingImportExportButtons from './ProfilingImportExportButtons';
    
  24. import SnapshotSelector from './SnapshotSelector';
    
  25. import SidebarCommitInfo from './SidebarCommitInfo';
    
  26. import NoProfilingData from './NoProfilingData';
    
  27. import RecordingInProgress from './RecordingInProgress';
    
  28. import ProcessingData from './ProcessingData';
    
  29. import ProfilingNotSupported from './ProfilingNotSupported';
    
  30. import SidebarSelectedFiberInfo from './SidebarSelectedFiberInfo';
    
  31. import SettingsModal from 'react-devtools-shared/src/devtools/views/Settings/SettingsModal';
    
  32. import SettingsModalContextToggle from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContextToggle';
    
  33. import {SettingsModalContextController} from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContext';
    
  34. import portaledContent from '../portaledContent';
    
  35. import {StoreContext} from '../context';
    
  36. import {TimelineContext} from 'react-devtools-timeline/src/TimelineContext';
    
  37. 
    
  38. import styles from './Profiler.css';
    
  39. 
    
  40. function Profiler(_: {}) {
    
  41.   const {
    
  42.     didRecordCommits,
    
  43.     isProcessingData,
    
  44.     isProfiling,
    
  45.     selectedCommitIndex,
    
  46.     selectedFiberID,
    
  47.     selectedTabID,
    
  48.     selectTab,
    
  49.     supportsProfiling,
    
  50.   } = useContext(ProfilerContext);
    
  51. 
    
  52.   const {file: timelineTraceEventData, searchInputContainerRef} =
    
  53.     useContext(TimelineContext);
    
  54. 
    
  55.   const {supportsTimeline} = useContext(StoreContext);
    
  56. 
    
  57.   const isLegacyProfilerSelected = selectedTabID !== 'timeline';
    
  58. 
    
  59.   let view = null;
    
  60.   if (didRecordCommits || selectedTabID === 'timeline') {
    
  61.     switch (selectedTabID) {
    
  62.       case 'flame-chart':
    
  63.         view = <CommitFlamegraph />;
    
  64.         break;
    
  65.       case 'ranked-chart':
    
  66.         view = <CommitRanked />;
    
  67.         break;
    
  68.       case 'timeline':
    
  69.         view = <Timeline />;
    
  70.         break;
    
  71.       default:
    
  72.         break;
    
  73.     }
    
  74.   } else if (isProfiling) {
    
  75.     view = <RecordingInProgress />;
    
  76.   } else if (isProcessingData) {
    
  77.     view = <ProcessingData />;
    
  78.   } else if (timelineTraceEventData) {
    
  79.     view = <OnlyTimelineData />;
    
  80.   } else if (supportsProfiling) {
    
  81.     view = <NoProfilingData />;
    
  82.   } else {
    
  83.     view = <ProfilingNotSupported />;
    
  84.   }
    
  85. 
    
  86.   let sidebar = null;
    
  87.   if (!isProfiling && !isProcessingData && didRecordCommits) {
    
  88.     switch (selectedTabID) {
    
  89.       case 'flame-chart':
    
  90.       case 'ranked-chart':
    
  91.         // TRICKY
    
  92.         // Handle edge case where no commit is selected because of a min-duration filter update.
    
  93.         // In that case, the selected commit index would be null.
    
  94.         // We could still show a sidebar for the previously selected fiber,
    
  95.         // but it would be an odd user experience.
    
  96.         // TODO (ProfilerContext) This check should not be necessary.
    
  97.         if (selectedCommitIndex !== null) {
    
  98.           if (selectedFiberID !== null) {
    
  99.             sidebar = <SidebarSelectedFiberInfo />;
    
  100.           } else {
    
  101.             sidebar = <SidebarCommitInfo />;
    
  102.           }
    
  103.         }
    
  104.         break;
    
  105.       case 'timeline':
    
  106.         sidebar = <SidebarEventInfo />;
    
  107.         break;
    
  108.       default:
    
  109.         break;
    
  110.     }
    
  111.   }
    
  112. 
    
  113.   return (
    
  114.     <SettingsModalContextController>
    
  115.       <div className={styles.Profiler}>
    
  116.         <div className={styles.LeftColumn}>
    
  117.           <div className={styles.Toolbar}>
    
  118.             <RecordToggle disabled={!supportsProfiling} />
    
  119.             <ReloadAndProfileButton disabled={!supportsProfiling} />
    
  120.             <ClearProfilingDataButton />
    
  121.             <ProfilingImportExportButtons />
    
  122.             <div className={styles.VRule} />
    
  123.             <TabBar
    
  124.               currentTab={selectedTabID}
    
  125.               id="Profiler"
    
  126.               selectTab={selectTab}
    
  127.               tabs={supportsTimeline ? tabsWithTimeline : tabs}
    
  128.               type="profiler"
    
  129.             />
    
  130.             <RootSelector />
    
  131.             <div className={styles.Spacer} />
    
  132.             {!isLegacyProfilerSelected && (
    
  133.               <div
    
  134.                 ref={searchInputContainerRef}
    
  135.                 className={styles.TimelineSearchInputContainer}
    
  136.               />
    
  137.             )}
    
  138.             <SettingsModalContextToggle />
    
  139.             {isLegacyProfilerSelected && didRecordCommits && (
    
  140.               <Fragment>
    
  141.                 <div className={styles.VRule} />
    
  142.                 <SnapshotSelector />
    
  143.               </Fragment>
    
  144.             )}
    
  145.           </div>
    
  146.           <div className={styles.Content}>
    
  147.             {view}
    
  148.             <ModalDialog />
    
  149.           </div>
    
  150.         </div>
    
  151.         <div className={styles.RightColumn}>{sidebar}</div>
    
  152.         <SettingsModal />
    
  153.       </div>
    
  154.     </SettingsModalContextController>
    
  155.   );
    
  156. }
    
  157. 
    
  158. const OnlyTimelineData = () => (
    
  159.   <div className={styles.Column}>
    
  160.     <div className={styles.Header}>Timeline only</div>
    
  161.     <div className={styles.Row}>
    
  162.       The current profile contains only Timeline data.
    
  163.     </div>
    
  164.   </div>
    
  165. );
    
  166. 
    
  167. const tabs = [
    
  168.   {
    
  169.     id: 'flame-chart',
    
  170.     icon: 'flame-chart',
    
  171.     label: 'Flamegraph',
    
  172.     title: 'Flamegraph chart',
    
  173.   },
    
  174.   {
    
  175.     id: 'ranked-chart',
    
  176.     icon: 'ranked-chart',
    
  177.     label: 'Ranked',
    
  178.     title: 'Ranked chart',
    
  179.   },
    
  180. ];
    
  181. 
    
  182. const tabsWithTimeline = [
    
  183.   ...tabs,
    
  184.   null, // Divider/separator
    
  185.   {
    
  186.     id: 'timeline',
    
  187.     icon: 'timeline',
    
  188.     label: 'Timeline',
    
  189.     title: 'Timeline',
    
  190.   },
    
  191. ];
    
  192. 
    
  193. export default (portaledContent(Profiler): React.ComponentType<{}>);