Skip to content

Commit 1f05e00

Browse files
authored
feat: add button to detach from session without termination (#2641)
* feat: add button to detach from session without termination * docs: add docs for detach button * address copilot comment * docs: add emphasis that the session is retained
1 parent eafde26 commit 1f05e00

File tree

10 files changed

+44
-21
lines changed

10 files changed

+44
-21
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,5 +321,6 @@
321321
"Import from File": "Import from File",
322322
"useMjpegStream": "Use MJPEG Stream",
323323
"useScreenshotApi": "Use Screenshot API",
324-
"Box Model": "Box Model"
324+
"Box Model": "Box Model",
325+
"detachFromSession": "Detach from Session"
325326
}

app/common/renderer/actions/SessionBuilder.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ export function bindWindowClose() {
991991
let {driver} = getState().inspector;
992992
if (driver) {
993993
try {
994-
const action = quitSession('Window closed');
994+
const action = quitSession();
995995
await action(dispatch, getState);
996996
} finally {
997997
driver = null;

app/common/renderer/actions/SessionInspector.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ export function restartSession(error, params) {
263263
title: i18n.t('RestartSessionMessage'),
264264
duration: 3,
265265
});
266-
const quitSes = quitSession('Window closed');
266+
const quitSes = quitSession();
267267
const newSes = newSession(getState().builder.caps);
268268
const getPageSrc = applyClientMethod({methodName: 'getPageSource'});
269269
const storeSessionSet = storeSessionSettings();
@@ -298,14 +298,16 @@ export function setExpandedPaths(paths) {
298298
/**
299299
* Quit the session and go back to the new session window
300300
*/
301-
export function quitSession(reason, killedByUser = true) {
301+
export function quitSession({reason, manualQuit = true, detachOnly = false} = {}) {
302302
return async (dispatch, getState) => {
303303
const killAction = killKeepAliveLoop();
304304
killAction(dispatch, getState);
305-
const applyAction = applyClientMethod({methodName: 'deleteSession'});
306-
await applyAction(dispatch, getState);
305+
if (!detachOnly) {
306+
const applyAction = applyClientMethod({methodName: 'deleteSession'});
307+
await applyAction(dispatch, getState);
308+
}
307309
dispatch({type: QUIT_SESSION_DONE});
308-
if (!killedByUser) {
310+
if (!manualQuit) {
309311
showError(new Error(reason || i18n.t('Session has been terminated')), {secs: 0});
310312
}
311313
};

app/common/renderer/components/SessionInspector/Header/HeaderButtons.jsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
IconMessageChatbot,
88
IconPlayerPause,
99
IconPlayerPlay,
10+
IconPlugConnectedX,
1011
IconRecycle,
1112
IconRefresh,
1213
IconSearch,
@@ -38,7 +39,7 @@ const HeaderButtons = (props) => {
3839
showLocatorTestModal,
3940
showSiriCommandModal,
4041
applyClientMethod,
41-
quitCurrentSession,
42+
quitSessionAndReturn,
4243
driver,
4344
contexts,
4445
currentContext,
@@ -231,10 +232,19 @@ const HeaderButtons = (props) => {
231232
</Space.Compact>
232233
);
233234

234-
const quitSessionButton = (
235-
<Tooltip title={t('Quit Session')}>
236-
<Button id="btnClose" icon={<IconX size={18} />} onClick={quitCurrentSession} />
237-
</Tooltip>
235+
const quitControls = (
236+
<Space.Compact>
237+
<Tooltip title={t('detachFromSession')}>
238+
<Button
239+
id="btnDetach"
240+
icon={<IconPlugConnectedX size={18} />}
241+
onClick={() => quitSessionAndReturn({detachOnly: true})}
242+
/>
243+
</Tooltip>
244+
<Tooltip title={t('Quit Session')}>
245+
<Button id="btnClose" icon={<IconX size={18} />} onClick={quitSessionAndReturn} />
246+
</Tooltip>
247+
</Space.Compact>
238248
);
239249

240250
const sessionReloadButton = (
@@ -255,7 +265,7 @@ const HeaderButtons = (props) => {
255265
{appModeControls}
256266
{generalControls}
257267
{sessionReloadButton}
258-
{quitSessionButton}
268+
{quitControls}
259269
</Space>
260270
<Divider />
261271
</div>

app/common/renderer/components/SessionInspector/SessionInspector.jsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,9 @@ const Inspector = (props) => {
188188
applyClientMethod({methodName: 'getPageSource'});
189189
};
190190

191-
const quitCurrentSession = useCallback(
192-
async (reason, killedByUser = true) => {
193-
await quitSession(reason, killedByUser);
191+
const quitSessionAndReturn = useCallback(
192+
async ({reason, manualQuit = true, detachOnly = false} = {}) => {
193+
await quitSession({reason, manualQuit, detachOnly});
194194
navigate('/session', {replace: true});
195195
},
196196
[navigate, quitSession],
@@ -245,7 +245,7 @@ const Inspector = (props) => {
245245
// Create timeout only once while prompt is visible
246246
if (!sessionExpiryTimeoutRef.current) {
247247
sessionExpiryTimeoutRef.current = setTimeout(() => {
248-
quitCurrentSession(t('Session closed due to inactivity'), false);
248+
quitSessionAndReturn({reason: t('Session closed due to inactivity'), manualQuit: false});
249249
}, SESSION_EXPIRY_PROMPT_TIMEOUT);
250250
setUserWaitTimeout(sessionExpiryTimeoutRef.current);
251251
}
@@ -255,7 +255,7 @@ const Inspector = (props) => {
255255
sessionExpiryTimeoutRef.current = null;
256256
setUserWaitTimeout(null);
257257
}
258-
}, [quitCurrentSession, setUserWaitTimeout, showKeepAlivePrompt, t]);
258+
}, [quitSessionAndReturn, setUserWaitTimeout, showKeepAlivePrompt, t]);
259259

260260
const screenShotControls = (
261261
<div className={styles.screenshotControls}>
@@ -379,13 +379,13 @@ const Inspector = (props) => {
379379

380380
return (
381381
<div className={styles.inspectorContainer}>
382-
<HeaderButtons quitCurrentSession={quitCurrentSession} {...props} />
382+
<HeaderButtons quitSessionAndReturn={quitSessionAndReturn} {...props} />
383383
{main}
384384
<Modal
385385
title={t('Session Inactive')}
386386
open={showKeepAlivePrompt}
387387
onOk={() => keepSessionAlive()}
388-
onCancel={() => quitCurrentSession()}
388+
onCancel={() => quitSessionAndReturn()}
389389
okText={t('Keep Session Running')}
390390
cancelText={t('Quit Session')}
391391
>
434 Bytes
Loading
1.24 KB
Loading
5.5 KB
Loading
-149 Bytes
Loading

docs/session-inspector/header.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,18 @@ session (selecting an element, interacting with the screenshot, searching for el
129129
that returns a failed response. Since such a response is not guaranteed to be caused by a
130130
disconnected device, this functionality is disabled by default.
131131

132+
## Detach from Session
133+
134+
![Detach Button](./assets/images/header/detach-button.png)
135+
136+
This button disconnects the Inspector from the active session and returns to the
137+
[Session Builder](../session-builder/index.md), but the session itself ^^is not deleted^^, and
138+
^^remains running^^ on the Appium server. This approach may be useful if you are attaching to an
139+
existing session mid-test and want to resume the test afterwards, or if you want to reuse the
140+
session later.
141+
132142
## Quit Session
133143

134144
![Quit Button](./assets/images/header/quit-button.png)
135145

136-
This button quits the Inspector session and returns to the [Session Builder](../session-builder/index.md).
146+
This button deletes the active session and returns to the Session Builder.

0 commit comments

Comments
 (0)