From e68a6598fc3db611b5f639b1c3485fc6ef47366e Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Thu, 5 Nov 2026 09:33:59 +0000 Subject: [PATCH] new app store assets ready for relese ui updates --- Tarnax/App.swift | 4 ++-- Tarnax/Core.swift | 7 +++++++ Tarnax/Details.swift | 40 ++++++++++++++++++++++++++++++---------- docs/VALIDATION.md | 4 ++++ 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/Tarnax/App.swift b/Tarnax/App.swift index 5f63655..0b4c0fb 100644 --- a/Tarnax/App.swift +++ b/Tarnax/App.swift @@ -144,12 +144,12 @@ struct TorrentRow: View { var body: some View { VStack(alignment: .leading, spacing: 10) { (dynamicTypeSize.isAccessibilitySize ? AnyLayout(VStackLayout(alignment: .leading, spacing: 6)) : AnyLayout(HStackLayout(alignment: .top))) { - Image(systemName: torrent.complete ? "checkmark.circle.fill" : "arrow.down.circle.fill").foregroundStyle(torrent.complete ? .green : .yellow).font(.title2) + Image(systemName: torrent.complete ? "checkmark.circle.fill" : "arrow.down.circle.fill").foregroundStyle(torrent.statusColor).font(.title2) Text(torrent.name).font(.headline).lineLimit(dynamicTypeSize.isAccessibilitySize ? nil : 2) if !dynamicTypeSize.isAccessibilitySize { Spacer() } Text(torrent.progress, format: .percent.precision(.fractionLength(1))).font(.subheadline.monospacedDigit()).foregroundStyle(.secondary) } - ProgressView(value: torrent.complete ? 1 : min(max(torrent.progress, 0), 1)).tint(torrent.complete ? .green : .yellow).accessibilityLabel("Download progress") + ProgressView(value: torrent.complete ? 1 : min(max(torrent.progress, 0), 1)).tint(torrent.statusColor).accessibilityLabel("Download progress") (dynamicTypeSize.isAccessibilitySize ? AnyLayout(VStackLayout(alignment: .leading, spacing: 6)) : AnyLayout(HStackLayout())) { Label(bytes(torrent.dlspeed) + "/s", systemImage: "arrow.down").accessibilityLabel("Download speed").accessibilityValue(bytes(torrent.dlspeed) + " per second") Label(bytes(torrent.upspeed) + "/s", systemImage: "arrow.up").accessibilityLabel("Upload speed").accessibilityValue(bytes(torrent.upspeed) + " per second") diff --git a/Tarnax/Core.swift b/Tarnax/Core.swift index 6384bd4..28c1b19 100644 --- a/Tarnax/Core.swift +++ b/Tarnax/Core.swift @@ -43,6 +43,13 @@ struct Torrent: Decodable, Identifiable, Hashable { var ratio: Double; var category: String; var tags: String var id: String { hash } var complete: Bool { progress >= 1 || ["uploading", "stalledUP", "forcedUP", "queuedUP", "pausedUP", "stoppedUP"].contains(state) } + var statusColor: Color { + if ["error", "missingFiles"].contains(state) || state.hasPrefix("stalled") { return .red } + if paused { return .pink } + if state.hasPrefix("queued") { return .orange } + if ["uploading", "forcedUP"].contains(state) { return .purple } + return complete ? .green : .yellow + } var paused: Bool { state.hasPrefix("paused") || state.hasPrefix("stopped") } var active: Bool { ["downloading", "forcedDL", "metaDL", "forcedMetaDL"].contains(state) } } diff --git a/Tarnax/Details.swift b/Tarnax/Details.swift index 8a63b91..f6b69be 100644 --- a/Tarnax/Details.swift +++ b/Tarnax/Details.swift @@ -21,10 +21,11 @@ struct DetailView: View { List { Section { TorrentRow(torrent: live) } Section { - ViewThatFits(in: .horizontal) { - HStack { primaryControls } - VStack(alignment: .leading) { primaryControls } - }.buttonStyle(.bordered).controlSize(.regular) + if dynamicTypeSize.isAccessibilitySize { + VStack(spacing: 8) { primaryControls } + } else { + HStack(spacing: 8) { primaryControls } + } if model.pendingActions.contains(torrent.hash) { ProgressView("Applying action…") } if let feedback = model.actionFeedback { Text(feedback).font(.callout) } } @@ -71,12 +72,15 @@ struct DetailView: View { } } @ViewBuilder var primaryControls: some View { - Button("Resume", systemImage: "play.fill") { Task { await model.action("resume", hash: torrent.hash) } } - .frame(minHeight: 44).disabled(!live.paused || model.pendingActions.contains(torrent.hash)) - Button("Pause", systemImage: "pause.fill") { Task { await model.action("pause", hash: torrent.hash) } } - .frame(minHeight: 44).disabled(live.paused || model.pendingActions.contains(torrent.hash)) - Menu("More", systemImage: "ellipsis.circle") { controls } - .frame(minHeight: 44).disabled(model.pendingActions.contains(torrent.hash) || !model.connected) + Button("Resume") { Task { await model.action("resume", hash: torrent.hash) } } + .buttonStyle(TorrentActionButtonStyle()) + .disabled(!live.paused || model.pendingActions.contains(torrent.hash) || !model.connected) + Button("Pause") { Task { await model.action("pause", hash: torrent.hash) } } + .buttonStyle(TorrentActionButtonStyle()) + .disabled(live.paused || model.pendingActions.contains(torrent.hash) || !model.connected) + Menu { controls } label: { Text("More") } + .buttonStyle(TorrentActionButtonStyle()) + .disabled(model.pendingActions.contains(torrent.hash) || !model.connected) } var controls: some View { Group { @@ -194,3 +198,19 @@ struct KeyValues: View { } } import CoreFoundation + +// Identical label frames keep the two buttons and the menu the same size. +private struct TorrentActionButtonStyle: ButtonStyle { + @Environment(\.isEnabled) private var isEnabled + func makeBody(configuration: Configuration) -> some View { + configuration.label + .font(.subheadline.weight(.medium)) + .multilineTextAlignment(.center) + .padding(.horizontal, 4) + .frame(maxWidth: .infinity, minHeight: 44) + .foregroundStyle(isEnabled ? Color.accentColor : Color.secondary) + .background(Color.accentColor.opacity(configuration.isPressed ? 0.24 : 0.10), in: RoundedRectangle(cornerRadius: 10)) + .contentShape(RoundedRectangle(cornerRadius: 10)) + .opacity(isEnabled ? 1 : 0.55) + } +} diff --git a/docs/VALIDATION.md b/docs/VALIDATION.md index fa4bd09..a9fbe00 100644 --- a/docs/VALIDATION.md +++ b/docs/VALIDATION.md @@ -105,3 +105,7 @@ Build 7 adds blue/indigo/violet download strokes, teal dashed upload strokes, in Device acceptance still required: narrow iPhone/iPad widths and largest Dynamic Type, live server actions, Settings navigation under poor connectivity, background/relaunch graph restoration, notification permission denial and successful completion delivery. No signed deployment performed for this update. Final verification: unsigned generic iOS device Debug build **BUILD SUCCEEDED**; **91 executable model/transport/persistence/formatting contract checks passed**, including Settings view cancellation and real-error reporting; three provisioning-profile tests passed; project/Info.plist validation and diff whitespace checks passed. Logs: ~/Developer/Scratch/Tarnax/build-verified.log and tests-verified.log. Physical-device and live-server verification remain outstanding. + +### Status colours and detail controls — 5 November 2026 + +Shared torrent icons and bars now use yellow for downloads, purple for active seeding, red for stalled/error/missing files, pink for paused/stopped, orange for queued, and green for other completed transfers. Status-specific colours take priority over completion. Detail Resume/Pause/More share one button style with equal widths, 44-point minimum touch height, smaller subheadline labels and pressed feedback. Accessibility text sizes stack the controls vertically. Unsigned generic iOS Debug build succeeded; physical-device visual verification remains outstanding. Temporary build files and log are under ~/Developer/Scratch/Tarnax.