Skip to content

Commit cc2f010

Browse files
committed
fix(relay): keep Live Activity alerts when push notifications are off
1 parent bb72101 commit cc2f010

2 files changed

Lines changed: 59 additions & 4 deletions

File tree

‎infra/relay/src/agentActivity/ApnsDeliveries.test.ts‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,15 @@ const notificationsDisabledPreferences = JSON.stringify({
140140
notifyOnFailure: true,
141141
});
142142

143+
const liveActivityWithoutPushPreferences = JSON.stringify({
144+
liveActivitiesEnabled: true,
145+
notificationsEnabled: false,
146+
notifyOnApproval: true,
147+
notifyOnInput: true,
148+
notifyOnCompletion: true,
149+
notifyOnFailure: true,
150+
});
151+
143152
const target: LiveActivities.TargetRow = {
144153
user_id: "dev:julius",
145154
device_id: "device-1",
@@ -523,6 +532,53 @@ describe("ApnsDeliveries", () => {
523532
},
524533
);
525534

535+
it.effect("keeps the Live Activity alert when push notifications are preferred off", () => {
536+
const attempts: Array<DeliveryAttempts.DeliveryAttemptInput> = [];
537+
const queuedJobs: Array<SignedApnsDeliveryJob> = [];
538+
const previousAggregate: RelayAgentActivityAggregateState = {
539+
...aggregate,
540+
activities: [
541+
{
542+
...aggregate.activities[0]!,
543+
phase: "running",
544+
status: "Working",
545+
},
546+
],
547+
};
548+
const inputAggregate: RelayAgentActivityAggregateState = {
549+
...aggregate,
550+
activities: [
551+
{
552+
...aggregate.activities[0]!,
553+
phase: "waiting_for_approval",
554+
status: "Approval",
555+
},
556+
],
557+
};
558+
559+
return Effect.gen(function* () {
560+
const deliveries = yield* ApnsDeliveries.ApnsDeliveries;
561+
const result = yield* deliveries.sendForTarget({
562+
target: {
563+
...target,
564+
push_token: "apns-device-token",
565+
preferences_json: liveActivityWithoutPushPreferences,
566+
last_aggregate_json: JSON.stringify(previousAggregate),
567+
},
568+
aggregate: inputAggregate,
569+
nowMs: 5_000,
570+
});
571+
572+
expect(result?.kind).toBe("live_activity_update");
573+
expect(queuedJobs).toHaveLength(1);
574+
expect(queuedJobs[0]?.payload).toMatchObject({
575+
kind: "live_activity_update",
576+
alert: { title: "Needs your reply" },
577+
});
578+
expect(attempts).toEqual([]);
579+
}).pipe(Effect.provide(layerFor({ attempts, queuedJobs })));
580+
});
581+
526582
it.effect(
527583
"queues a companion push with Live Activity updates so stale activity tokens cannot swallow alerts",
528584
() => {

‎infra/relay/src/agentActivity/ApnsDeliveries.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,12 +1156,11 @@ const make = Effect.gen(function* () {
11561156
? endAlertRow
11571157
: null
11581158
: delivery.alertRow;
1159-
// Prefer a regular push when we have a device token — Live Activity alert
1160-
// payloads alone stay silent if the activity token is stale or the card was
1161-
// dismissed. Content updates only companion on a new transition; ends may
1162-
// still use notificationForAggregate (e.g. Live Activities turned off).
1159+
// Prefer a companion push when allowed; otherwise keep the Live Activity alert.
1160+
// Skip companions when notifications are off so a leftover token cannot mute both.
11631161
const transitionCompanion =
11641162
input.target.push_token === null ||
1163+
preferences?.notificationsEnabled !== true ||
11651164
input.replay ||
11661165
suppressAlerts ||
11671166
liveActivityAlert === null ||

0 commit comments

Comments
 (0)