Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions appleApp/FTWKit/Sources/FTWShared/PushAlert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,27 @@ public enum PushKind {
public static let boxUnreachable = "box.unreachable"
public static let cloudGranted = "cloud.granted"
public static let cloudRevoked = "cloud.revoked"
/// Access from outside the house, for every owner (docs/agents.md in
/// srcfl/ftw). They come as the box's `notice`, never as an event.
public static let agentAdded = "agent.added"
public static let agentRequest = "agent.request"
public static let agentExpiring = "agent.expiring"
public static let approvalKeySet = "approval.key.set"
public static let ownerAdded = "owner.added"
/// The box asks which car is on a charger. The buttons, one per car,
/// come with the car app (docs/cars.md in srcfl/ftw).
public static let carWhich = "car.which"

/// In the catalogue's order.
public static let all = [
chargingConnected, chargingSessionComplete, chargingGoalAtRisk, chargingInterrupted, updateInstalled,
driverOffline, fuseOverLimit, boxUnreachable, cloudGranted, cloudRevoked, carWhich,
driverOffline, fuseOverLimit, boxUnreachable, cloudGranted, cloudRevoked,
agentAdded, agentRequest, agentExpiring, approvalKeySet, ownerAdded, carWhich,
]

/// The kinds only owners may read. A box sends them as a `notice`; an
/// `event` that names one reaches viewers too, so it reads as generic.
public static let notices: Set<String> = [agentAdded, agentRequest, agentExpiring, approvalKeySet, ownerAdded]
}

/// What a push says once checked. Only `read` makes one, so a kind that has
Expand Down Expand Up @@ -134,7 +146,8 @@ public struct PushAlert: Sendable, Equatable {
}
if let ts = ev.ts { at = min(at, ts) }
let age = nowMs.subtractingReportingOverflow(at)
guard PushKind.all.contains(ev.kind), ev.kind != PushKind.boxUnreachable, !age.overflow, age.partialValue <= maxAgeMs else {
guard PushKind.all.contains(ev.kind), ev.kind != PushKind.boxUnreachable, !PushKind.notices.contains(ev.kind),
!age.overflow, age.partialValue <= maxAgeMs else {
return generic
}
return PushAlert(.event, home: home, kind: ev.kind, args: ev.args, src: ev.src, lp: ev.lp, boot: ev.boot.map { Data($0) }, seq: ev.seq, atMs: at)
Expand Down
10 changes: 10 additions & 0 deletions appleApp/FTWKit/Sources/FTWShared/PushText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public enum PushText {
PushKind.boxUnreachable: Sentence(title: "Your box is out of reach", body: "It stopped answering. Power cut, or the internet at home is down."),
PushKind.cloudGranted: Sentence(title: "FTW Cloud is on", body: "Your box now shares a copy of its readings with FTW Cloud. You can turn it off in the app or on the box."),
PushKind.cloudRevoked: Sentence(title: "FTW Cloud is off", body: "Your box stopped sharing with FTW Cloud and asked it to delete its copy."),
PushKind.agentAdded: Sentence(title: "New access to your box", body: "You gave an assistant or Sourceful access. See who has access in the app."),
PushKind.agentRequest: Sentence(title: "Waiting for your answer", body: "An assistant asks to change a setting. Open FTW to confirm or decline."),
PushKind.agentExpiring: Sentence(title: "Access ends tomorrow", body: "An assistant's or Sourceful's access to your box runs out in a day. Renew it in the app if it should go on."),
PushKind.approvalKeySet: Sentence(title: "A phone can now approve access", body: "A phone set up the key it uses to approve access to your box. If that was not you, remove the phone in the app."),
PushKind.ownerAdded: Sentence(title: "A new owner", body: "A phone was paired as an owner of your box. If that was not you, remove it in the app."),
PushKind.carWhich: Sentence(title: "Which car is plugged in?", body: "Tap the car so the box plans for its level and goal."),
]

Expand All @@ -90,6 +95,11 @@ public enum PushText {
PushKind.boxUnreachable: Sentence(title: "Din box går inte att nå", body: "Den har slutat svara. Strömavbrott, eller så är internet hemma nere."),
PushKind.cloudGranted: Sentence(title: "FTW Cloud är på", body: "Din box delar nu en kopia av sina mätvärden med FTW Cloud. Du kan stänga av det i appen eller på boxen."),
PushKind.cloudRevoked: Sentence(title: "FTW Cloud är av", body: "Din box har slutat dela med FTW Cloud och bett den radera sin kopia."),
PushKind.agentAdded: Sentence(title: "Ny åtkomst till din box", body: "En assistent eller Sourceful har fått åtkomst. Se vem som har åtkomst i appen."),
PushKind.agentRequest: Sentence(title: "Väntar på ditt svar", body: "En assistent vill ändra en inställning. Öppna FTW för att godkänna eller avböja."),
PushKind.agentExpiring: Sentence(title: "Åtkomsten upphör i morgon", body: "En assistents eller Sourcefuls åtkomst till din box går ut om ett dygn. Förnya den i appen om den ska fortsätta."),
PushKind.approvalKeySet: Sentence(title: "En telefon kan nu godkänna åtkomst", body: "En telefon har satt upp nyckeln den använder för att godkänna åtkomst till din box. Var det inte du, ta bort telefonen i appen."),
PushKind.ownerAdded: Sentence(title: "En ny ägare", body: "En telefon har parats som ägare av din box. Var det inte du, ta bort den i appen."),
PushKind.carWhich: Sentence(title: "Vilken bil är inkopplad?", body: "Tryck på bilen så planerar boxen efter dess nivå och mål."),
]

Expand Down
8 changes: 8 additions & 0 deletions appleApp/FTWKit/Tests/FTWSharedTests/PushAlertTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,14 @@ import Testing
#expect(a.src == nil)
}

