3.6 KB113 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 {UICodeReviewProvider} from './codeReview/UICodeReviewProvider';
9import type {CommitInfo, DiffSummary, Hash} from './types';
10
11import {Button} from 'isl-components/Button';
12import {Icon} from 'isl-components/Icon';
13import {Tooltip} from 'isl-components/Tooltip';
14import {useAtomValue} from 'jotai';
15import {nullthrows} from 'shared/utils';
16import {OperationDisabledButton} from './OperationDisabledButton';
17import {allDiffSummaries, codeReviewProvider} from './codeReview/CodeReviewInfo';
18import {t, T} from './i18n';
19import {HideOperation} from './operations/HideOperation';
20import {useRunOperation} from './operationsState';
21import {type Dag, dagWithPreviews} from './previews';
22import {latestSuccessorUnlessExplicitlyObsolete} from './successionUtils';
23
24export function isStackEligibleForCleanup(
25 hash: Hash,
26 dag: Dag,
27 diffMap: Map<string, DiffSummary>,
28 provider: UICodeReviewProvider,
29): boolean {
30 return dag
31 .descendants(hash)
32 .toSeq()
33 .every(h => {
34 const info = dag.get(h);
35 // don't allow hiding a stack you're checked out on
36 if (info == null || info.isDot) {
37 return false;
38 }
39 // allow clean up obsoleted commits regardless of review state
40 if (info.successorInfo != null) {
41 return true;
42 }
43 // if not obsoleted, still allow cleanup for "Closed" diffs
44 if (info.diffId != null) {
45 const diffInfo = diffMap.get(info.diffId);
46 if (diffInfo != null && provider.isDiffEligibleForCleanup(diffInfo)) {
47 return true;
48 }
49 }
50 // no cleanup otherwise
51 return false;
52 });
53}
54
55export function CleanupButton({commit, hasChildren}: {commit: CommitInfo; hasChildren: boolean}) {
56 const runOperation = useRunOperation();
57 return (
58 <Tooltip
59 title={
60 hasChildren
61 ? t('You can safely "clean up" by hiding this stack of commits.')
62 : t('You can safely "clean up" by hiding this commit.')
63 }
64 placement="bottom">
65 <Button
66 icon
67 onClick={() => {
68 runOperation(new HideOperation(latestSuccessorUnlessExplicitlyObsolete(commit)));
69 }}>
70 <Icon icon="eye-closed" slot="start" />
71 {hasChildren ? <T>Clean up stack</T> : <T>Clean up</T>}
72 </Button>
73 </Tooltip>
74 );
75}
76
77export function CleanupAllButton() {
78 const dag = useAtomValue(dagWithPreviews);
79 const reviewProvider = useAtomValue(codeReviewProvider);
80 const diffMap = useAtomValue(allDiffSummaries)?.value;
81 if (diffMap == null || reviewProvider == null) {
82 return null;
83 }
84
85 const stackBases = dag.roots(dag.draft()).toArray();
86 const cleanableStacks = stackBases.filter(hash =>
87 isStackEligibleForCleanup(hash, dag, diffMap, reviewProvider),
88 );
89
90 const disabled = cleanableStacks.length === 0;
91 return (
92 <Tooltip
93 title={
94 disabled
95 ? t('No landed or closed commits to hide')
96 : t('Hide all commits for landed or closed Diffs')
97 }>
98 <OperationDisabledButton
99 contextKey="cleanup-all"
100 runOperation={() => {
101 return cleanableStacks.map(hash => {
102 const info = nullthrows(dag.get(hash));
103 return new HideOperation(latestSuccessorUnlessExplicitlyObsolete(info));
104 });
105 }}
106 icon={<Icon icon="eye-closed" slot="start" />}
107 disabled={disabled}>
108 <T>Clean up all</T>
109 </OperationDisabledButton>
110 </Tooltip>
111 );
112}
113