3.6 KB106 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 type {Operation} from './operations/Operation';
9
10import {Button} from 'isl-components/Button';
11import {ButtonDropdown} from 'isl-components/ButtonDropdown';
12import {Icon} from 'isl-components/Icon';
13import {DOCUMENTATION_DELAY, Tooltip} from 'isl-components/Tooltip';
14import {useAtom, useAtomValue} from 'jotai';
15import {fetchStableLocations} from './BookmarksData';
16import {Internal} from './Internal';
17import {t, T} from './i18n';
18import {configBackedAtom} from './jotaiUtils';
19import {PullOperation} from './operations/PullOperation';
20import {useRunOperation} from './operationsState';
21import {uncommittedChangesWithPreviews, useMostRecentPendingOperation} from './previews';
22
23import './PullButton.css';
24
25const DEFAULT_PULL_BUTTON = {
26 id: 'pull',
27 label: <T>Pull</T>,
28 getOperation: () => new PullOperation(),
29 isRunning: (op: Operation) => op instanceof PullOperation,
30 tooltip: t('Fetch latest repository and branch information from remote.'),
31 allowWithUncommittedChanges: true,
32};
33const pullButtonChoiceKey = configBackedAtom<string>(
34 'isl.pull-button-choice',
35 DEFAULT_PULL_BUTTON.id,
36);
37
38export type PullButtonOption = {
39 id: string;
40 label: React.ReactNode;
41 getOperation: () => Operation;
42 isRunning: (op: Operation) => boolean;
43 tooltip: string;
44 allowWithUncommittedChanges: boolean;
45};
46
47export function PullButton() {
48 const runOperation = useRunOperation();
49
50 const pullButtonOptions: Array<PullButtonOption> = [];
51 pullButtonOptions.push(DEFAULT_PULL_BUTTON, ...(Internal.additionalPullOptions ?? []));
52
53 const [dropdownChoiceKey, setDropdownChoiceKey] = useAtom(pullButtonChoiceKey);
54 const currentChoice =
55 pullButtonOptions.find(option => option.id === dropdownChoiceKey) ?? pullButtonOptions[0];
56
57 const trackedChanges = useAtomValue(uncommittedChangesWithPreviews).filter(
58 change => change.status !== '?',
59 );
60 const hasUncommittedChanges = trackedChanges.length > 0;
61
62 const disabledFromUncommittedChanges =
63 currentChoice.allowWithUncommittedChanges === false && hasUncommittedChanges;
64
65 let tooltip =
66 currentChoice.tooltip +
67 (disabledFromUncommittedChanges == false
68 ? ''
69 : '\n\n' + t('Disabled due to uncommitted changes.'));
70 const pendingOperation = useMostRecentPendingOperation();
71 const isRunningPull = pendingOperation != null && currentChoice.isRunning(pendingOperation);
72 if (isRunningPull) {
73 tooltip += '\n\n' + t('Pull is already running.');
74 }
75
76 return (
77 <Tooltip placement="bottom" delayMs={DOCUMENTATION_DELAY} title={tooltip}>
78 <div className="pull-info">
79 {pullButtonOptions.length > 1 ? (
80 <ButtonDropdown
81 buttonDisabled={!!isRunningPull || disabledFromUncommittedChanges}
82 options={pullButtonOptions}
83 onClick={() => {
84 runOperation(currentChoice.getOperation());
85 fetchStableLocations();
86 }}
87 onChangeSelected={choice => setDropdownChoiceKey(choice.id)}
88 selected={currentChoice}
89 icon={<Icon slot="start" icon={isRunningPull ? 'loading' : 'repo'} />}
90 />
91 ) : (
92 <Button
93 disabled={!!isRunningPull}
94 onClick={() => {
95 runOperation(new PullOperation());
96 fetchStableLocations();
97 }}>
98 <Icon slot="start" icon={isRunningPull ? 'loading' : 'cloud-download'} />
99 <T>Pull</T>
100 </Button>
101 )}
102 </div>
103 </Tooltip>
104 );
105}
106