/// Access news is for owners only. A box sends it as a `notice`; as an
/// `event` it also reaches viewers, so it reads as generic.
@Test func accessNewsAsAnEventIsGeneric() {
for kind in PushKind.notices {
#expect(Self.read(Self.envelope(Self.body(kind: kind))).verdict == .generic, "\(kind)")
}
}

/// Unknown keys are a newer box, not a fault.
@Test func unknownKeysAreIgnored() {
#expect(Self.read(Self.envelope(Self.body([("later", .m([("x", .u(1))]))]))).verdict == .event)
Expand Down
26 changes: 26 additions & 0 deletions protocol/push-catalogue.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,32 @@ events:
# To every owner, when FTW Cloud is turned off at either door.
title: FTW Cloud is off
body: "Your box stopped sharing with FTW Cloud and asked it to delete its copy."
- kind: agent.added
# To every owner, when an owner gives an assistant, Sourceful support or
# the release check access to the box. Access from outside the house is
# news every owner should hear, as with cloud.granted.
title: New access to your box
body: "You gave an assistant or Sourceful access. See who has access in the app."
- kind: agent.request
# To every owner, when an agent asks for a durable change. It waits for
# one of them to confirm it, and runs out after twelve hours.
title: Waiting for your answer
body: "An assistant asks to change a setting. Open FTW to confirm or decline."
- kind: agent.expiring
# To every owner, a day before an agent's grant runs out, so access that
# should go on is renewed and access that should end can be left to end.
title: Access ends tomorrow
body: "An assistant's or Sourceful's access to your box runs out in a day. Renew it in the app if it should go on."
- kind: approval.key.set
# To every owner, when a phone registers or replaces the key that
# approves access. A new key is a new way to let others in.
title: A phone can now approve access
body: "A phone set up the key it uses to approve access to your box. If that was not you, remove the phone in the app."
- kind: owner.added
# To every owner, when a phone pairs as an owner. An owner can do
# anything, including give others access.
title: A new owner
body: "A phone was paired as an owner of your box. If that was not you, remove it in the app."
- kind: car.which
# When a car is plugged in and the box cannot tell which of its owners'
# cars it is: no charger identity, no word from the car within a minute,
Expand Down
Loading