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
21 changes: 11 additions & 10 deletions NormalMonitor/NormalMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,17 @@ final class NormalMonitor: DeviceActivityMonitor {
else { return }

let domains = gatedDomains(dto.customDomains)
let allowing = activeSchedules(shouldBlock: false)
if dto.isGroupUnblock {
guard !sharedStore.isMainTimedUnblockActive() else {
sharedStore.removeTimedUnblock(id: dto.id)
return
}
store.unionShields(with: selection)
store.unionFilterDomains(domains)
store.unionShields(with: allowing.applied(to: selection))
store.unionFilterDomains(allowing.applied(to: domains))
} else {
store.replaceShields(with: selection)
store.replaceFilterDomains(domains)
store.replaceShields(with: allowing.applied(to: selection))
store.replaceFilterDomains(allowing.applied(to: domains))
if dto.blockAllPreventsAppDelete == true {
store.application.denyAppRemoval = true
}
Expand Down Expand Up @@ -121,7 +122,7 @@ final class NormalMonitor: DeviceActivityMonitor {
else { return }

let domains = gatedDomains(schedule.customDomains)
let stillActive = otherActiveSchedules(like: schedule)
let stillActive = activeSchedules(shouldBlock: schedule.shouldBlock, excluding: schedule.id)
if schedule.shouldBlock {
store.subtractShields(with: stillActive.applied(to: selection))
store.subtractFilterDomains(stillActive.applied(to: domains))
Expand All @@ -137,15 +138,15 @@ final class NormalMonitor: DeviceActivityMonitor {
.groupUnblocks(sharedStore.loadTimedUnblocks(), at: .now)
}

private func otherActiveSchedules(like schedule: ScheduleDTO) -> ShieldExclusion {
private func activeSchedules(shouldBlock: Bool, excluding excludedID: UUID? = nil) -> ShieldExclusion {
let monitored = Set(DeviceActivityCenter().activities.map(\.rawValue))
return .activeSchedules(
sharedStore.loadSchedules(),
shouldBlock: schedule.shouldBlock,
shouldBlock: shouldBlock,
at: .now,
excluding: schedule.id
) { other in
monitored.contains(SharedConstants.scheduleActivityName(for: other.id))
excluding: excludedID
) { schedule in
monitored.contains(SharedConstants.scheduleActivityName(for: schedule.id))
}
}

Expand Down
15 changes: 15 additions & 0 deletions NormalTests/Shared/ShieldExclusionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,19 @@ struct ShieldExclusionTests {
let remaining = groups.union(schedules).applied(to: TokenFixtures.selection(apps: [1, 2, 3]))
#expect(remaining.applicationTokens == [TokenFixtures.app(3)])
}

@Test func withoutExclusionIncludesEveryActiveScheduleOfKind() throws {
let lunch = try schedule(apps: [1], domains: ["a.com"], shouldBlock: false)
let work = try schedule(apps: [2])

let allowing = ShieldExclusion.activeSchedules(
[lunch, work],
shouldBlock: false,
at: noonWednesday,
calendar: cal
) { _ in true }

#expect(allowing.applied(to: TokenFixtures.selection(apps: [1, 2])).applicationTokens == [TokenFixtures.app(2)])
#expect(allowing.applied(to: ["a.com", "b.com"]) == ["b.com"])
}
}
2 changes: 1 addition & 1 deletion Shared/SharedDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
static func groupUnblocks(_ unblocks: [TimedUnblockDTO], at date: Date) -> ShieldExclusion {
var exclusion = ShieldExclusion()
for unblock in unblocks where unblock.isGroupUnblock && unblock.endDate > date {
exclusion.add(unblock.selectionData, domains: unblock.customDomains)

Check warning on line 168 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

main actor-isolated property 'customDomains' can not be referenced from a nonisolated context

Check warning on line 168 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

main actor-isolated property 'customDomains' can not be referenced from a nonisolated context
}
return exclusion
}
Expand All @@ -174,7 +174,7 @@
_ schedules: [ScheduleDTO],
shouldBlock: Bool,
at date: Date,
excluding excludedID: UUID,
excluding excludedID: UUID? = nil,
calendar: Calendar = .current,
isEnabled: (ScheduleDTO) -> Bool
) -> ShieldExclusion {
Expand All @@ -183,28 +183,28 @@
where schedule.id != excludedID
&& schedule.shouldBlock == shouldBlock
&& isEnabled(schedule)
&& schedule.isActive(at: date, calendar: calendar)

Check warning on line 186 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

call to main actor-isolated instance method 'isActive(at:calendar:)' in a synchronous nonisolated context [#ActorIsolatedCall]

Check warning on line 186 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

call to main actor-isolated instance method 'isActive(at:calendar:)' in a synchronous nonisolated context [#ActorIsolatedCall]
{
exclusion.add(schedule.selectionData, domains: schedule.customDomains)

Check warning on line 188 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

main actor-isolated property 'customDomains' can not be referenced from a nonisolated context

Check warning on line 188 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

main actor-isolated property 'customDomains' can not be referenced from a nonisolated context
}
return exclusion
}

func union(_ other: ShieldExclusion) -> ShieldExclusion {
var merged = self
merged.selection = selection.union(other.selection)

Check warning on line 195 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

call to main actor-isolated instance method 'union' in a synchronous nonisolated context [#ActorIsolatedCall]

Check warning on line 195 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

call to main actor-isolated instance method 'union' in a synchronous nonisolated context [#ActorIsolatedCall]
merged.domains.formUnion(other.domains)
return merged
}

private mutating func add(_ selectionData: Data, domains: [String]) {
guard let added = try? FamilyActivitySelection.fromData(selectionData) else { return }

Check warning on line 201 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

call to main actor-isolated static method 'fromData' in a synchronous nonisolated context [#ActorIsolatedCall]

Check warning on line 201 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

call to main actor-isolated static method 'fromData' in a synchronous nonisolated context [#ActorIsolatedCall]
selection = selection.union(added)

Check warning on line 202 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

call to main actor-isolated instance method 'union' in a synchronous nonisolated context [#ActorIsolatedCall]

Check warning on line 202 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

call to main actor-isolated instance method 'union' in a synchronous nonisolated context [#ActorIsolatedCall]
self.domains.formUnion(domains)
}

func applied(to selection: FamilyActivitySelection) -> FamilyActivitySelection {
selection.subtracting(self.selection)

Check warning on line 207 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

call to main actor-isolated instance method 'subtracting' in a synchronous nonisolated context [#ActorIsolatedCall]

Check warning on line 207 in Shared/SharedDTO.swift

View workflow job for this annotation

GitHub Actions / Unit tests

call to main actor-isolated instance method 'subtracting' in a synchronous nonisolated context [#ActorIsolatedCall]
}

func applied(to domains: [String]) -> [String] {
Expand Down
Loading