mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 09:27:29 +00:00
fix(mobile): tell the user when closing a session tab fails (#41404)
The close actions swallowed refusals and transport errors, so a tap on close did nothing when the host refused (for example while the desktop window is still loading). Show a short toast instead, with a specific message for runtime_unavailable.
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "session.tab-close",
|
||||
"family": "session.tab-close",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"3e15def58214": {
|
||||
"activeHandle": "terminal-1",
|
||||
"sessionTabs": [
|
||||
{
|
||||
"id": "tab-1",
|
||||
"isActive": true,
|
||||
"terminal": "terminal-1",
|
||||
"title": "Terminal",
|
||||
"type": "terminal"
|
||||
}
|
||||
],
|
||||
"terminals": [
|
||||
{
|
||||
"handle": "terminal-1",
|
||||
"isActive": true,
|
||||
"title": "Terminal"
|
||||
}
|
||||
]
|
||||
},
|
||||
"50a46af5cf5c": {
|
||||
"name": "session.tabs.close#1",
|
||||
"ordinal": 2,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"session.tabs.close\",\"params\":{\"worktree\":\"id:workspace-1\",\"tabId\":\"tab-1\",\"reason\":\"user\"}}"
|
||||
},
|
||||
"b8970b98f1e3": {
|
||||
"name": "toast",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"message": "Couldn't close the tab. Orca on your computer is still loading. Try again in a moment."
|
||||
}
|
||||
},
|
||||
"d1730750ac1c": {
|
||||
"name": "session.tabs.close#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "session.tabs.close"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"reason": "user",
|
||||
"tabId": "tab-1",
|
||||
"worktree": "id:workspace-1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "runtime_unavailable",
|
||||
"message": "runtime_unavailable"
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "session-tab-close-host-unavailable-tells-user",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "kept",
|
||||
"observation": {
|
||||
"sender": ["d1730750ac1c"],
|
||||
"payloads": ["50a46af5cf5c"],
|
||||
"settlements": {
|
||||
"close-tab": "eb79a9b3682a"
|
||||
},
|
||||
"state": "3e15def58214",
|
||||
"effects": ["b8970b98f1e3"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,13 @@
|
||||
"family": "session.tab-close",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"26d1891758e0": {
|
||||
"name": "toast",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"message": "Couldn't close the tab. Try again."
|
||||
}
|
||||
},
|
||||
"3e15def58214": {
|
||||
"activeHandle": "terminal-1",
|
||||
"sessionTabs": [
|
||||
@@ -84,7 +91,7 @@
|
||||
"close-terminal": "eb79a9b3682a"
|
||||
},
|
||||
"state": "3e15def58214",
|
||||
"effects": []
|
||||
"effects": ["26d1891758e0"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -19386,6 +19386,37 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "session-tab-close-host-unavailable-tells-user",
|
||||
"operation": "session.tab-close",
|
||||
"family": "session.tab-close",
|
||||
"sites": ["mobile/src/session/tabs/use-mobile-session-close-actions.ts"],
|
||||
"schedules": [],
|
||||
"steps": [
|
||||
{
|
||||
"action": "close-tab",
|
||||
"id": "close-tab"
|
||||
},
|
||||
{
|
||||
"complete": "session.tabs.close#1",
|
||||
"params": {
|
||||
"reason": "user",
|
||||
"tabId": "tab-1",
|
||||
"worktree": "id:workspace-1"
|
||||
},
|
||||
"reply": {
|
||||
"ok": false,
|
||||
"error": {
|
||||
"code": "runtime_unavailable",
|
||||
"message": "runtime_unavailable"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"checkpoint": "kept"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "review-mark-reviewed-persists",
|
||||
"operation": "session.diff-review-actions",
|
||||
|
||||
@@ -2498,6 +2498,10 @@
|
||||
"useAttachmentInputLeaseGate": {
|
||||
"reconnecting": "Anhängen fehlgeschlagen (erneuter Verbindungsaufbau)"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "Der Tab konnte nicht geschlossen werden. Versuche es erneut.",
|
||||
"closeFailedDesktopNotReady": "Der Tab konnte nicht geschlossen werden. Orca wird auf deinem Computer noch geladen. Versuche es gleich noch einmal."
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"browserCommandFailed": "Browser-Befehl fehlgeschlagen",
|
||||
"browserCreateFailed": "Browser konnte nicht erstellt werden",
|
||||
|
||||
@@ -2545,6 +2545,10 @@
|
||||
"copyFailed": "Couldn't copy",
|
||||
"selectionCleared": "Selection cleared (scrolled out of buffer)"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "Couldn't close the tab. Try again.",
|
||||
"closeFailedDesktopNotReady": "Couldn't close the tab. Orca on your computer is still loading. Try again in a moment."
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"noteCreateFailed": "Failed to create markdown note",
|
||||
"untitledNoteCreateFailed": "Unable to create untitled markdown note",
|
||||
|
||||
@@ -2568,6 +2568,10 @@
|
||||
"useAttachmentInputLeaseGate": {
|
||||
"reconnecting": "No se pudo adjuntar (reconectando)"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "No se pudo cerrar la pestaña. Inténtalo de nuevo.",
|
||||
"closeFailedDesktopNotReady": "No se pudo cerrar la pestaña. Orca todavía se está cargando en tu ordenador. Vuelve a intentarlo en un momento."
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"browserCommandFailed": "No se pudo ejecutar el comando del navegador",
|
||||
"browserCreateFailed": "No se pudo crear el navegador",
|
||||
|
||||
@@ -2568,6 +2568,10 @@
|
||||
"useAttachmentInputLeaseGate": {
|
||||
"reconnecting": "Échec de l'ajout (reconnexion en cours)"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "Impossible de fermer l’onglet. Réessayez.",
|
||||
"closeFailedDesktopNotReady": "Impossible de fermer l’onglet. Orca est encore en cours de chargement sur votre ordinateur. Réessayez dans un instant."
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"browserCommandFailed": "Échec de la commande du navigateur",
|
||||
"browserCreateFailed": "Échec de la création du navigateur",
|
||||
|
||||
@@ -2494,6 +2494,10 @@
|
||||
"useAttachmentInputLeaseGate": {
|
||||
"reconnecting": "添付に失敗しました(再接続中)。"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "タブを閉じられませんでした。もう一度お試しください。",
|
||||
"closeFailedDesktopNotReady": "タブを閉じられませんでした。コンピューターの Orca はまだ読み込み中です。少ししてからもう一度お試しください。"
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"browserCommandFailed": "ブラウザコマンドの実行に失敗しました。",
|
||||
"browserCreateFailed": "ブラウザの作成に失敗しました。",
|
||||
|
||||
@@ -2494,6 +2494,10 @@
|
||||
"useAttachmentInputLeaseGate": {
|
||||
"reconnecting": "첨부 실패 (다시 연결 중)"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "탭을 닫지 못했습니다. 다시 시도하세요.",
|
||||
"closeFailedDesktopNotReady": "탭을 닫지 못했습니다. 컴퓨터에서 Orca를 아직 불러오는 중입니다. 잠시 후 다시 시도하세요."
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"browserCommandFailed": "브라우저 명령 실행에 실패했습니다.",
|
||||
"browserCreateFailed": "브라우저를 만들지 못했습니다.",
|
||||
|
||||
@@ -2941,6 +2941,10 @@
|
||||
"useAttachmentInputLeaseGate": {
|
||||
"reconnecting": "Falha ao anexar (reconectando)"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "Não foi possível fechar a aba. Tente novamente.",
|
||||
"closeFailedDesktopNotReady": "Não foi possível fechar a aba. O Orca ainda está carregando no seu computador. Tente novamente em instantes."
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"browserCommandFailed": "Falha no comando do navegador",
|
||||
"browserCreateFailed": "Falha ao criar o navegador",
|
||||
|
||||
@@ -2638,6 +2638,10 @@
|
||||
"useAttachmentInputLeaseGate": {
|
||||
"reconnecting": "Не удалось прикрепить (повторное подключение)"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "Не удалось закрыть вкладку. Попробуйте ещё раз.",
|
||||
"closeFailedDesktopNotReady": "Не удалось закрыть вкладку. Orca на компьютере ещё загружается. Попробуйте ещё раз через минуту."
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"browserCommandFailed": "Не удалось выполнить команду браузера",
|
||||
"browserCreateFailed": "Не удалось создать вкладку браузера",
|
||||
|
||||
@@ -2498,6 +2498,10 @@
|
||||
"useAttachmentInputLeaseGate": {
|
||||
"reconnecting": "Eklenemedi (yeniden bağlanılıyor)"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "Sekme kapatılamadı. Tekrar deneyin.",
|
||||
"closeFailedDesktopNotReady": "Sekme kapatılamadı. Orca bilgisayarınızda hâlâ yükleniyor. Birazdan tekrar deneyin."
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"browserCommandFailed": "Tarayıcı komutu çalıştırılamadı",
|
||||
"browserCreateFailed": "Tarayıcı oluşturulamadı",
|
||||
|
||||
@@ -2494,6 +2494,10 @@
|
||||
"useAttachmentInputLeaseGate": {
|
||||
"reconnecting": "加入附件失敗(重新連線中)"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "無法關閉分頁。請再試一次。",
|
||||
"closeFailedDesktopNotReady": "無法關閉分頁。電腦上的 Orca 仍在載入中,請稍後再試。"
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"browserCommandFailed": "瀏覽器命令執行失敗",
|
||||
"browserCreateFailed": "建立瀏覽器失敗",
|
||||
|
||||
@@ -2500,6 +2500,10 @@
|
||||
"useAttachmentInputLeaseGate": {
|
||||
"reconnecting": "添加附件失败(重新连接中)"
|
||||
},
|
||||
"useCloseActions": {
|
||||
"closeFailed": "无法关闭标签页。请重试。",
|
||||
"closeFailedDesktopNotReady": "无法关闭标签页。电脑上的 Orca 仍在加载,请稍后重试。"
|
||||
},
|
||||
"useContentCreateActions": {
|
||||
"browserCommandFailed": "浏览器命令执行失败",
|
||||
"browserCreateFailed": "创建浏览器失败",
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
type HostMutationPhase
|
||||
} from '../../query/host-mutation'
|
||||
import type { RpcClient } from '../../transport/rpc/rpc-client-contract'
|
||||
import { translate } from '../../i18n/mobile-i18n'
|
||||
|
||||
async function sendTerminalRename(
|
||||
client: RpcClient,
|
||||
@@ -64,9 +65,22 @@ export function useMobileSessionCloseActions(scope: MobileSessionContentCreateAc
|
||||
scheduleDelayedAction,
|
||||
unsubscribeTerminal,
|
||||
subscribeToTerminal,
|
||||
fetchTerminals
|
||||
fetchTerminals,
|
||||
showToast
|
||||
} = scope
|
||||
const terminalRenameWrite = useHostMutation(terminalRename, { hostId, client })
|
||||
// A refused or lost close keeps the tab, so say so rather than leave the tap unanswered.
|
||||
function showCloseFailed(code?: string) {
|
||||
showToast(
|
||||
code === 'runtime_unavailable'
|
||||
? translate(
|
||||
'session.useCloseActions.closeFailedDesktopNotReady',
|
||||
"Couldn't close the tab. Orca on your computer is still loading. Try again in a moment."
|
||||
)
|
||||
: translate('session.useCloseActions.closeFailed', "Couldn't close the tab. Try again."),
|
||||
2400
|
||||
)
|
||||
}
|
||||
// Throws on refusal so the sheet keeps the typed name and offers Retry.
|
||||
async function handleRenameTerminal(
|
||||
value: string,
|
||||
@@ -96,10 +110,11 @@ export function useMobileSessionCloseActions(scope: MobileSessionContentCreateAc
|
||||
}
|
||||
|
||||
try {
|
||||
const response = sessionTerminalClose.interpret(
|
||||
await sessionTerminalClose.request(client, { terminal: target.handle })
|
||||
)
|
||||
if (response.accepted) {
|
||||
const reply = await sessionTerminalClose.request(client, { terminal: target.handle })
|
||||
const response = sessionTerminalClose.interpret(reply)
|
||||
if (!response.accepted) {
|
||||
showCloseFailed(reply.ok ? undefined : reply.error.code)
|
||||
} else {
|
||||
unsubscribeTerminal(target.handle)
|
||||
terminalRefs.current.delete(target.handle)
|
||||
const next = terminalsRef.current.filter((terminal) => terminal.handle !== target.handle)
|
||||
@@ -121,7 +136,9 @@ export function useMobileSessionCloseActions(scope: MobileSessionContentCreateAc
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
showCloseFailed()
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCloseSessionTab(tab: MobileSessionTab) {
|
||||
@@ -129,16 +146,17 @@ export function useMobileSessionCloseActions(scope: MobileSessionContentCreateAc
|
||||
return
|
||||
}
|
||||
try {
|
||||
const response = sessionTabClose.interpret(
|
||||
await sessionTabClose.request(client, {
|
||||
worktree: `id:${worktreeId}`,
|
||||
tabId: tab.id,
|
||||
// Why: a tapped tab close is explicit user intent; older hosts strip
|
||||
// the unknown field and keep their legacy behavior.
|
||||
reason: 'user'
|
||||
})
|
||||
)
|
||||
if (response.accepted && !response.value.refused) {
|
||||
const reply = await sessionTabClose.request(client, {
|
||||
worktree: `id:${worktreeId}`,
|
||||
tabId: tab.id,
|
||||
// Why: a tapped tab close is explicit user intent; older hosts strip
|
||||
// the unknown field and keep their legacy behavior.
|
||||
reason: 'user'
|
||||
})
|
||||
const response = sessionTabClose.interpret(reply)
|
||||
if (!response.accepted || response.value.refused) {
|
||||
showCloseFailed(reply.ok ? undefined : reply.error.code)
|
||||
} else {
|
||||
const chatScope = mobileNativeChatScopeKey(hostId, worktreeId, tab.id)
|
||||
if (chatScope && response.value.retirementConfirmed) {
|
||||
closeMobileNativeChatTabDraft(chatScope)
|
||||
@@ -195,7 +213,9 @@ export function useMobileSessionCloseActions(scope: MobileSessionContentCreateAc
|
||||
setActiveHandle(null)
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
showCloseFailed()
|
||||
}
|
||||
}
|
||||
return {
|
||||
handleRenameTerminal,
|
||||
|
||||
@@ -134,6 +134,7 @@ export function sessionScreenTabMountAdapters(
|
||||
pendingSelectionRef: { current: null },
|
||||
pendingBrowserFocusPageIdRef: { current: null },
|
||||
unsubscribeTerminal: (handle: string) => effect('unsubscribe-terminal', { handle }),
|
||||
showToast: (message: string) => effect('toast', { message }),
|
||||
subscribeToTerminal: (handle: string) => {
|
||||
effect('subscribe-terminal', { handle })
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user