Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
aac7da2
Add Settings Audit Log feature
Copilot Apr 7, 2026
31ceba3
Fix DateFormatter reuse in groupedEntries
Copilot Apr 7, 2026
90dfa96
Use static DateFormatters to avoid repeated expensive initialization
Copilot Apr 7, 2026
2e6b7b9
fix: correct provider recursion and simplify pagination in settings a…
Copilot Apr 7, 2026
4ddcf4d
fix: add missing closing brace for view(resolver:) in Screen.swift
Copilot Apr 7, 2026
86573b2
fix: add missing settings audit log files to Xcode project
Copilot Apr 7, 2026
9dcdf8a
fix: move source files from PBXResourcesBuildPhase to PBXSourcesBuild…
Copilot Apr 7, 2026
5e5648a
refactor: DRY - extract shared therapy audit logging and generic Mirr…
Copilot Apr 7, 2026
503b8cb
fix: remove dead selectedCategory == "All" check in loadEntries
Copilot Apr 7, 2026
066be2b
fix: quote paths containing + in PBXFileReference entries for Setting…
Copilot Apr 7, 2026
de6793e
feat: 10-minute grouping for settings change events with shared notes…
Copilot Apr 7, 2026
ecabc8b
fix: thread safety for group resolution and stable UUID fallback
Copilot Apr 7, 2026
3cebf58
fix: crash when opening settings audit log - use plain structs instea…
Copilot Apr 7, 2026
d6fb851
fix: clarify comment about managed object conversion timing
Copilot Apr 7, 2026
44fd15f
fix: empty detail page on tap and add glucose unit conversion in audi…
Copilot Apr 7, 2026
df76ec5
fix: handle range formats and mg/dL/U unit in glucose conversion for …
Copilot Apr 7, 2026
cd57cee
feat: show notes on overview, auto-save notes, fix crash, show daily …
Copilot Apr 7, 2026
8ce3ef8
feat: audit log TT/Override activations and pump changes
Copilot Apr 7, 2026
f607a31
fix: restrict daily basal total to therapy.basalProfile key only
Copilot Apr 7, 2026
71383ec
fix: replace overrideTarget with target nil/zero check on OverrideStored
Copilot Apr 7, 2026
81bbca3
fix: prevent use-after-free crash in SettingsAuditLog by cancelling d…
Copilot Apr 7, 2026
0551260
fix: remove @MainActor from loadEntries/updateNote to fix compiler er…
Copilot Apr 7, 2026
04119ff
fix: prevent use-after-free crash in settingsauditlog statemodel deinit
HT-Sjoerd-Bo3 Apr 7, 2026
b26f6e5
feat: add CSV export, time format cleanup (HH:mm), and delete all ent…
Copilot Apr 7, 2026
4a96e8f
perf: make time-seconds regex a static constant to avoid recompilation
Copilot Apr 7, 2026
a513659
fix: remove duplicate ShareSheet declaration causing compiler error
Copilot Apr 7, 2026
00c3397
fix: force-copy strings in changeentry to break nsstring bridge with …
Sjoerd-Bo3 Apr 7, 2026
2f1471b
fix: replace computed properties with stored state to prevent crash d…
Sjoerd-Bo3 Apr 7, 2026
be2998e
fix: consolidate dual sheet modifiers into single ActiveSheet enum to…
Copilot Apr 8, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import CoreData
import Foundation

