addons/isl/src/TopBar.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import {Button} from 'isl-components/Button';
b69ab319import {FlexSpacer} from 'isl-components/Flex';
b69ab3110import {Icon} from 'isl-components/Icon';
b69ab3111import {DOCUMENTATION_DELAY, Tooltip} from 'isl-components/Tooltip';
b69ab3112import {useAtomValue} from 'jotai';
b69ab3113import {clearTrackedCache} from 'shared/LRU';
b69ab3114import {BookmarksManagerMenu} from './BookmarksManager';
b69ab3115import {BugButton} from './BugButton';
b69ab3116import {BulkActionsMenu} from './BulkActionsMenu';
b69ab3117import serverAPI from './ClientToServerAPI';
b69ab3118import {CwdSelector} from './CwdSelector';
b69ab3119import {DownloadCommitsTooltipButton} from './DownloadCommitsMenu';
b69ab3120import {FocusModeToggle} from './FocusMode';
b69ab3121import {generatedFileCache} from './GeneratedFile';
b69ab3122import {PullButton} from './PullButton';
b69ab3123import {SettingsGearButton} from './SettingsTooltip';
b69ab3124import {ShelvedChangesMenu} from './ShelvedChanges';
b69ab3125import {tracker} from './analytics';
b69ab3126import {DebugToolsButton} from './debug/DebugToolsButton';
b69ab3127import {T} from './i18n';
b69ab3128import {maybeRemoveForgottenOperation, useClearAllOptimisticState} from './operationsState';
4bb999b29import {applicationinfo, haveCommitsLoadedYet, haveRemotePath, isFetchingCommits, watchmanStatus} from './serverAPIState';
b69ab3130
b69ab3131import {Internal} from './Internal';
b69ab3132import './TopBar.css';
b69ab3133
b69ab3134export function TopBar() {
b69ab3135 const loaded = useAtomValue(haveCommitsLoadedYet);
b69ab3136 const canPush = useAtomValue(haveRemotePath);
4bb999b37 const appInfo = useAtomValue(applicationinfo);
4bb999b38 const readOnly = appInfo?.readOnly ?? false;
b69ab3139
b69ab3140 if (!loaded) {
b69ab3141 return null;
b69ab3142 }
b69ab3143 return (
b69ab3144 <div className="top-bar">
b69ab3145 <span className="button-group">
4bb999b46 {readOnly && <span className="read-only-badge"><T>Read-only</T></span>}
4bb999b47 {!readOnly && canPush && <PullButton />}
b69ab3148 <CwdSelector />
4bb999b49 {!readOnly && <DownloadCommitsTooltipButton />}
4bb999b50 {!readOnly && <ShelvedChangesMenu />}
4bb999b51 {!readOnly && <BulkActionsMenu />}
4bb999b52 {!readOnly && <BookmarksManagerMenu />}
4bb999b53 {!readOnly && Internal.FullRepoBranchButton && <Internal.FullRepoBranchButton />}
b69ab3154 <FetchingDataIndicator />
b69ab3155 </span>
b69ab3156 <span className="button-group">
b69ab3157 <FlexSpacer />
4bb999b58 {!readOnly && <DebugToolsButton />}
4fe1f3459 <WatchmanStatusIndicator />
b69ab3160 <FocusModeToggle />
4bb999b61 {!readOnly && <BugButton />}
b69ab3162 <SettingsGearButton />
b69ab3163 <RefreshButton />
b69ab3164 </span>
b69ab3165 </div>
b69ab3166 );
b69ab3167}
b69ab3168
b69ab3169function FetchingDataIndicator() {
b69ab3170 const isFetching = useAtomValue(isFetchingCommits);
b69ab3171 return <Icon icon={isFetching ? 'loading' : 'blank'} />;
b69ab3172}
b69ab3173
4fe1f3474function WatchmanStatusIndicator() {
4fe1f3475 const status = useAtomValue(watchmanStatus);
4fe1f3476 if (status == null) {
4fe1f3477 return null;
4fe1f3478 }
4fe1f3479 const titles: Record<string, string> = {
4fe1f3480 healthy: 'Watchman is active. Repository changes are detected instantly.',
4fe1f3481 initializing: 'Watchman is initializing. Auto-refresh may be delayed.',
4fe1f3482 reconnecting: 'Watchman is reconnecting. Auto-refresh may be delayed.',
4fe1f3483 errored: 'Watchman encountered an error. Falling back to polling for changes.',
4fe1f3484 ended: 'Watchman has stopped. Falling back to polling for changes.',
4fe1f3485 unavailable: 'Watchman is not installed. Falling back to polling for changes.',
4fe1f3486 };
4fe1f3487 const isHealthy = status === 'healthy';
4fe1f3488 return (
4fe1f3489 <Tooltip placement="bottom" title={titles[status] ?? 'Watchman status unknown'}>
4fe1f3490 <Button icon>
4fe1f3491 <Icon
4fe1f3492 icon={isHealthy ? 'eye' : 'warning'}
4fe1f3493 style={{color: isHealthy ? '#4d8a78' : 'var(--warning-foreground)'}}
4fe1f3494 />
4fe1f3495 </Button>
4fe1f3496 </Tooltip>
4fe1f3497 );
4fe1f3498}
4fe1f3499
b69ab31100function RefreshButton() {
b69ab31101 const clearOptimisticState = useClearAllOptimisticState();
b69ab31102 return (
b69ab31103 <Tooltip
b69ab31104 delayMs={DOCUMENTATION_DELAY}
b69ab31105 placement="bottom"
b69ab31106 title={<T>Re-fetch latest commits and uncommitted changes.</T>}>
b69ab31107 <Button
b69ab31108 onClick={() => {
b69ab31109 tracker.track('ClickedRefresh');
b69ab31110 clearOptimisticState();
b69ab31111 maybeRemoveForgottenOperation();
b69ab31112 generatedFileCache.clear(); // allow generated files to be rechecked
b69ab31113 serverAPI.postMessage({type: 'refresh'});
b69ab31114 clearTrackedCache();
b69ab31115 }}
b69ab31116 data-testid="refresh-button">
b69ab31117 <Icon icon="refresh" />
b69ab31118 </Button>
b69ab31119 </Tooltip>
b69ab31120 );
b69ab31121}