3.9 KB150 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 {Heartbeat} from './heartbeat';
9
10import * as stylex from '@stylexjs/stylex';
11import {Button} from 'isl-components/Button';
12import {ErrorNotice} from 'isl-components/ErrorNotice';
13import {Icon} from 'isl-components/Icon';
14import {Tooltip} from 'isl-components/Tooltip';
15import {useAtomValue} from 'jotai';
16import {Copyable} from './Copyable';
17import {DropdownFields} from './DropdownFields';
18import {Internal} from './Internal';
19import {DEFAULT_HEARTBEAT_TIMEOUT_MS, useHeartbeat} from './heartbeat';
20import {t, T} from './i18n';
21import platform from './platform';
22import {applicationinfo} from './serverAPIState';
23
24import './BugButton.css';
25
26const styles = stylex.create({
27 centered: {
28 justifyContent: 'center',
29 },
30});
31
32export function BugButton() {
33 return (
34 <Tooltip
35 trigger="click"
36 component={dismiss => <BugDropdown dismiss={dismiss} />}
37 group="topbar"
38 placement="bottom">
39 <Button icon data-testid="bug-button">
40 <Icon icon="bug" />
41 </Button>
42 </Tooltip>
43 );
44}
45
46function BugDropdown({dismiss}: {dismiss: () => void}) {
47 const heartbeat = useHeartbeat();
48
49 return (
50 <DropdownFields
51 title={<T>Help</T>}
52 icon="bug"
53 data-testid="bug-dropdown"
54 className="bug-dropdown">
55 <ISLVersion />
56 <HeartbeatWarning heartbeat={heartbeat} />
57 <div className="bug-dropdown-actions">
58 <FileABug dismissBugDropdown={dismiss} heartbeat={heartbeat} />
59 </div>
60 </DropdownFields>
61 );
62}
63
64function ISLVersion() {
65 const info = useAtomValue(applicationinfo);
66 if (info == null) {
67 return <Icon icon="loading" />;
68 }
69
70 return (
71 <div className="bug-dropdown-version">
72 <Copyable children={`ISL version ${info.version} (${info.platformName})`} />
73 </div>
74 );
75}
76
77function HeartbeatWarning({heartbeat}: {heartbeat: Heartbeat}) {
78 const appInfo = useAtomValue(applicationinfo);
79 if (heartbeat.type === 'timeout') {
80 return (
81 <>
82 <ErrorNotice
83 error={new Error(t(`Heartbeat timed out after ${DEFAULT_HEARTBEAT_TIMEOUT_MS}ms`))}
84 title={t("Can't reach server — most features won't work")}
85 description={t('The ISL server needs to be restarted')}></ErrorNotice>
86 {appInfo && (
87 <div>
88 <T
89 replace={{
90 $logfile: (
91 <code>
92 <Copyable className="log-file-path">{appInfo.logFilePath}</Copyable>
93 </code>
94 ),
95 }}>
96 Your log file is located at: $logfile
97 </T>
98 </div>
99 )}
100 </>
101 );
102 }
103 return null;
104}
105
106function FileABug({
107 dismissBugDropdown,
108 heartbeat,
109}: {
110 dismissBugDropdown: () => void;
111 heartbeat: Heartbeat;
112}) {
113 return Internal.FileABugButton != null ? (
114 <Internal.FileABugButton dismissBugDropdown={dismissBugDropdown} heartbeat={heartbeat} />
115 ) : (
116 <OSSFileABug />
117 );
118}
119
120function OSSFileABug() {
121 return (
122 <>
123 <Button
124 xstyle={styles.centered}
125 onClick={() => {
126 platform.openExternalLink('https://sapling-scm.com/docs/addons/isl');
127 }}>
128 <Icon icon="book" slot="start" />
129 <T>View Documentation</T>
130 </Button>
131 <Button
132 xstyle={styles.centered}
133 onClick={() => {
134 platform.openExternalLink('https://discord.gg/X6baZ94Vzh');
135 }}>
136 <Icon icon="comment-discussion" slot="start" />
137 <T>Help and Feedback on Discord</T>
138 </Button>
139 <Button
140 xstyle={styles.centered}
141 onClick={() => {
142 platform.openExternalLink('https://github.com/facebook/sapling/issues');
143 }}>
144 <Icon icon="bug" slot="start" />
145 <T>Report an Issue on GitHub</T>
146 </Button>
147 </>
148 );
149}
150