feat(features): improve responsiveness and empty states across feature views

Use adaptive grid columns and atlasContentWidth for responsive layouts
in Overview, Apps, History, and SmartClean views. Add action buttons
to empty states. Use flexible height for Apps split view. Refine
SmartClean scan/plan UI with brand-tinted progress and status chips.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zhukang
2026-03-10 21:57:41 +08:00
parent 7da436a220
commit b7087c706e
4 changed files with 292 additions and 239 deletions

View File

@@ -130,7 +130,7 @@ public struct AppsFeatureView: View {
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}
.frame(height: 560)
.frame(minHeight: 400, maxHeight: .infinity)
}
}
.onAppear(perform: syncSelection)
@@ -186,7 +186,9 @@ public struct AppsFeatureView: View {
title: AtlasL10n.string("apps.list.empty.title"),
detail: AtlasL10n.string("apps.list.empty.detail"),
systemImage: "square.stack.3d.up.slash",
tone: .neutral
tone: .neutral,
actionTitle: AtlasL10n.string("emptystate.action.refresh"),
onAction: onRefreshApps
)
} else {
List(selection: $selectedAppID) {
@@ -351,6 +353,8 @@ private struct AppDetailView: View {
let onPreview: () -> Void
let onUninstall: () -> Void
@State private var showUninstallConfirmation = false
var body: some View {
VStack(alignment: .leading, spacing: AtlasSpacing.xl) {
HStack(alignment: .top, spacing: AtlasSpacing.lg) {
@@ -442,7 +446,7 @@ private struct AppDetailView: View {
title: item.title,
subtitle: item.detail,
footnote: item.recoverable ? AtlasL10n.string("apps.preview.row.recoverable") : AtlasL10n.string("apps.preview.row.review"),
systemImage: icon(for: item.kind),
systemImage: item.kind.atlasSystemImage,
tone: item.recoverable ? .success : .warning
) {
AtlasStatusChip(
@@ -491,26 +495,26 @@ private struct AppDetailView: View {
private var uninstallButton: some View {
Button(isUninstalling ? AtlasL10n.string("apps.uninstall.running") : AtlasL10n.string("apps.uninstall.action")) {
onUninstall()
showUninstallConfirmation = true
}
.buttonStyle(.atlasPrimary)
.disabled(isBusy || previewPlan == nil)
.accessibilityIdentifier("apps.uninstall.\(app.id.uuidString)")
.accessibilityHint(AtlasL10n.string("apps.uninstall.hint"))
}
private func icon(for kind: ActionItem.Kind) -> String {
switch kind {
case .removeCache:
return "trash"
case .removeApp:
return "app.badge.minus"
case .archiveFile:
return "archivebox"
case .inspectPermission:
return "lock.shield"
.confirmationDialog(
AtlasL10n.string("apps.confirm.uninstall.title"),
isPresented: $showUninstallConfirmation,
titleVisibility: .visible
) {
Button(AtlasL10n.string("apps.uninstall.action"), role: .destructive) {
onUninstall()
}
Button(AtlasL10n.string("confirm.cancel"), role: .cancel) {}
} message: {
Text(AtlasL10n.string("apps.confirm.uninstall.message", app.name))
}
}
}
private struct AppGroup: Identifiable {

View File

@@ -119,7 +119,7 @@ public struct HistoryFeatureView: View {
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}
.frame(height: 560)
.frame(minHeight: 400, maxHeight: .infinity)
}
}
}
@@ -713,7 +713,7 @@ private struct HistoryTaskSidebarRow: View {
var body: some View {
VStack(alignment: .leading, spacing: AtlasSpacing.xs) {
HStack(alignment: .center, spacing: AtlasSpacing.sm) {
Image(systemName: taskRun.kind.historySystemImage)
Image(systemName: taskRun.kind.atlasSystemImage)
.font(AtlasTypography.caption)
.foregroundStyle(taskRun.status.atlasTone.tint)
.accessibilityHidden(true)
@@ -1049,37 +1049,7 @@ private enum HistoryRecoveryCategory: String, CaseIterable {
}
}
private extension TaskKind {
var historySystemImage: String {
switch self {
case .scan:
return "sparkles"
case .executePlan:
return "play.circle"
case .uninstallApp:
return "trash"
case .restore:
return "arrow.uturn.backward.circle"
case .inspectPermissions:
return "lock.shield"
}
}
}
private extension TaskStatus {
var atlasTone: AtlasTone {
switch self {
case .queued:
return .neutral
case .running:
return .warning
case .completed:
return .success
case .failed, .cancelled:
return .danger
}
}
var historyCalloutTitle: String {
switch self {
case .queued:

View File

@@ -6,13 +6,28 @@ import SwiftUI
public struct OverviewFeatureView: View {
private let snapshot: AtlasWorkspaceSnapshot
private let isRefreshingHealthSnapshot: Bool
private let onStartSmartClean: (() -> Void)?
private let onNavigateToSmartClean: (() -> Void)?
private let onNavigateToHistory: (() -> Void)?
private let onNavigateToPermissions: (() -> Void)?
@Environment(\.atlasContentWidth) private var contentWidth
@State private var showAllOptimizations = false
public init(
snapshot: AtlasWorkspaceSnapshot = AtlasScaffoldWorkspace.snapshot(),
isRefreshingHealthSnapshot: Bool = false
isRefreshingHealthSnapshot: Bool = false,
onStartSmartClean: (() -> Void)? = nil,
onNavigateToSmartClean: (() -> Void)? = nil,
onNavigateToHistory: (() -> Void)? = nil,
onNavigateToPermissions: (() -> Void)? = nil
) {
self.snapshot = snapshot
self.isRefreshingHealthSnapshot = isRefreshingHealthSnapshot
self.onStartSmartClean = onStartSmartClean
self.onNavigateToSmartClean = onNavigateToSmartClean
self.onNavigateToHistory = onNavigateToHistory
self.onNavigateToPermissions = onNavigateToPermissions
}
public var body: some View {
@@ -27,15 +42,18 @@ public struct OverviewFeatureView: View {
systemImage: overviewCalloutTone.symbol
)
LazyVGrid(columns: AtlasLayout.metricColumns, spacing: AtlasSpacing.lg) {
AtlasMetricCard(
title: AtlasL10n.string("overview.metric.reclaimable.title"),
value: AtlasFormatters.byteCount(snapshot.reclaimableSpaceBytes),
detail: AtlasL10n.string("overview.metric.reclaimable.detail"),
tone: .success,
systemImage: "sparkles",
elevation: .prominent
)
// MARK: - Hero metric full width
AtlasMetricCard(
title: AtlasL10n.string("overview.metric.reclaimable.title"),
value: AtlasFormatters.byteCount(snapshot.reclaimableSpaceBytes),
detail: AtlasL10n.string("overview.metric.reclaimable.detail"),
tone: .success,
systemImage: "sparkles",
elevation: .prominent
)
// MARK: - Secondary metrics adaptive 2/1 columns
LazyVGrid(columns: secondaryColumns, spacing: AtlasSpacing.lg) {
AtlasMetricCard(
title: AtlasL10n.string("overview.metric.findings.title"),
value: "\(snapshot.findings.count)",
@@ -54,72 +72,13 @@ public struct OverviewFeatureView: View {
)
}
AtlasInfoCard(
title: AtlasL10n.string("overview.snapshot.title"),
subtitle: AtlasL10n.string("overview.snapshot.subtitle")
) {
if isRefreshingHealthSnapshot, snapshot.healthSnapshot == nil {
AtlasLoadingState(
title: AtlasL10n.string("overview.snapshot.loading.title"),
detail: AtlasL10n.string("overview.snapshot.loading.detail")
)
} else if let healthSnapshot = snapshot.healthSnapshot {
LazyVGrid(columns: AtlasLayout.metricColumns, spacing: AtlasSpacing.lg) {
AtlasMetricCard(
title: AtlasL10n.string("overview.snapshot.memory.title"),
value: "\(formatted(healthSnapshot.memoryUsedGB))/\(formatted(healthSnapshot.memoryTotalGB)) GB",
detail: AtlasL10n.string("overview.snapshot.memory.detail"),
tone: healthSnapshot.memoryUsedGB / max(healthSnapshot.memoryTotalGB, 1) > 0.75 ? .warning : .neutral,
systemImage: "memorychip"
)
AtlasMetricCard(
title: AtlasL10n.string("overview.snapshot.disk.title"),
value: "\(formatted(healthSnapshot.diskUsedPercent))%",
detail: AtlasL10n.string("overview.snapshot.disk.detail", formatted(healthSnapshot.diskUsedGB), formatted(healthSnapshot.diskTotalGB)),
tone: healthSnapshot.diskUsedPercent > 80 ? .warning : .success,
systemImage: "internaldrive"
)
AtlasMetricCard(
title: AtlasL10n.string("overview.snapshot.uptime.title"),
value: "\(formatted(healthSnapshot.uptimeDays)) \(AtlasL10n.string("common.days"))",
detail: AtlasL10n.string("overview.snapshot.uptime.detail"),
tone: .neutral,
systemImage: "clock"
)
}
// MARK: - Quick actions bar
quickActionsBar
AtlasCallout(
title: healthSnapshot.diskUsedPercent > 80 ? AtlasL10n.string("overview.snapshot.callout.warning.title") : AtlasL10n.string("overview.snapshot.callout.ok.title"),
detail: healthSnapshot.diskUsedPercent > 80
? AtlasL10n.string("overview.snapshot.callout.warning.detail")
: AtlasL10n.string("overview.snapshot.callout.ok.detail"),
tone: healthSnapshot.diskUsedPercent > 80 ? .warning : .success,
systemImage: healthSnapshot.diskUsedPercent > 80 ? "exclamationmark.triangle.fill" : "checkmark.circle.fill"
)
VStack(alignment: .leading, spacing: AtlasSpacing.md) {
ForEach(Array(healthSnapshot.optimizations.prefix(4))) { optimization in
AtlasDetailRow(
title: optimization.name,
subtitle: optimization.detail,
footnote: AtlasL10n.localizedCategory(optimization.category).capitalized,
systemImage: optimization.isSafe ? "checkmark.shield" : "slider.horizontal.3",
tone: optimization.isSafe ? .success : .warning
) {
AtlasStatusChip(optimization.isSafe ? AtlasL10n.string("risk.safe") : AtlasL10n.string("risk.review"), tone: optimization.isSafe ? .success : .warning)
}
}
}
} else {
AtlasEmptyState(
title: AtlasL10n.string("overview.snapshot.empty.title"),
detail: AtlasL10n.string("overview.snapshot.empty.detail"),
systemImage: "waveform.path.ecg",
tone: .warning
)
}
}
// MARK: - System Snapshot (flattened no InfoCard wrapper)
systemSnapshotSection
// MARK: - Recommended Actions
AtlasInfoCard(
title: AtlasL10n.string("overview.actions.title"),
subtitle: AtlasL10n.string("overview.actions.subtitle")
@@ -138,7 +97,7 @@ public struct OverviewFeatureView: View {
title: finding.title,
subtitle: finding.detail,
footnote: "\(AtlasL10n.localizedCategory(finding.category))\(riskSupport(for: finding.risk))",
systemImage: icon(for: finding.category),
systemImage: AtlasCategoryIcon.systemImage(for: finding.category),
tone: finding.risk.atlasTone
) {
VStack(alignment: .trailing, spacing: AtlasSpacing.sm) {
@@ -149,10 +108,24 @@ public struct OverviewFeatureView: View {
}
}
}
if snapshot.findings.count > 4, let onNavigateToSmartClean {
Button {
onNavigateToSmartClean()
} label: {
Label(
AtlasL10n.string("overview.actions.viewAll", snapshot.findings.count),
systemImage: "arrow.right.circle"
)
}
.buttonStyle(.atlasGhost)
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
}
// MARK: - Recent Activity
AtlasInfoCard(
title: AtlasL10n.string("overview.activity.title"),
subtitle: AtlasL10n.string("overview.activity.subtitle")
@@ -171,18 +144,193 @@ public struct OverviewFeatureView: View {
title: taskRun.kind.title,
subtitle: taskRun.summary,
footnote: timelineFootnote(for: taskRun),
systemImage: icon(for: taskRun.kind),
systemImage: taskRun.kind.atlasSystemImage,
tone: taskRun.status.atlasTone
) {
AtlasStatusChip(taskRun.status.title, tone: taskRun.status.atlasTone)
}
}
if snapshot.taskRuns.count > 3, let onNavigateToHistory {
Button {
onNavigateToHistory()
} label: {
Label(
AtlasL10n.string("overview.activity.viewAll"),
systemImage: "arrow.right.circle"
)
}
.buttonStyle(.atlasGhost)
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
}
}
}
// MARK: - Quick Actions Bar
@ViewBuilder
private var quickActionsBar: some View {
ViewThatFits(in: .horizontal) {
HStack(spacing: AtlasSpacing.lg) {
quickActionButtons
}
VStack(spacing: AtlasSpacing.md) {
quickActionButtons
}
}
}
@ViewBuilder
private var quickActionButtons: some View {
if let onStartSmartClean {
Button {
onStartSmartClean()
} label: {
Label(
AtlasL10n.string("overview.action.smartClean"),
systemImage: AtlasIcon.smartClean
)
}
.buttonStyle(.atlasPrimary)
.accessibilityHint(AtlasL10n.string("overview.action.smartClean.hint"))
}
if !requiredPermissionsReady, let onNavigateToPermissions {
Button {
onNavigateToPermissions()
} label: {
Label(
AtlasL10n.string("overview.action.permissions"),
systemImage: AtlasIcon.permissions
)
}
.buttonStyle(.atlasSecondary)
.accessibilityHint(AtlasL10n.string("overview.action.permissions.hint"))
}
}
// MARK: - System Snapshot Section (flattened)
@ViewBuilder
private var systemSnapshotSection: some View {
VStack(alignment: .leading, spacing: AtlasSpacing.xl) {
// Section header
VStack(alignment: .leading, spacing: AtlasSpacing.xs) {
Text(AtlasL10n.string("overview.snapshot.title"))
.font(AtlasTypography.sectionTitle)
Text(AtlasL10n.string("overview.snapshot.subtitle"))
.font(AtlasTypography.body)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
if isRefreshingHealthSnapshot, snapshot.healthSnapshot == nil {
AtlasLoadingState(
title: AtlasL10n.string("overview.snapshot.loading.title"),
detail: AtlasL10n.string("overview.snapshot.loading.detail")
)
} else if let healthSnapshot = snapshot.healthSnapshot {
LazyVGrid(columns: adaptiveColumns, spacing: AtlasSpacing.lg) {
AtlasMetricCard(
title: AtlasL10n.string("overview.snapshot.memory.title"),
value: "\(formatted(healthSnapshot.memoryUsedGB))/\(formatted(healthSnapshot.memoryTotalGB)) GB",
detail: AtlasL10n.string("overview.snapshot.memory.detail"),
tone: healthSnapshot.memoryUsedGB / max(healthSnapshot.memoryTotalGB, 1) > 0.75 ? .warning : .neutral,
systemImage: "memorychip"
)
AtlasMetricCard(
title: AtlasL10n.string("overview.snapshot.disk.title"),
value: "\(formatted(healthSnapshot.diskUsedPercent))%",
detail: AtlasL10n.string("overview.snapshot.disk.detail", formatted(healthSnapshot.diskUsedGB), formatted(healthSnapshot.diskTotalGB)),
tone: healthSnapshot.diskUsedPercent > 80 ? .warning : .success,
systemImage: "internaldrive"
)
AtlasMetricCard(
title: AtlasL10n.string("overview.snapshot.uptime.title"),
value: "\(formatted(healthSnapshot.uptimeDays)) \(AtlasL10n.string("common.days"))",
detail: AtlasL10n.string("overview.snapshot.uptime.detail"),
tone: .neutral,
systemImage: "clock"
)
}
AtlasCallout(
title: healthSnapshot.diskUsedPercent > 80 ? AtlasL10n.string("overview.snapshot.callout.warning.title") : AtlasL10n.string("overview.snapshot.callout.ok.title"),
detail: healthSnapshot.diskUsedPercent > 80
? AtlasL10n.string("overview.snapshot.callout.warning.detail")
: AtlasL10n.string("overview.snapshot.callout.ok.detail"),
tone: healthSnapshot.diskUsedPercent > 80 ? .warning : .success,
systemImage: healthSnapshot.diskUsedPercent > 80 ? "exclamationmark.triangle.fill" : "checkmark.circle.fill"
)
VStack(alignment: .leading, spacing: AtlasSpacing.md) {
let optimizations = healthSnapshot.optimizations
let visibleOptimizations = showAllOptimizations ? optimizations : Array(optimizations.prefix(4))
ForEach(Array(visibleOptimizations)) { optimization in
AtlasDetailRow(
title: optimization.name,
subtitle: optimization.detail,
footnote: AtlasL10n.localizedCategory(optimization.category).capitalized,
systemImage: optimization.isSafe ? "checkmark.shield" : "slider.horizontal.3",
tone: optimization.isSafe ? .success : .warning
) {
AtlasStatusChip(optimization.isSafe ? AtlasL10n.string("risk.safe") : AtlasL10n.string("risk.review"), tone: optimization.isSafe ? .success : .warning)
}
}
if optimizations.count > 4 {
Button {
withAnimation(AtlasMotion.standard) {
showAllOptimizations.toggle()
}
} label: {
Label(
showAllOptimizations
? AtlasL10n.string("overview.snapshot.collapseOptimizations")
: AtlasL10n.string("overview.snapshot.viewAllOptimizations", optimizations.count),
systemImage: showAllOptimizations ? "chevron.up" : "chevron.down"
)
}
.buttonStyle(.atlasGhost)
.frame(maxWidth: .infinity, alignment: .center)
}
}
} else {
AtlasEmptyState(
title: AtlasL10n.string("overview.snapshot.empty.title"),
detail: AtlasL10n.string("overview.snapshot.empty.detail"),
systemImage: "waveform.path.ecg",
tone: .warning
)
}
}
}
// MARK: - Adaptive Columns
private var adaptiveColumns: [GridItem] {
AtlasLayout.adaptiveMetricColumns(for: contentWidth)
}
private var secondaryColumns: [GridItem] {
contentWidth >= 420
? [
GridItem(.flexible(minimum: 180), spacing: AtlasSpacing.lg),
GridItem(.flexible(minimum: 180), spacing: AtlasSpacing.lg),
]
: [
GridItem(.flexible(minimum: 180), spacing: AtlasSpacing.lg),
]
}
// MARK: - Computed Properties
private var requiredPermissionStates: [PermissionState] {
snapshot.permissions.filter { $0.kind.isRequiredForCurrentWorkflows }
}
@@ -238,61 +386,4 @@ public struct OverviewFeatureView: View {
return AtlasL10n.string("overview.activity.timeline.running", start)
}
private func icon(for category: String) -> String {
switch category.lowercased() {
case "developer":
return "hammer"
case "system":
return "gearshape.2"
case "apps":
return "square.stack.3d.up"
case "browsers":
return "globe"
default:
return "sparkles"
}
}
private func icon(for kind: TaskKind) -> String {
switch kind {
case .scan:
return "sparkles"
case .executePlan:
return "play.circle"
case .uninstallApp:
return "trash"
case .restore:
return "arrow.uturn.backward.circle"
case .inspectPermissions:
return "lock.shield"
}
}
}
private extension RiskLevel {
var atlasTone: AtlasTone {
switch self {
case .safe:
return .success
case .review:
return .warning
case .advanced:
return .danger
}
}
}
private extension TaskStatus {
var atlasTone: AtlasTone {
switch self {
case .queued:
return .neutral
case .running:
return .warning
case .completed:
return .success
case .failed, .cancelled:
return .danger
}
}
}

View File

@@ -16,6 +16,8 @@ public struct SmartCleanFeatureView: View {
private let onRefreshPreview: () -> Void
private let onExecutePlan: () -> Void
@State private var showExecuteConfirmation = false
public init(
findings: [Finding] = AtlasScaffoldFixtures.findings,
plan: ActionPlan = AtlasScaffoldFixtures.actionPlan,
@@ -77,14 +79,13 @@ public struct SmartCleanFeatureView: View {
if scanProgress > 0 {
ProgressView(value: max(scanProgress, 0), total: 1)
.controlSize(.large)
.tint(AtlasColor.brand)
}
AtlasCallout(
title: primaryAction.title,
detail: primaryAction.detail,
tone: primaryAction.tone,
systemImage: primaryAction.systemImage
)
Text(primaryAction.detail)
.font(AtlasTypography.body)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
ViewThatFits(in: .horizontal) {
HStack(alignment: .center, spacing: AtlasSpacing.md) {
@@ -159,21 +160,25 @@ public struct SmartCleanFeatureView: View {
)
}
AtlasCallout(
title: manualReviewCount == 0 ? AtlasL10n.string("smartclean.preview.callout.safe.title") : AtlasL10n.string("smartclean.preview.callout.review.title"),
detail: manualReviewCount == 0
? AtlasL10n.string("smartclean.preview.callout.safe.detail")
: AtlasL10n.string("smartclean.preview.callout.review.detail"),
tone: manualReviewCount == 0 ? .success : .warning,
systemImage: manualReviewCount == 0 ? "checkmark.shield.fill" : "exclamationmark.triangle.fill"
)
if plan.items.isEmpty || manualReviewCount > 0 {
AtlasCallout(
title: manualReviewCount == 0 ? AtlasL10n.string("smartclean.preview.callout.safe.title") : AtlasL10n.string("smartclean.preview.callout.review.title"),
detail: manualReviewCount == 0
? AtlasL10n.string("smartclean.preview.callout.safe.detail")
: AtlasL10n.string("smartclean.preview.callout.review.detail"),
tone: manualReviewCount == 0 ? .success : .warning,
systemImage: manualReviewCount == 0 ? "checkmark.shield.fill" : "exclamationmark.triangle.fill"
)
}
if plan.items.isEmpty {
AtlasEmptyState(
title: AtlasL10n.string("smartclean.preview.empty.title"),
detail: AtlasL10n.string("smartclean.preview.empty.detail"),
systemImage: "list.bullet.clipboard",
tone: .neutral
tone: .neutral,
actionTitle: AtlasL10n.string("emptystate.action.startScan"),
onAction: onStartScan
)
} else {
VStack(alignment: .leading, spacing: AtlasSpacing.md) {
@@ -182,7 +187,7 @@ public struct SmartCleanFeatureView: View {
title: item.title,
subtitle: item.detail,
footnote: supportText(for: item.kind),
systemImage: icon(for: item.kind),
systemImage: item.kind.atlasSystemImage,
tone: item.recoverable ? .success : .warning
) {
VStack(alignment: .trailing, spacing: AtlasSpacing.xs) {
@@ -205,7 +210,9 @@ public struct SmartCleanFeatureView: View {
title: AtlasL10n.string("smartclean.empty.title"),
detail: AtlasL10n.string("smartclean.empty.detail"),
systemImage: "sparkles.tv",
tone: .neutral
tone: .neutral,
actionTitle: AtlasL10n.string("emptystate.action.startScan"),
onAction: onStartScan
)
} else {
ForEach(RiskLevel.allCases, id: \.self) { risk in
@@ -231,7 +238,7 @@ public struct SmartCleanFeatureView: View {
title: finding.title,
subtitle: finding.detail,
footnote: "\(AtlasL10n.localizedCategory(finding.category))\(actionExpectation(for: finding.risk))",
systemImage: icon(for: finding.category),
systemImage: AtlasCategoryIcon.systemImage(for: finding.category),
tone: risk.atlasTone
) {
VStack(alignment: .trailing, spacing: AtlasSpacing.sm) {
@@ -381,8 +388,16 @@ public struct SmartCleanFeatureView: View {
return .refresh
}
private func primaryActionTapped() {
if primaryAction == .execute {
showExecuteConfirmation = true
} else {
primaryAction.handler(startScan: onStartScan, refreshPreview: onRefreshPreview, executePlan: onExecutePlan)()
}
}
private var primaryActionButton: some View {
Button(action: primaryAction.handler(startScan: onStartScan, refreshPreview: onRefreshPreview, executePlan: onExecutePlan)) {
Button(action: primaryActionTapped) {
Label(primaryAction.buttonTitle, systemImage: primaryAction.buttonSystemImage)
}
.buttonStyle(.atlasPrimary)
@@ -390,6 +405,18 @@ public struct SmartCleanFeatureView: View {
.disabled(primaryAction.isDisabled(canExecutePlan: canExecutePlan))
.accessibilityIdentifier(primaryAction.accessibilityIdentifier)
.accessibilityHint(primaryAction.accessibilityHint)
.confirmationDialog(
AtlasL10n.string("smartclean.confirm.execute.title"),
isPresented: $showExecuteConfirmation,
titleVisibility: .visible
) {
Button(AtlasL10n.string("smartclean.action.execute"), role: .destructive) {
onExecutePlan()
}
Button(AtlasL10n.string("confirm.cancel"), role: .cancel) {}
} message: {
Text(AtlasL10n.string("smartclean.confirm.execute.message"))
}
}
@ViewBuilder
@@ -451,33 +478,6 @@ public struct SmartCleanFeatureView: View {
}
}
private func icon(for kind: ActionItem.Kind) -> String {
switch kind {
case .removeCache:
return "trash"
case .removeApp:
return "app.badge.minus"
case .archiveFile:
return "archivebox"
case .inspectPermission:
return "lock.shield"
}
}
private func icon(for category: String) -> String {
switch category.lowercased() {
case "developer":
return "hammer"
case "system":
return "gearshape.2"
case "apps":
return "square.stack.3d.up"
case "browsers":
return "globe"
default:
return "sparkles"
}
}
}
private enum SmartCleanPrimaryAction: Equatable {
@@ -596,15 +596,3 @@ private enum SmartCleanPrimaryAction: Equatable {
}
}
private extension RiskLevel {
var atlasTone: AtlasTone {
switch self {
case .safe:
return .success
case .review:
return .warning
case .advanced:
return .danger
}
}
}