listless

A simple list app for Apple platforms
Log | Files | Refs | README | LICENSE

commit 15bb996563bf53846b97608b473ba0dc2286ca53
parent ea40cd889a49660810e5763e530259bfb54c15e0
Author: Michael Camilleri <[email protected]>
Date:   Thu,  5 Mar 2026 10:37:00 +0900

Rename Sync Diagnostics to iCloud Diagnostics

Diffstat:
MListless.xcodeproj/project.pbxproj | 4++++
MListlessMac/ListlessMacApp.swift | 96+++----------------------------------------------------------------------------
AListlessMac/Views/SyncDiagnosticsView.swift | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MListlessiOS/Views/SyncDiagnosticsView.swift | 2+-
4 files changed, 99 insertions(+), 94 deletions(-)

diff --git a/Listless.xcodeproj/project.pbxproj b/Listless.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ 4DD2030E321567BD25661760 /* SyncDiagnosticsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9288507CE6023425D1DE724 /* SyncDiagnosticsView.swift */; }; 4E5A0A02121E02124F80E320 /* TaskListView+SyncUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7BD42B1E3C71333FA24893 /* TaskListView+SyncUI.swift */; }; 5035EC4C7518A5FF9AD454CA /* TaskRowDragGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = F416DD868A4C044F0D64F8D0 /* TaskRowDragGesture.swift */; }; + 568635BB34CD7EBE24E66A15 /* SyncDiagnosticsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF3374CE58E7D9378C6997D2 /* SyncDiagnosticsView.swift */; }; 5761B201BF46FCA9C5C98CEF /* PlatformScrollIndicatorsModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 466F9B0E407DF1F5B4789531 /* PlatformScrollIndicatorsModifier.swift */; }; 5B60B409CE4BA668DB30A65D /* Listless.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = C093494053E6C348F245D4EC /* Listless.xcdatamodeld */; }; 5D3EE9526DA269EE9EE3AB52 /* AppColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E998119283F784B9ADEE28 /* AppColors.swift */; }; @@ -141,6 +142,7 @@ C71466C5CD1A5BA984352F8D /* Listless iOS Unit Tests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = "Listless iOS Unit Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; C9B14DC786A336008AAB78EE /* .gitkeep */ = {isa = PBXFileReference; path = .gitkeep; sourceTree = "<group>"; }; CB43816B8E7F083A2AD07F28 /* TaskListView+Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TaskListView+Toolbar.swift"; sourceTree = "<group>"; }; + CF3374CE58E7D9378C6997D2 /* SyncDiagnosticsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncDiagnosticsView.swift; sourceTree = "<group>"; }; D2C018476BD91B73870244B9 /* TaskListViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskListViewProtocol.swift; sourceTree = "<group>"; }; D2D9CDDA8913CD116FB4DA74 /* TaskListView+PullGestures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TaskListView+PullGestures.swift"; sourceTree = "<group>"; }; D3E995954787F0A14CCFF348 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; }; @@ -296,6 +298,7 @@ D12ECC901ABED96B86CC85B5 /* Views */ = { isa = PBXGroup; children = ( + CF3374CE58E7D9378C6997D2 /* SyncDiagnosticsView.swift */, 632DA39B24C4CF1528A1A24D /* TaskListView.swift */, E06485DBE35B60868E14202A /* TaskRowView.swift */, ); @@ -511,6 +514,7 @@ A0AA8FD4C542E9AEB2437BC2 /* PersistenceController.swift in Sources */, 5761B201BF46FCA9C5C98CEF /* PlatformScrollIndicatorsModifier.swift in Sources */, DEE187A790A4058FE4AFDB2E /* PlatformTextFieldWidthModifier.swift in Sources */, + 568635BB34CD7EBE24E66A15 /* SyncDiagnosticsView.swift in Sources */, C1FE091454864C4BBBBEB077 /* TaskItem.swift in Sources */, 93275BD83342D6CE94272E6A /* TaskListTypes.swift in Sources */, E5878BAA0EA66A94440E2B0F /* TaskListView+Logic.swift in Sources */, diff --git a/ListlessMac/ListlessMacApp.swift b/ListlessMac/ListlessMacApp.swift @@ -164,7 +164,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation { } let defaultContentSize = NSSize(width: 760, height: 520) - let rootView = SyncDiagnosticsWindowView(syncMonitor: persistenceController.syncMonitor) + let rootView = SyncDiagnosticsView(syncMonitor: persistenceController.syncMonitor) let window = NSWindow( contentRect: NSRect(origin: .zero, size: defaultContentSize), styleMask: [.titled, .closable, .miniaturizable, .resizable], @@ -172,7 +172,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation { defer: false ) window.contentViewController = NSHostingController(rootView: rootView) - window.title = "Sync Diagnostics" + window.title = "iCloud Diagnostics" window.setContentSize(defaultContentSize) window.minSize = NSSize(width: 480, height: 320) window.isReleasedWhenClosed = false @@ -304,7 +304,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation { windowMenu.addItem(withTitle: "Zoom", action: #selector(NSWindow.performZoom(_:)), keyEquivalent: "") windowMenu.addItem(NSMenuItem.separator()) let syncDiagnosticsItem = NSMenuItem( - title: "Sync Diagnostics", + title: "iCloud Diagnostics", action: #selector(handleShowSyncDiagnostics), keyEquivalent: "" ) @@ -340,93 +340,3 @@ enum ListlessMacMain { } } } - -private struct SyncDiagnosticsWindowView: View { - @ObservedObject var syncMonitor: CloudKitSyncMonitor - - private static let timestampFormatter: DateFormatter = { - let formatter = DateFormatter() - formatter.dateStyle = .none - formatter.timeStyle = .medium - return formatter - }() - - var body: some View { - List { - Section("Status") { - row("Transient Banner", syncMonitor.transientErrorMessage ?? "None") - row("Last Error Domain", syncMonitor.lastCloudKitErrorDomain ?? "None") - row("Last Error Code", syncMonitor.lastCloudKitErrorCode.map(String.init) ?? "None") - row("Last Error Description", syncMonitor.lastCloudKitErrorDescription ?? "None") - row( - "Last Success", - syncMonitor.lastSuccessfulSyncDate.map(Self.timestampFormatter.string(from:)) ?? "None" - ) - } - - Section("Recent Events") { - if syncMonitor.recentDiagnostics.isEmpty { - Text("No events captured yet.") - .foregroundStyle(.secondary) - } else { - ForEach(syncMonitor.recentDiagnostics.reversed()) { entry in - VStack(alignment: .leading, spacing: 4) { - Text( - "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())]" - ) - .font(.caption.monospaced()) - .foregroundStyle(.secondary) - - Text(entry.message) - .font(.caption.monospaced()) - .textSelection(.enabled) - } - .padding(.vertical, 2) - } - } - } - } - .textSelection(.enabled) - .toolbar { - ToolbarItem { - Button("Copy") { - let pasteboard = NSPasteboard.general - pasteboard.clearContents() - pasteboard.setString(diagnosticDump, forType: .string) - } - } - } - } - - @ViewBuilder - private func row(_ title: String, _ value: String) -> some View { - VStack(alignment: .leading, spacing: 4) { - Text(title) - .font(.caption) - .foregroundStyle(.secondary) - Text(value) - .font(.body.monospaced()) - .textSelection(.enabled) - } - .padding(.vertical, 2) - } - - private var diagnosticDump: String { - var lines: [String] = [] - lines.append("Transient Banner: \(syncMonitor.transientErrorMessage ?? "None")") - lines.append("Last Error Domain: \(syncMonitor.lastCloudKitErrorDomain ?? "None")") - lines.append("Last Error Code: \(syncMonitor.lastCloudKitErrorCode.map(String.init) ?? "None")") - lines.append("Last Error Description: \(syncMonitor.lastCloudKitErrorDescription ?? "None")") - lines.append( - "Last Success: \(syncMonitor.lastSuccessfulSyncDate.map(Self.timestampFormatter.string(from:)) ?? "None")" - ) - lines.append("") - lines.append("Recent Events:") - for entry in syncMonitor.recentDiagnostics { - lines.append( - "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())] \(entry.message)" - ) - } - return lines.joined(separator: "\n") - } -} diff --git a/ListlessMac/Views/SyncDiagnosticsView.swift b/ListlessMac/Views/SyncDiagnosticsView.swift @@ -0,0 +1,91 @@ +import SwiftUI + +struct SyncDiagnosticsView: View { + @ObservedObject var syncMonitor: CloudKitSyncMonitor + + private static let timestampFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateStyle = .none + formatter.timeStyle = .medium + return formatter + }() + + var body: some View { + List { + Section("Status") { + row("Transient Banner", syncMonitor.transientErrorMessage ?? "None") + row("Last Error Domain", syncMonitor.lastCloudKitErrorDomain ?? "None") + row("Last Error Code", syncMonitor.lastCloudKitErrorCode.map(String.init) ?? "None") + row("Last Error Description", syncMonitor.lastCloudKitErrorDescription ?? "None") + row( + "Last Success", + syncMonitor.lastSuccessfulSyncDate.map(Self.timestampFormatter.string(from:)) ?? "None" + ) + } + + Section("Recent Events") { + if syncMonitor.recentDiagnostics.isEmpty { + Text("No events captured yet.") + .foregroundStyle(.secondary) + } else { + ForEach(syncMonitor.recentDiagnostics.reversed()) { entry in + VStack(alignment: .leading, spacing: 4) { + Text( + "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())]" + ) + .font(.caption.monospaced()) + .foregroundStyle(.secondary) + + Text(entry.message) + .font(.caption.monospaced()) + .textSelection(.enabled) + } + .padding(.vertical, 2) + } + } + } + } + .textSelection(.enabled) + .toolbar { + ToolbarItem { + Button("Copy") { + let pasteboard = NSPasteboard.general + pasteboard.clearContents() + pasteboard.setString(diagnosticDump, forType: .string) + } + } + } + } + + @ViewBuilder + private func row(_ title: String, _ value: String) -> some View { + VStack(alignment: .leading, spacing: 4) { + Text(title) + .font(.caption) + .foregroundStyle(.secondary) + Text(value) + .font(.body.monospaced()) + .textSelection(.enabled) + } + .padding(.vertical, 2) + } + + private var diagnosticDump: String { + var lines: [String] = [] + lines.append("Transient Banner: \(syncMonitor.transientErrorMessage ?? "None")") + lines.append("Last Error Domain: \(syncMonitor.lastCloudKitErrorDomain ?? "None")") + lines.append("Last Error Code: \(syncMonitor.lastCloudKitErrorCode.map(String.init) ?? "None")") + lines.append("Last Error Description: \(syncMonitor.lastCloudKitErrorDescription ?? "None")") + lines.append( + "Last Success: \(syncMonitor.lastSuccessfulSyncDate.map(Self.timestampFormatter.string(from:)) ?? "None")" + ) + lines.append("") + lines.append("Recent Events:") + for entry in syncMonitor.recentDiagnostics { + lines.append( + "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())] \(entry.message)" + ) + } + return lines.joined(separator: "\n") + } +} diff --git a/ListlessiOS/Views/SyncDiagnosticsView.swift b/ListlessiOS/Views/SyncDiagnosticsView.swift @@ -49,7 +49,7 @@ struct SyncDiagnosticsView: View { } } } - .navigationTitle("Sync Diagnostics") + .navigationTitle("iCloud Diagnostics") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) {