Skip to content

Commit b71b77e

Browse files
authored
feat: support manually attaching to session using session ID (#2754)
* remove unneeded i18next backends the caching only brings issues, and all hosting is local only anyway * working on updated attach to session design * refactor attached session caps extraction * validate user-provided session IDs * address comments * address comments * address comments * merge extracted code back to minimise unrelated changes
1 parent 25d6675 commit b71b77e

File tree

3 files changed

+72
-32
lines changed

3 files changed

+72
-32
lines changed

app/common/public/locales/en/translation.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@
152152
"number": "number",
153153
"JSON object": "JSON object",
154154
"connectToExistingSessionInstructions": "If you have an already-running session of the above server type, you can attach an inspector to it directly.",
155-
"selectSessionIDInDropdown": "Select the Session ID in the dropdown below.",
155+
"selectSessionID": "You can manually enter the ID of the session you want to attach to, or, if the server supports session discovery, simply select from a list of active sessions.",
156156
"allowUnauthorizedCerts": "Allow Unauthorized Certificates",
157157
"Use Proxy": "Use Proxy",
158158
"JSON Representation": "JSON Representation",
@@ -194,7 +194,7 @@
194194
"Description": "Description",
195195
"Created": "Created",
196196
"Actions": "Actions",
197-
"enterYourSessionId": "Enter your session ID here",
197+
"searchSessions": "Search discovered sessions",
198198
"Proxy URL": "Proxy URL",
199199
"Source": "Source",
200200
"Commands": "Commands",
@@ -263,7 +263,7 @@
263263
"confirmDeletion": "Are you sure you want to delete this?",
264264
"Copied!": "Copied!",
265265
"Error Fetching Session URL": "Error Fetching Session URL",
266-
"noResultsFound": "No results found",
266+
"noRunningSessionsFound": "No running sessions found",
267267
"invalidCapType": "Invalid capability type: {{type}}",
268268
"whitespaceDetected": "Text Starts and/or Ends With Whitespace",
269269
"duplicateCapabilityNameError": "A capability set with the same name already exists",
@@ -322,5 +322,6 @@
322322
"useMjpegStream": "Use MJPEG Stream",
323323
"useScreenshotApi": "Use Screenshot API",
324324
"Box Model": "Box Model",
325-
"detachFromSession": "Detach from Session"
325+
"detachFromSession": "Detach from Session",
326+
"enterSessionID": "Enter session ID for attaching manually"
326327
}

app/common/renderer/components/SessionBuilder/AttachToSessionTab/AttachToSession.jsx

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import {IconRefresh} from '@tabler/icons-react';
2-
import {Button, Card, Col, Form, Row, Select, Tooltip} from 'antd';
1+
import {IconLinkPlus, IconRefresh} from '@tabler/icons-react';
2+
import {Button, Card, Col, Empty, Form, Input, Row, Select, Spin, Tooltip} from 'antd';
3+
import _ from 'lodash';
4+
import {useRef, useState} from 'react';
35
import {useTranslation} from 'react-i18next';
46

7+
import {BUTTON} from '../../../constants/antd-types.js';
58
import {getSessionInfo} from '../../../utils/attaching-to-session.js';
69
import builderStyles from '../SessionBuilder.module.css';
710
import styles from './AttachToSession.module.css';
@@ -11,50 +14,86 @@ const AttachToSession = ({
1114
attachSessId,
1215
setAttachSessId,
1316
runningAppiumSessions,
17+
gettingSessions,
1418
getRunningSessions,
19+
loadNewSession,
1520
}) => {
1621
const {t} = useTranslation();
22+
const [manualSessionId, setManualSessionId] = useState(null);
23+
const debouncedSetManualSessionId = useRef(
24+
_.debounce((value) => setManualSessionId(value), 200),
25+
).current;
26+
27+
// list is reversed in order to place the most recent sessions at the top
28+
// slice() is added because reverse() mutates the original array
29+
const sortedRunningSessions = [...runningAppiumSessions]
30+
.reverse()
31+
.map((session) => ({value: session.id, label: getSessionInfo(session, serverType)}));
32+
1733
return (
1834
<Form>
1935
<Form.Item>
2036
<Card>
2137
<p className={builderStyles.localDesc}>
2238
{t('connectToExistingSessionInstructions')}
2339
<br />
24-
{t('selectSessionIDInDropdown')}
40+
{t('selectSessionID')}
2541
</p>
2642
</Card>
2743
</Form.Item>
2844
<Form.Item>
29-
<Row>
30-
<Col span={23}>
31-
<Select
32-
showSearch
33-
notFoundContent={t('noResultsFound')}
34-
placeholder={t('enterYourSessionId')}
35-
value={attachSessId || undefined}
36-
onChange={(value) => setAttachSessId(value)}
37-
options={runningAppiumSessions
38-
.slice()
39-
.reverse()
40-
.map((session) =>
41-
// list is reversed in order to place the most recent sessions at the top
42-
// slice() is added because reverse() mutates the original array
43-
({value: session.id, label: getSessionInfo(session, serverType)}),
44-
)}
45+
<Row gutter={8}>
46+
<Col span={8} offset={6}>
47+
<Input
48+
placeholder={t('enterSessionID')}
49+
allowClear={true}
50+
onChange={(e) => debouncedSetManualSessionId(e.target.value)}
4551
/>
4652
</Col>
47-
<Col span={1}>
48-
<Tooltip title={t('Reload')}>
49-
<Button
50-
className={styles.btnReload}
51-
onClick={getRunningSessions}
52-
icon={<IconRefresh size={18} />}
53-
/>
54-
</Tooltip>
53+
<Col span={4}>
54+
<Button
55+
type={BUTTON.PRIMARY}
56+
disabled={!manualSessionId || manualSessionId.trim() === ''}
57+
onClick={() => loadNewSession(null, manualSessionId)}
58+
icon={<IconLinkPlus size={18} />}
59+
>
60+
{t('attachToSession')}
61+
</Button>
5562
</Col>
5663
</Row>
5764
</Form.Item>
65+
<Spin spinning={gettingSessions}>
66+
{sortedRunningSessions.length !== 0 ? (
67+
<Form.Item>
68+
<Row>
69+
<Col span={23}>
70+
<Select
71+
showSearch
72+
placeholder={t('searchSessions')}
73+
value={attachSessId || undefined}
74+
onChange={(value) => setAttachSessId(value)}
75+
options={sortedRunningSessions}
76+
/>
77+
</Col>
78+
<Col span={1}>
79+
<Tooltip title={t('Reload')}>
80+
<Button
81+
className={styles.btnReload}
82+
onClick={getRunningSessions}
83+
icon={<IconRefresh size={18} />}
84+
/>
85+
</Tooltip>
86+
</Col>
87+
</Row>
88+
</Form.Item>
89+
) : (
90+
<Empty description={t('noRunningSessionsFound')} image={Empty.PRESENTED_IMAGE_SIMPLE}>
91+
<Button onClick={getRunningSessions} icon={<IconRefresh size={18} />}>
92+
{t('Reload')}
93+
</Button>
94+
</Empty>
95+
)}
96+
</Spin>
5897
</Form>
5998
);
6099
};

app/common/renderer/components/SessionBuilder/SessionBuilder.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ const Session = (props) => {
176176
label: t('Attach to Session'),
177177
key: SESSION_BUILDER_TABS.ATTACH_TO_SESSION,
178178
className: styles.scrollingTab,
179-
children: <AttachToSession {...props} />,
179+
children: <AttachToSession loadNewSession={loadNewSession} {...props} />,
180180
},
181181
]}
182182
/>

0 commit comments

Comments
 (0)