4.3 KB122 lines
Blame
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
8import {Button} from 'isl-components/Button';
9import {FlexSpacer} from 'isl-components/Flex';
10import {Icon} from 'isl-components/Icon';
11import {DOCUMENTATION_DELAY, Tooltip} from 'isl-components/Tooltip';
12import {useAtomValue} from 'jotai';
13import {clearTrackedCache} from 'shared/LRU';
14import {BookmarksManagerMenu} from './BookmarksManager';
15import {BugButton} from './BugButton';
16import {BulkActionsMenu} from './BulkActionsMenu';
17import serverAPI from './ClientToServerAPI';
18import {CwdSelector} from './CwdSelector';
19import {DownloadCommitsTooltipButton} from './DownloadCommitsMenu';
20import {FocusModeToggle} from './FocusMode';
21import {generatedFileCache} from './GeneratedFile';
22import {PullButton} from './PullButton';
23import {SettingsGearButton} from './SettingsTooltip';
24import {ShelvedChangesMenu} from './ShelvedChanges';
25import {tracker} from './analytics';
26import {DebugToolsButton} from './debug/DebugToolsButton';
27import {T} from './i18n';
28import {maybeRemoveForgottenOperation, useClearAllOptimisticState} from './operationsState';
29import {applicationinfo, haveCommitsLoadedYet, haveRemotePath, isFetchingCommits, watchmanStatus} from './serverAPIState';
30
31import {Internal} from './Internal';
32import './TopBar.css';
33
34export function TopBar() {
35 const loaded = useAtomValue(haveCommitsLoadedYet);
36 const canPush = useAtomValue(haveRemotePath);
37 const appInfo = useAtomValue(applicationinfo);
38 const readOnly = appInfo?.readOnly ?? false;
39
40 if (!loaded) {
41 return null;
42 }
43 return (
44 <div className="top-bar">
45 <span className="button-group">
46 {readOnly && <span className="read-only-badge"><T>Read-only</T></span>}
47 {!readOnly && canPush && <PullButton />}
48 <CwdSelector />
49 {!readOnly && <DownloadCommitsTooltipButton />}
50 {!readOnly && <ShelvedChangesMenu />}
51 {!readOnly && <BulkActionsMenu />}
52 {!readOnly && <BookmarksManagerMenu />}
53 {!readOnly && Internal.FullRepoBranchButton && <Internal.FullRepoBranchButton />}
54 <FetchingDataIndicator />
55 </span>
56 <span className="button-group">
57 <FlexSpacer />
58 {!readOnly && <DebugToolsButton />}
59 <WatchmanStatusIndicator />
60 <FocusModeToggle />
61 {!readOnly && <BugButton />}
62 <SettingsGearButton />
63 <RefreshButton />
64 </span>
65 </div>
66 );
67}
68
69function FetchingDataIndicator() {
70 const isFetching = useAtomValue(isFetchingCommits);
71 return <Icon icon={isFetching ? 'loading' : 'blank'} />;
72}
73
74function WatchmanStatusIndicator() {
75 const status = useAtomValue(watchmanStatus);
76 if (status == null) {
77 return null;
78 }
79 const titles: Record<string, string> = {
80 healthy: 'Watchman is active. Repository changes are detected instantly.',
81 initializing: 'Watchman is initializing. Auto-refresh may be delayed.',
82 reconnecting: 'Watchman is reconnecting. Auto-refresh may be delayed.',
83 errored: 'Watchman encountered an error. Falling back to polling for changes.',
84 ended: 'Watchman has stopped. Falling back to polling for changes.',
85 unavailable: 'Watchman is not installed. Falling back to polling for changes.',
86 };
87 const isHealthy = status === 'healthy';
88 return (
89 <Tooltip placement="bottom" title={titles[status] ?? 'Watchman status unknown'}>
90 <Button icon>
91 <Icon
92 icon={isHealthy ? 'eye' : 'warning'}
93 style={{color: isHealthy ? '#4d8a78' : 'var(--warning-foreground)'}}
94 />
95 </Button>
96 </Tooltip>
97 );
98}
99
100function RefreshButton() {
101 const clearOptimisticState = useClearAllOptimisticState();
102 return (
103 <Tooltip
104 delayMs={DOCUMENTATION_DELAY}
105 placement="bottom"
106 title={<T>Re-fetch latest commits and uncommitted changes.</T>}>
107 <Button
108 onClick={() => {
109 tracker.track('ClickedRefresh');
110 clearOptimisticState();
111 maybeRemoveForgottenOperation();
112 generatedFileCache.clear(); // allow generated files to be rechecked
113 serverAPI.postMessage({type: 'refresh'});
114 clearTrackedCache();
115 }}
116 data-testid="refresh-button">
117 <Icon icon="refresh" />
118 </Button>
119 </Tooltip>
120 );
121}
122