mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-11 02:46:29 +00:00
fix(app): open notification agents once in their current workspace (#7612)
* fix(app): open notification agents once in their current workspace Keep tap ownership across Activity and React remounts, clear native responses, and resolve agent workspaces through existing navigation after startup settles. Co-authored-by: Kassam Khoja <kassam@shopwrk.com> * fix(app): hydrate agent targets with the compact sidebar closed Hold directory demand for the agent route while resolving its workspace; the compact sidebar does not supply demand before it opens. * fix(app): keep deferred workspace opens in the layout model Key pending tab opens by workspace so redirects and retained screens cannot lose or consume another workspace's target. Legacy open URLs seed this state at their route boundary. * docs(app): document notification tap ownership and native replay * fix(ci): route feature-owned Electron sources to desktop checks * refactor(app): separate notification API from React integration * fix(app): consume pending opens before painting the restored tab A legacy open link showed the previously focused terminal for a frame before switching to the requested terminal. Seed and consume the pending open in layout effects so a ready target switches before paint. * fix(e2e): focus the visible retained terminal surface --------- Co-authored-by: Kassam Khoja <kassam@shopwrk.com>
This commit is contained in:
co-authored by
Kassam Khoja
parent
4574142314
commit
12e7a448f0
+16
-9
@@ -104,17 +104,24 @@ Do not collapse this topology with a catch-all, `getId`, or
|
||||
|
||||
## Agent Targets
|
||||
|
||||
Notifications and agent URLs enter the router with different authoritative
|
||||
targets.
|
||||
Notifications and agent URLs name a host and agent. Both use
|
||||
`navigateToAgent()`, which reads the agent's current workspace from the store
|
||||
or routes through `/h/[serverId]/agent/[agentId]`. The agent route waits for
|
||||
the named host, demands its directory, and waits for workspace and layout
|
||||
hydration before opening the tab. Keep the stable agent URL free of a workspace
|
||||
hint. [Notifications](notifications.md) owns tap delivery and native lifecycle
|
||||
constraints.
|
||||
|
||||
- Notifications carry `serverId`, `workspaceId`, and `agentId`. Route them
|
||||
directly to the workspace with the agent open intent.
|
||||
- Agent URLs carry only `serverId` and `agentId`. Route them through
|
||||
`/h/[serverId]/agent/[agentId]`; that route waits for the named host, resolves
|
||||
the agent's workspace from the host, and then opens the agent there.
|
||||
When a workspace target cannot open yet, `navigateToWorkspace()` records a
|
||||
pending open in the layout model under that host and workspace key. The owning
|
||||
workspace screen consumes it once after hydration. Archived-workspace recovery
|
||||
reads the same pending target, so startup redirects cannot discard it and a
|
||||
retained screen cannot consume another workspace's request.
|
||||
|
||||
Both paths converge on `navigateToAgent()`. Do not make notification routing
|
||||
guess a workspace, and do not add a workspace to the stable agent URL format.
|
||||
Legacy `?open=` links seed this model state at the matched route boundary and
|
||||
then lose the query parameter. Internal navigation does not use the query as a
|
||||
handoff. Read matched route params locally; global search params let retained
|
||||
workspace screens observe requests for other workspaces.
|
||||
|
||||
## Params
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# Notifications
|
||||
|
||||
Use `notification-open` for OS notification taps. It owns delivery, parsing,
|
||||
process-local deduplication, startup readiness, and native acknowledgement.
|
||||
Keep agent and workspace navigation in their existing navigation modules.
|
||||
Import the pure API from `@/notification-open` and the app-shell component from
|
||||
`@/notification-open/react`; senders must not load the React routing runtime.
|
||||
|
||||
Agent payloads name a host and agent. Ignore their workspace hint when opening:
|
||||
the agent can move after a notification is sent. Use `navigateToAgent`, which
|
||||
uses the current store record or the agent route to resolve the workspace from
|
||||
the host. Terminal payloads name their workspace and use `navigateToWorkspace`.
|
||||
|
||||
See [agent targets and deferred opens](expo-router.md#agent-targets) for route
|
||||
ownership and workspace recovery. Notification generation, presence, and push
|
||||
eligibility remain daemon concerns.
|
||||
|
||||
## Native delivery
|
||||
|
||||
Keep the router instance at module scope. Android's `singleTask` Activity can
|
||||
finish on Back while its JS context remains alive. A React ref is lost when the
|
||||
root remounts; process-local handled IDs must survive that remount.
|
||||
|
||||
Clear `lastNotificationResponse` after dispatch. Reading a launch response does
|
||||
not acknowledge it. Without clearing it, a later Activity launch can reopen the
|
||||
old agent.
|
||||
|
||||
With expo-notifications 57, Android also replays its pending response list to
|
||||
new native listeners. Clearing the last response does not drain that list.
|
||||
Deduplication covers listener registration within one JS context. A development
|
||||
JS reload creates another context and can replay a cold-launch response; do not
|
||||
add persisted handled IDs to compensate. Recheck this limitation when upgrading
|
||||
expo-notifications.
|
||||
|
||||
iOS drains pending responses when its delegate first handles them. Push data
|
||||
read by Expo arrives under `userInfo.body`; simulator payloads must match:
|
||||
|
||||
```json
|
||||
{
|
||||
"aps": { "alert": { "title": "Agent finished", "body": "Open the agent" }, "sound": "default" },
|
||||
"body": {
|
||||
"serverId": "host",
|
||||
"workspaceId": "workspace",
|
||||
"agentId": "agent",
|
||||
"reason": "finished"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
An Android FCM launch intent needs `google.message_id` and a JSON `body` extra.
|
||||
An arbitrary Activity launch with data extras does not exercise Expo's FCM path.
|
||||
|
||||
Foreground pushes remain in Notification Center without a banner or sound.
|
||||
This preserves notifications sent with stale presence information.
|
||||
|
||||
## Startup and verification
|
||||
|
||||
Wait for the root navigator, host registry, remembered selection, and startup
|
||||
redirects before handing off a tap. A compact agent route must demand the host's
|
||||
directory itself; its sidebar can be closed for the entire cold launch.
|
||||
|
||||
Verify warm and cold taps, stale and missing workspace hints, two distinct taps,
|
||||
and Android Back followed by launcher reopen or a different notification tap. Check the focused tab and every
|
||||
workspace's saved layout, since a foreign pinned tab survives navigation.
|
||||
Use the daemon's payload shape on both iOS and Android; a browser event alone
|
||||
does not test native replay or acknowledgement.
|
||||
|
||||
Existing foreign pins belong to the reconciliation fix in
|
||||
[#6900](https://github.com/getpaseo/paseo/issues/6900). Desktop main-process window
|
||||
selection is tracked separately in
|
||||
[#6875](https://github.com/getpaseo/paseo/issues/6875).
|
||||
Reference in New Issue
Block a user