mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 08:03:20 +00:00
fix(mobile): reconcile dismissed pushes on first connection
This commit is contained in:
@@ -246,11 +246,13 @@ export function subscribeToDesktopNotifications(client: RpcClient, hostId: strin
|
||||
// left over from a previous desktop lifetime, so the catch-up request carries
|
||||
// a watermark that means something against the counter now answering it.
|
||||
adoptNotificationEpoch(session, hostId, readyEpoch)
|
||||
// A reconnect always catches up. A cold open catches up only when this device
|
||||
// has delivered for this host before — a first-ever pairing must not be handed
|
||||
// the desktop's whole retained buffer.
|
||||
// First pairing recovers tray dismissals without replaying historical alerts.
|
||||
if (isReconnect || session.hadStoredWatermark) {
|
||||
await fetchMissed()
|
||||
} else {
|
||||
await requestNotificationCatchup(client, hostId, undefined, () => disposed).catch(
|
||||
() => {}
|
||||
)
|
||||
}
|
||||
})()
|
||||
return
|
||||
|
||||
@@ -148,6 +148,39 @@ beforeEach(() => {
|
||||
})
|
||||
|
||||
describe('reopen after a push the OS showed while Orca was closed', () => {
|
||||
it('recovers a dismissed push without a stored watermark or replaying historical alerts', async () => {
|
||||
const identity = { notificationId: 'a:6', notificationSeq: 6, notificationEpoch: 'epoch-1' }
|
||||
presentTray([{ hostFingerprint, ...identity }])
|
||||
const { client, ready } = catchUpClient()
|
||||
vi.mocked(client.sendRequest).mockResolvedValue({
|
||||
ok: true,
|
||||
result: {
|
||||
dismissedPushes: [identity],
|
||||
notifications: [{ type: 'notification', title: 'Historical alert', notificationSeq: 5 }]
|
||||
}
|
||||
} as never)
|
||||
const dispose = subscribeToDesktopNotifications(client, 'host-1')
|
||||
ready()
|
||||
await flushAsync()
|
||||
expect(client.sendRequest).toHaveBeenCalledWith('notifications.getMissedSince', {
|
||||
lastSeenSeq: Number.MAX_SAFE_INTEGER,
|
||||
deliveredPushes: [identity]
|
||||
})
|
||||
expect(Notifications.dismissNotificationAsync).toHaveBeenCalledExactlyOnceWith('tray-0')
|
||||
expect(shownTitles()).toEqual([])
|
||||
expect(persistedSeq()).toBe(0)
|
||||
dispose()
|
||||
})
|
||||
|
||||
it('does not request history on a first pairing with an empty tray', async () => {
|
||||
const { client, ready } = catchUpClient()
|
||||
const dispose = subscribeToDesktopNotifications(client, 'host-1')
|
||||
ready()
|
||||
await flushAsync()
|
||||
expect(client.sendRequest).not.toHaveBeenCalled()
|
||||
dispose()
|
||||
})
|
||||
|
||||
it('replays only the events still missing from the tray', async () => {
|
||||
presentTray([
|
||||
{ hostFingerprint, notificationId: 'a:6', notificationSeq: 6, notificationEpoch: 'epoch-1' }
|
||||
|
||||
@@ -61,12 +61,16 @@ async function readDelivered(hostId: string): Promise<Map<string, OrcaPushPayloa
|
||||
export async function requestNotificationCatchup(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
hostId: string,
|
||||
params: { lastSeenSeq: number; epoch?: string; includeDesktopSuppressed?: boolean },
|
||||
params: { lastSeenSeq: number; epoch?: string; includeDesktopSuppressed?: boolean } | undefined,
|
||||
isDisposed: () => boolean
|
||||
) {
|
||||
const delivered = await readDelivered(hostId)
|
||||
if (!params && (delivered.size === 0 || isDisposed())) {
|
||||
return { ok: true, result: { notifications: [] } }
|
||||
}
|
||||
const response = await client.sendRequest('notifications.getMissedSince', {
|
||||
...params,
|
||||
// First pairing reconciles the tray without requesting historical alerts.
|
||||
...(params ?? { lastSeenSeq: Number.MAX_SAFE_INTEGER }),
|
||||
...(delivered.size
|
||||
? { deliveredPushes: [...delivered.values()].map((payload) => identity(payload)!) }
|
||||
: {})
|
||||
|
||||
Reference in New Issue
Block a user