@objc(SettingsChangeStored) public class SettingsChangeStored: NSManagedObject {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import CoreData
import Foundation

public extension SettingsChangeStored {
@nonobjc class func fetchRequest() -> NSFetchRequest<SettingsChangeStored> {
NSFetchRequest<SettingsChangeStored>(entityName: "SettingsChangeStored")
}

@NSManaged var id: UUID?
@NSManaged var date: Date?
@NSManaged var category: String?
@NSManaged var subcategory: String?
@NSManaged var settingName: String?
@NSManaged var settingKey: String?
@NSManaged var oldValue: String?
@NSManaged var newValue: String?
@NSManaged var unit: String?
@NSManaged var note: String?
@NSManaged var source: String?
@NSManaged var groupId: UUID?
}

extension SettingsChangeStored: Identifiable {}
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,32 @@
<fetchIndexElement property="startDate" type="Binary" order="descending"/>
</fetchIndex>
</entity>
<entity name="SettingsChangeStored" representedClassName="SettingsChangeStored" syncable="YES">
<attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="category" optional="YES" attributeType="String"/>
<attribute name="subcategory" optional="YES" attributeType="String"/>
<attribute name="settingName" optional="YES" attributeType="String"/>
<attribute name="settingKey" optional="YES" attributeType="String"/>
<attribute name="oldValue" optional="YES" attributeType="String"/>
<attribute name="newValue" optional="YES" attributeType="String"/>
<attribute name="unit" optional="YES" attributeType="String"/>
<attribute name="note" optional="YES" attributeType="String"/>
<attribute name="source" optional="YES" attributeType="String"/>
<attribute name="groupId" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
<fetchIndex name="byDate">
<fetchIndexElement property="date" type="Binary" order="descending"/>
</fetchIndex>
<fetchIndex name="bySettingKey">
<fetchIndexElement property="settingKey" type="Binary" order="ascending"/>
</fetchIndex>
<fetchIndex name="byCategory">
<fetchIndexElement property="category" type="Binary" order="ascending"/>
</fetchIndex>
<fetchIndex name="byGroupId">
<fetchIndexElement property="groupId" type="Binary" order="descending"/>
</fetchIndex>
</entity>
<entity name="TempTargetStored" representedClassName="TempTargetStored" syncable="YES">
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="duration" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
Expand Down
48 changes: 48 additions & 0 deletions Trio.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,14 @@
FE41E4D629463EE20047FD55 /* NightscoutPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */; };
FE66D16B291F74F8005D6F77 /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE66D16A291F74F8005D6F77 /* Bundle+Extensions.swift */; };
FEFFA7A22929FE49007B8193 /* UIDevice+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFFA7A12929FE49007B8193 /* UIDevice+Extensions.swift */; };
FBCB3699E2A64154804513CB /* SettingsAuditStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAA3132E8B54247814312BF /* SettingsAuditStorage.swift */; };
48CA13DF0276417A91EC645F /* SettingsMetadataRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E99596B1BB84920872C57F3 /* SettingsMetadataRegistry.swift */; };
7653F937FB444D939244C3F4 /* SettingsChangeStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD0682F55104692AB78F945 /* SettingsChangeStored+CoreDataClass.swift */; };
265022BEAF494CF3A17FBE3B /* SettingsChangeStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E890DBA44C94C3EBFB05B4F /* SettingsChangeStored+CoreDataProperties.swift */; };
FA78C14237D94C23A43D7364 /* SettingsAuditLogDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D9D82C45974BABB46050FE /* SettingsAuditLogDataFlow.swift */; };
6134855D5A744E32B610E66D /* SettingsAuditLogProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0761A354F4004A5690B74369 /* SettingsAuditLogProvider.swift */; };
80ACD53EB1C24FAC88CD980F /* SettingsAuditLogStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D942BE6567401B80F6F0C4 /* SettingsAuditLogStateModel.swift */; };
D21CE159AA2F40C099B3F5CC /* SettingsAuditLogRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE96FC62A4B242278697EBBA /* SettingsAuditLogRootView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -1584,6 +1592,14 @@
FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutPreferences.swift; sourceTree = "<group>"; };
FE66D16A291F74F8005D6F77 /* Bundle+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Extensions.swift"; sourceTree = "<group>"; };
FEFFA7A12929FE49007B8193 /* UIDevice+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Extensions.swift"; sourceTree = "<group>"; };
8AAA3132E8B54247814312BF /* SettingsAuditStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAuditStorage.swift; sourceTree = "<group>"; };
1E99596B1BB84920872C57F3 /* SettingsMetadataRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMetadataRegistry.swift; sourceTree = "<group>"; };
EAD0682F55104692AB78F945 /* SettingsChangeStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsChangeStored+CoreDataClass.swift"; sourceTree = "<group>"; };
0E890DBA44C94C3EBFB05B4F /* SettingsChangeStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsChangeStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
D2D9D82C45974BABB46050FE /* SettingsAuditLogDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAuditLogDataFlow.swift; sourceTree = "<group>"; };
0761A354F4004A5690B74369 /* SettingsAuditLogProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAuditLogProvider.swift; sourceTree = "<group>"; };
45D942BE6567401B80F6F0C4 /* SettingsAuditLogStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAuditLogStateModel.swift; sourceTree = "<group>"; };
CE96FC62A4B242278697EBBA /* SettingsAuditLogRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAuditLogRootView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFileSystemSynchronizedRootGroup section */
Expand Down Expand Up @@ -1951,6 +1967,7 @@
C2C98283C436DB934D7E7994 /* Treatments */,
190EBCC229FF134900BA767D /* UserInterfaceSettings */,
CE94597C29E9E1CD0047C9C6 /* WatchConfig */,
711F34EA2FAD42E58BB8DCF6 /* SettingsAuditLog */,
);
path = Modules;
sourceTree = "<group>";
Expand Down Expand Up @@ -2526,6 +2543,7 @@
BDC2EA442C3043B000E5BBD0 /* OverrideStorage.swift */,
5864E8582C42CFAE00294306 /* DeterminationStorage.swift */,
BD4D73A12D15A4220052227B /* TDDStorage.swift */,
8AAA3132E8B54247814312BF /* SettingsAuditStorage.swift */,
);
path = Storage;
sourceTree = "<group>";
Expand All @@ -2547,6 +2565,7 @@
isa = PBXGroup;
children = (
38AEE75125F022080013F05B /* SettingsManager.swift */,
1E99596B1BB84920872C57F3 /* SettingsMetadataRegistry.swift */,
);
path = SettingsManager;
sourceTree = "<group>";
Expand Down Expand Up @@ -3669,6 +3688,8 @@
491D6FBA2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift */,
491D6FBB2D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift */,
491D6FBC2D56741C00C49F67 /* TempTargetStored+CoreDataProperties.swift */,
EAD0682F55104692AB78F945 /* SettingsChangeStored+CoreDataClass.swift */,
0E890DBA44C94C3EBFB05B4F /* SettingsChangeStored+CoreDataProperties.swift */,
);
path = "Classes+Properties";
sourceTree = "<group>";
Expand Down Expand Up @@ -3833,6 +3854,25 @@
path = View;
sourceTree = "<group>";
};
2E4621F96353486780553A79 /* View */ = {
isa = PBXGroup;
children = (
CE96FC62A4B242278697EBBA /* SettingsAuditLogRootView.swift */,
);
path = View;
sourceTree = "<group>";
};
711F34EA2FAD42E58BB8DCF6 /* SettingsAuditLog */ = {
isa = PBXGroup;
children = (
D2D9D82C45974BABB46050FE /* SettingsAuditLogDataFlow.swift */,
0761A354F4004A5690B74369 /* SettingsAuditLogProvider.swift */,
45D942BE6567401B80F6F0C4 /* SettingsAuditLogStateModel.swift */,
2E4621F96353486780553A79 /* View */,
);
path = SettingsAuditLog;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -4792,6 +4832,14 @@
8194B80890CDD6A3C13B0FEE /* SnoozeStateModel.swift in Sources */,
BDA25EE42D260CD500035F34 /* AppleWatchManager.swift in Sources */,
0437CE46C12535A56504EC19 /* SnoozeRootView.swift in Sources */,
FBCB3699E2A64154804513CB /* SettingsAuditStorage.swift in Sources */,
48CA13DF0276417A91EC645F /* SettingsMetadataRegistry.swift in Sources */,
7653F937FB444D939244C3F4 /* SettingsChangeStored+CoreDataClass.swift in Sources */,
265022BEAF494CF3A17FBE3B /* SettingsChangeStored+CoreDataProperties.swift in Sources */,
FA78C14237D94C23A43D7364 /* SettingsAuditLogDataFlow.swift in Sources */,
6134855D5A744E32B610E66D /* SettingsAuditLogProvider.swift in Sources */,
80ACD53EB1C24FAC88CD980F /* SettingsAuditLogStateModel.swift in Sources */,
D21CE159AA2F40C099B3F5CC /* SettingsAuditLogRootView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
18 changes: 18 additions & 0 deletions Trio/Sources/APS/DeviceDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
@Injected() private var glucoseStorage: GlucoseStorage!
@Injected() private var settingsManager: SettingsManager!
@Injected() private var bluetoothProvider: BluetoothStateManager!
@Injected() private var auditStorage: SettingsAuditStorage!

@Persisted(key: "BaseDeviceDataManager.lastEventDate") var lastEventDate: Date? = nil
@SyncAccess(lock: accessLock) @Persisted(key: "BaseDeviceDataManager.lastHeartBeatTime") var lastHeartBeatTime: Date =
Expand All @@ -85,6 +86,23 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {

var pumpManager: PumpManagerUI? {
didSet {
// Audit log pump changes
let oldName = oldValue?.localizedTitle
let newName = pumpManager?.localizedTitle
if oldName != newName {
auditStorage?.logChange(
category: "Devices",
subcategory: "Pump",
settingName: "Pump",
settingKey: "devices.pump",
oldValue: oldName ?? "(none)",
newValue: newName ?? "(disconnected)",
unit: nil,
note: nil,
source: "manual"
)
}

if let pumpManager = pumpManager {
pumpManager.pumpManagerDelegate = self
pumpManager.delegateQueue = processQueue
Expand Down
Loading