mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-05-01 20:03:51 -04:00
refactor: Update job status icons and UI elements in Swift client
- Replaced job status icons with SF Symbols for improved visual consistency. - Adjusted spacing and font sizes in JobMonitorView and JobRowView for better layout and readability. - Simplified progress percentage and duration formatting to display whole numbers. - Enhanced the overall appearance of job rows with consistent padding and background styling.
This commit is contained in:
@@ -48,15 +48,15 @@ enum JobStatus: String, Codable, CaseIterable {
|
||||
var icon: String {
|
||||
switch self {
|
||||
case .running:
|
||||
return "⚡️"
|
||||
return "circle.fill"
|
||||
case .completed:
|
||||
return "✅"
|
||||
return "checkmark.circle.fill"
|
||||
case .failed:
|
||||
return "❌"
|
||||
return "xmark.circle.fill"
|
||||
case .paused:
|
||||
return "⏸️"
|
||||
return "pause.circle.fill"
|
||||
case .queued:
|
||||
return "⏳"
|
||||
return "clock.fill"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,18 +23,18 @@ struct JobMonitorView: View {
|
||||
|
||||
private var headerView: some View {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
Text("Spacedrive Jobs")
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
.font(.system(size: 16, weight: .semibold))
|
||||
.foregroundColor(.primary)
|
||||
|
||||
HStack(spacing: 6) {
|
||||
Circle()
|
||||
.fill(connectionStatusColor)
|
||||
.frame(width: 8, height: 8)
|
||||
.frame(width: 6, height: 6)
|
||||
|
||||
Text(viewModel.connectionStatus.displayName)
|
||||
.font(.caption)
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
@@ -46,14 +46,14 @@ struct JobMonitorView: View {
|
||||
viewModel.reconnect()
|
||||
}) {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
.font(.system(size: 14, weight: .medium))
|
||||
.font(.system(size: 12, weight: .medium))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.help("Reconnect to daemon")
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 10)
|
||||
}
|
||||
|
||||
private var connectionStatusColor: Color {
|
||||
@@ -90,7 +90,7 @@ struct JobMonitorView: View {
|
||||
|
||||
private var jobListView: some View {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 12) {
|
||||
LazyVStack(spacing: 4) {
|
||||
ForEach(viewModel.jobs) { job in
|
||||
JobRowView(job: job)
|
||||
.transition(.asymmetric(
|
||||
@@ -99,10 +99,10 @@ struct JobMonitorView: View {
|
||||
))
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
.animation(.easeInOut(duration: 0.3), value: viewModel.jobs.count)
|
||||
.animation(.easeInOut(duration: 0.2), value: viewModel.jobs.count)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ struct JobRowView: View {
|
||||
let job: JobInfo
|
||||
|
||||
private var progressPercentage: String {
|
||||
return String(format: "%.1f%%", job.progress * 100)
|
||||
return String(format: "%.0f%%", job.progress * 100)
|
||||
}
|
||||
|
||||
private var timeAgo: String {
|
||||
@@ -18,95 +18,93 @@ struct JobRowView: View {
|
||||
let duration = completedAt.timeIntervalSince(job.startedAt)
|
||||
|
||||
if duration < 60 {
|
||||
return String(format: "%.1fs", duration)
|
||||
return String(format: "%.0fs", duration)
|
||||
} else if duration < 3600 {
|
||||
return String(format: "%.1fm", duration / 60)
|
||||
return String(format: "%.0fm", duration / 60)
|
||||
} else {
|
||||
return String(format: "%.1fh", duration / 3600)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
// Header with job name and status
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
HStack(spacing: 12) {
|
||||
// Status indicator - simple colored circle
|
||||
Circle()
|
||||
.fill(statusColor)
|
||||
.frame(width: 8, height: 8)
|
||||
.opacity(job.status == .completed ? 1.0 : 0.8)
|
||||
|
||||
// Main content area
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
// Top row: Job name and status/progress
|
||||
HStack {
|
||||
Text(job.name)
|
||||
.font(.headline)
|
||||
.font(.system(size: 13, weight: .medium))
|
||||
.foregroundColor(.primary)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
|
||||
Text("ID: \(String(job.id.prefix(8)))...")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
Spacer()
|
||||
|
||||
Spacer()
|
||||
// Status and progress info
|
||||
HStack(spacing: 6) {
|
||||
if job.status == .running {
|
||||
Text(progressPercentage)
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.foregroundColor(.secondary)
|
||||
} else {
|
||||
Text(job.status.displayName)
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.foregroundColor(job.status == .failed ? .red : .secondary)
|
||||
}
|
||||
|
||||
HStack(spacing: 4) {
|
||||
Text(job.status.icon)
|
||||
.font(.title2)
|
||||
|
||||
Text(job.status.displayName)
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(statusColor)
|
||||
}
|
||||
}
|
||||
|
||||
// Progress bar (only show for running jobs)
|
||||
if job.status == .running {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack {
|
||||
Text("Progress")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(progressPercentage)
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
if let duration = duration {
|
||||
Text("• \(duration)")
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.secondary)
|
||||
.opacity(0.7)
|
||||
} else if job.status == .running {
|
||||
Text("• \(timeAgo)")
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.secondary)
|
||||
.opacity(0.7)
|
||||
}
|
||||
}
|
||||
|
||||
ProgressView(value: job.progress, total: 1.0)
|
||||
.progressViewStyle(LinearProgressViewStyle())
|
||||
.scaleEffect(x: 1, y: 0.8, anchor: .center)
|
||||
}
|
||||
}
|
||||
|
||||
// Error message (if any)
|
||||
if let errorMessage = job.errorMessage, !errorMessage.isEmpty {
|
||||
Text(errorMessage)
|
||||
.font(.caption)
|
||||
.foregroundColor(.red)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color.red.opacity(0.1))
|
||||
.cornerRadius(4)
|
||||
}
|
||||
// Progress bar for active jobs
|
||||
if job.status == .running || job.status == .paused {
|
||||
ProgressView(value: job.progress, total: 1.0)
|
||||
.progressViewStyle(LinearProgressViewStyle(tint: statusColor))
|
||||
.scaleEffect(x: 1, y: 0.6, anchor: .center)
|
||||
} else if job.status == .completed {
|
||||
// Completed indicator line
|
||||
Rectangle()
|
||||
.fill(Color.green.opacity(0.3))
|
||||
.frame(height: 2)
|
||||
.cornerRadius(1)
|
||||
}
|
||||
|
||||
// Timestamps
|
||||
HStack {
|
||||
Text("Started \(timeAgo)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Spacer()
|
||||
|
||||
if let duration = duration {
|
||||
Text("Duration: \(duration)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
// Error message (compact display)
|
||||
if let errorMessage = job.errorMessage, !errorMessage.isEmpty {
|
||||
Text(errorMessage)
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.red)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 44) // Fixed height for uniform appearance
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 10)
|
||||
.background(backgroundColorForStatus)
|
||||
.cornerRadius(8)
|
||||
.padding(.vertical, 6)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.fill(Color.primary.opacity(0.03))
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.stroke(borderColorForStatus, lineWidth: 1)
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.stroke(Color.primary.opacity(0.08), lineWidth: 0.5)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -124,36 +122,6 @@ struct JobRowView: View {
|
||||
return .gray
|
||||
}
|
||||
}
|
||||
|
||||
private var backgroundColorForStatus: Color {
|
||||
switch job.status {
|
||||
case .running:
|
||||
return Color.blue.opacity(0.05)
|
||||
case .completed:
|
||||
return Color.green.opacity(0.05)
|
||||
case .failed:
|
||||
return Color.red.opacity(0.05)
|
||||
case .paused:
|
||||
return Color.orange.opacity(0.05)
|
||||
case .queued:
|
||||
return Color.gray.opacity(0.05)
|
||||
}
|
||||
}
|
||||
|
||||
private var borderColorForStatus: Color {
|
||||
switch job.status {
|
||||
case .running:
|
||||
return Color.blue.opacity(0.2)
|
||||
case .completed:
|
||||
return Color.green.opacity(0.2)
|
||||
case .failed:
|
||||
return Color.red.opacity(0.2)
|
||||
case .paused:
|
||||
return Color.orange.opacity(0.2)
|
||||
case .queued:
|
||||
return Color.gray.opacity(0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
|
||||
Reference in New Issue
Block a user