Skip to content

Commit 05e1254

Browse files
devsemihclaude
andcommitted
Redesign all sheets with glass grouped sections and consistent UI components
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 708cca3 commit 05e1254

10 files changed

Lines changed: 554 additions & 398 deletions

appjail/Views/AppRowView.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ struct AppRowView: View {
2424
Image(systemName: isBlocked ? "minus.circle.fill" : "plus.circle.fill")
2525
.foregroundStyle(isBlocked ? .red : .green)
2626
.font(.title3)
27+
.frame(width: 32, height: 32)
28+
.contentShape(Rectangle())
2729
}
2830
.buttonStyle(.plain)
2931
}
3032
.padding(.vertical, 4)
3133
.padding(.horizontal, 4)
32-
.background(Color.gray.opacity(0.05), in: RoundedRectangle(cornerRadius: 6))
3334
}
3435
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import SwiftUI
2+
3+
struct NavigationRow: View {
4+
let icon: String
5+
let title: String
6+
var subtitle: String?
7+
let action: () -> Void
8+
9+
var body: some View {
10+
Button(action: action) {
11+
HStack(spacing: 10) {
12+
Image(systemName: icon)
13+
.font(.title3)
14+
.foregroundStyle(.tint)
15+
.frame(width: 28)
16+
VStack(alignment: .leading) {
17+
Text(title)
18+
.fontWeight(.medium)
19+
if let subtitle {
20+
Text(subtitle)
21+
.font(.caption)
22+
.foregroundStyle(.secondary)
23+
}
24+
}
25+
Spacer()
26+
Image(systemName: "chevron.right")
27+
.foregroundStyle(.tertiary)
28+
.font(.caption)
29+
}
30+
.padding(.horizontal, 12)
31+
.padding(.vertical, 10)
32+
.contentShape(Rectangle())
33+
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: 12))
34+
}
35+
.buttonStyle(.plain)
36+
}
37+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import SwiftUI
2+
3+
struct SheetFooter: View {
4+
let leadingTitle: String?
5+
let leadingAction: (() -> Void)?
6+
let trailingTitle: String
7+
let trailingAction: () -> Void
8+
var leadingDisabled: Bool = false
9+
var leadingDestructive: Bool = false
10+
11+
init(
12+
leadingTitle: String? = nil,
13+
leadingAction: (() -> Void)? = nil,
14+
leadingDisabled: Bool = false,
15+
leadingDestructive: Bool = false,
16+
trailingTitle: String,
17+
trailingAction: @escaping () -> Void
18+
) {
19+
self.leadingTitle = leadingTitle
20+
self.leadingAction = leadingAction
21+
self.leadingDisabled = leadingDisabled
22+
self.leadingDestructive = leadingDestructive
23+
self.trailingTitle = trailingTitle
24+
self.trailingAction = trailingAction
25+
}
26+
27+
var body: some View {
28+
HStack {
29+
if let title = leadingTitle, let action = leadingAction {
30+
Button(title, action: action)
31+
.buttonStyle(.bordered)
32+
.controlSize(.small)
33+
.tint(leadingDestructive ? .red : nil)
34+
.disabled(leadingDisabled)
35+
}
36+
Spacer()
37+
Button(trailingTitle, action: trailingAction)
38+
.buttonStyle(.borderedProminent)
39+
.controlSize(.small)
40+
}
41+
.padding(.horizontal)
42+
.padding(.vertical, 10)
43+
}
44+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import SwiftUI
2+
3+
struct SheetHeader: View {
4+
let title: String
5+
var backAction: (() -> Void)?
6+
var trailingText: String?
7+
var trailingIcon: String?
8+
var trailingAction: (() -> Void)?
9+
10+
var body: some View {
11+
HStack {
12+
if let backAction {
13+
Button(action: backAction) {
14+
Image(systemName: "chevron.left")
15+
.font(.body)
16+
.frame(width: 28, height: 28)
17+
.contentShape(Rectangle())
18+
}
19+
.buttonStyle(.plain)
20+
}
21+
Text(title)
22+
.font(.title3)
23+
.fontWeight(.bold)
24+
Spacer()
25+
if let text = trailingText {
26+
Text(text)
27+
.font(.caption)
28+
.foregroundStyle(.secondary)
29+
}
30+
if let icon = trailingIcon, let action = trailingAction {
31+
Button(action: action) {
32+
Image(systemName: icon)
33+
.font(.title3)
34+
.frame(width: 32, height: 32)
35+
.contentShape(Rectangle())
36+
}
37+
.buttonStyle(.plain)
38+
}
39+
}
40+
.padding(.horizontal)
41+
.padding(.top, 16)
42+
.padding(.bottom, 8)
43+
}
44+
}

appjail/Views/KeywordRowView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ struct KeywordRowView: View {
1212
Button(action: onDelete) {
1313
Image(systemName: "minus.circle.fill")
1414
.foregroundStyle(.red)
15+
.frame(width: 32, height: 32)
16+
.contentShape(Rectangle())
1517
}
1618
.buttonStyle(.plain)
1719
}

appjail/Views/Sheets/FocusTimerSheet.swift

Lines changed: 81 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -8,107 +8,106 @@ struct FocusTimerSheet: View {
88

99
var body: some View {
1010
VStack(spacing: 0) {
11-
HStack {
12-
Button(action: onDismiss) {
13-
Image(systemName: "chevron.left")
14-
.font(.body)
15-
}
16-
.buttonStyle(.plain)
17-
Text("Focus Timer")
18-
.font(.headline)
19-
Spacer()
20-
}
21-
.padding()
22-
23-
Divider()
24-
25-
VStack(spacing: 20) {
26-
Spacer()
11+
SheetHeader(title: "Focus Timer")
2712

28-
TimerRingView(
29-
progress: focusTimer.isIdle ? 0 : focusTimer.progress,
30-
label: focusTimer.isIdle
31-
? "\(focusTimer.state.durationSeconds / 60)m"
32-
: focusTimer.formattedRemaining,
33-
size: 140
34-
)
13+
ScrollView {
14+
VStack(spacing: 16) {
15+
// Timer ring in glass section
16+
VStack(spacing: 16) {
17+
TimerRingView(
18+
progress: focusTimer.isIdle ? 0 : focusTimer.progress,
19+
label: focusTimer.isIdle
20+
? "\(focusTimer.state.durationSeconds / 60)m"
21+
: focusTimer.formattedRemaining,
22+
size: 140
23+
)
24+
}
25+
.frame(maxWidth: .infinity)
26+
.padding(.vertical, 16)
27+
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: 12))
3528

36-
if focusTimer.isIdle {
37-
HStack(spacing: 8) {
38-
ForEach(presets, id: \.self) { minutes in
39-
Button("\(minutes)m") {
40-
focusTimer.setDuration(minutes: minutes)
29+
// Presets in glass section (only when idle)
30+
if focusTimer.isIdle {
31+
HStack(spacing: 8) {
32+
ForEach(presets, id: \.self) { minutes in
33+
Button("\(minutes)m") {
34+
focusTimer.setDuration(minutes: minutes)
35+
}
36+
.buttonStyle(.bordered)
37+
.controlSize(.small)
38+
.tint(focusTimer.state.durationSeconds == minutes * 60 ? .accentColor : .gray)
4139
}
42-
.buttonStyle(.bordered)
43-
.controlSize(.small)
44-
.tint(focusTimer.state.durationSeconds == minutes * 60 ? .accentColor : .gray)
4540
}
41+
.frame(maxWidth: .infinity)
42+
.padding(.vertical, 12)
43+
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: 12))
4644
}
47-
}
4845

49-
HStack(spacing: 12) {
50-
if focusTimer.isIdle {
51-
Button("Start") {
52-
focusTimer.start()
53-
}
54-
.buttonStyle(.borderedProminent)
55-
.disabled(focusTimer.state.durationSeconds == 0)
56-
} else if focusTimer.isRunning {
57-
Button("Pause") {
58-
focusTimer.pause()
59-
}
60-
.buttonStyle(.bordered)
46+
// Controls
47+
HStack(spacing: 12) {
48+
if focusTimer.isIdle {
49+
Button("Start") {
50+
focusTimer.start()
51+
}
52+
.buttonStyle(.borderedProminent)
53+
.disabled(focusTimer.state.durationSeconds == 0)
54+
} else if focusTimer.isRunning {
55+
Button("Pause") {
56+
focusTimer.pause()
57+
}
58+
.buttonStyle(.bordered)
59+
60+
if !focusTimer.state.strictMode {
61+
Button("Stop") {
62+
focusTimer.stop()
63+
}
64+
.buttonStyle(.bordered)
65+
.tint(.red)
66+
}
67+
} else if focusTimer.isPaused {
68+
Button("Resume") {
69+
focusTimer.resume()
70+
}
71+
.buttonStyle(.borderedProminent)
6172

62-
if !focusTimer.state.strictMode {
6373
Button("Stop") {
6474
focusTimer.stop()
6575
}
6676
.buttonStyle(.bordered)
6777
.tint(.red)
6878
}
69-
} else if focusTimer.isPaused {
70-
Button("Resume") {
71-
focusTimer.resume()
72-
}
73-
.buttonStyle(.borderedProminent)
74-
75-
Button("Stop") {
76-
focusTimer.stop()
77-
}
78-
.buttonStyle(.bordered)
79-
.tint(.red)
8079
}
81-
}
8280

83-
if focusTimer.isIdle {
84-
Toggle("Strict Mode", isOn: Binding(
85-
get: { focusTimer.state.strictMode },
86-
set: { newValue in
87-
focusTimer.state.strictMode = newValue
88-
}
89-
))
90-
.toggleStyle(.switch)
91-
.controlSize(.small)
92-
.padding(.horizontal, 40)
81+
// Strict mode toggle in glass section (only when idle)
82+
if focusTimer.isIdle {
83+
VStack(spacing: 6) {
84+
Toggle("Strict Mode", isOn: Binding(
85+
get: { focusTimer.state.strictMode },
86+
set: { newValue in
87+
focusTimer.state.strictMode = newValue
88+
}
89+
))
90+
.toggleStyle(.switch)
91+
.controlSize(.small)
9392

94-
Text("Strict mode prevents stopping the timer early")
95-
.font(.caption2)
96-
.foregroundStyle(.tertiary)
93+
Text("Strict mode prevents stopping the timer early")
94+
.font(.caption2)
95+
.foregroundStyle(.tertiary)
96+
.frame(maxWidth: .infinity, alignment: .leading)
97+
}
98+
.padding(.horizontal, 12)
99+
.padding(.vertical, 10)
100+
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: 12))
101+
}
97102
}
98-
99-
Spacer()
103+
.padding(.horizontal)
104+
.padding(.vertical, 8)
100105
}
101-
.padding()
102-
103-
Divider()
104106

105-
HStack {
106-
Spacer()
107-
Button("Done", action: onDismiss)
108-
.buttonStyle(.borderedProminent)
109-
.controlSize(.small)
110-
}
111-
.padding()
107+
SheetFooter(
108+
trailingTitle: "Done",
109+
trailingAction: onDismiss
110+
)
112111
}
113112
}
114113
}

0 commit comments

Comments
 (0)