Skip to content

Commit e5319bc

Browse files
committed
feat: Add sidebar visibility toggles for Tags and Worktrees
Persist the new settings in user defaults, cloud settings documents, and the settings snapshot with sync conflict display and reset defaults.
1 parent 52877b4 commit e5319bc

8 files changed

Lines changed: 146 additions & 18 deletions

File tree

‎macgit/App/AppState.swift‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ final class AppState: ObservableObject {
2626
private static let showWorkspaceReflogKey = "showWorkspaceReflog"
2727
private static let showWorkspacePullRequestsKey = "showWorkspacePullRequests"
2828
private static let showWorkspaceGitLFSKey = "showWorkspaceGitLFS"
29+
private static let showTagsKey = "showTags"
30+
private static let showWorktreesKey = "showWorktrees"
2931
private static let showSubmodulesKey = "showSubmodules"
3032
private static let showSubtreesKey = "showSubtrees"
3133
private static let showHeaderBranchButtonKey = "showHeaderBranchButton"
@@ -99,6 +101,22 @@ final class AppState: ObservableObject {
99101
}
100102
}
101103
}
104+
@Published var showTags: Bool {
105+
didSet {
106+
userDefaults.set(showTags, forKey: Self.showTagsKey)
107+
if !isApplyingSnapshot {
108+
currentSettingsSnapshot = snapshot
109+
}
110+
}
111+
}
112+
@Published var showWorktrees: Bool {
113+
didSet {
114+
userDefaults.set(showWorktrees, forKey: Self.showWorktreesKey)
115+
if !isApplyingSnapshot {
116+
currentSettingsSnapshot = snapshot
117+
}
118+
}
119+
}
102120
@Published var showSubmodules: Bool {
103121
didSet {
104122
userDefaults.set(showSubmodules, forKey: Self.showSubmodulesKey)
@@ -262,6 +280,8 @@ final class AppState: ObservableObject {
262280
let showWorkspaceReflog = userDefaults.object(forKey: Self.showWorkspaceReflogKey) as? Bool ?? true
263281
let showWorkspacePullRequests = userDefaults.object(forKey: Self.showWorkspacePullRequestsKey) as? Bool ?? true
264282
let showWorkspaceGitLFS = userDefaults.object(forKey: Self.showWorkspaceGitLFSKey) as? Bool ?? true
283+
let showTags = userDefaults.object(forKey: Self.showTagsKey) as? Bool ?? true
284+
let showWorktrees = userDefaults.object(forKey: Self.showWorktreesKey) as? Bool ?? true
265285
let showSubmodules = userDefaults.object(forKey: Self.showSubmodulesKey) as? Bool ?? false
266286
let showSubtrees = userDefaults.object(forKey: Self.showSubtreesKey) as? Bool ?? false
267287
let showHeaderBranchButton = userDefaults.object(forKey: Self.showHeaderBranchButtonKey) as? Bool ?? true
@@ -294,6 +314,8 @@ final class AppState: ObservableObject {
294314
showWorkspaceReflog: showWorkspaceReflog,
295315
showWorkspacePullRequests: showWorkspacePullRequests,
296316
showWorkspaceGitLFS: showWorkspaceGitLFS,
317+
showTags: showTags,
318+
showWorktrees: showWorktrees,
297319
showSubmodules: showSubmodules,
298320
showSubtrees: showSubtrees,
299321
showHeaderBranchButton: showHeaderBranchButton,
@@ -317,6 +339,8 @@ final class AppState: ObservableObject {
317339
self.showWorkspaceReflog = showWorkspaceReflog
318340
self.showWorkspacePullRequests = showWorkspacePullRequests
319341
self.showWorkspaceGitLFS = showWorkspaceGitLFS
342+
self.showTags = showTags
343+
self.showWorktrees = showWorktrees
320344
self.showSubmodules = showSubmodules
321345
self.showSubtrees = showSubtrees
322346
self.showHeaderBranchButton = showHeaderBranchButton
@@ -347,6 +371,8 @@ final class AppState: ObservableObject {
347371
showWorkspaceReflog: showWorkspaceReflog,
348372
showWorkspacePullRequests: showWorkspacePullRequests,
349373
showWorkspaceGitLFS: showWorkspaceGitLFS,
374+
showTags: showTags,
375+
showWorktrees: showWorktrees,
350376
showSubmodules: showSubmodules,
351377
showSubtrees: showSubtrees,
352378
showHeaderBranchButton: showHeaderBranchButton,
@@ -375,6 +401,8 @@ final class AppState: ObservableObject {
375401
showWorkspaceReflog = snapshot.showWorkspaceReflog
376402
showWorkspacePullRequests = snapshot.showWorkspacePullRequests
377403
showWorkspaceGitLFS = snapshot.showWorkspaceGitLFS
404+
showTags = snapshot.showTags
405+
showWorktrees = snapshot.showWorktrees
378406
showSubmodules = snapshot.showSubmodules
379407
showSubtrees = snapshot.showSubtrees
380408
showHeaderBranchButton = snapshot.showHeaderBranchButton

‎macgit/Models/AppSettingsSnapshot.swift‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ struct AppSettingsSnapshot: Codable, Equatable, Sendable {
2626
var showWorkspaceReflog: Bool
2727
var showWorkspacePullRequests: Bool
2828
var showWorkspaceGitLFS: Bool
29+
var showTags: Bool
30+
var showWorktrees: Bool
2931
var showSubmodules: Bool
3032
var showSubtrees: Bool
3133
var showHeaderBranchButton: Bool
@@ -49,6 +51,8 @@ struct AppSettingsSnapshot: Codable, Equatable, Sendable {
4951
showWorkspaceReflog: Bool = true,
5052
showWorkspacePullRequests: Bool = true,
5153
showWorkspaceGitLFS: Bool = true,
54+
showTags: Bool = true,
55+
showWorktrees: Bool = true,
5256
showSubmodules: Bool,
5357
showSubtrees: Bool,
5458
showHeaderBranchButton: Bool = true,
@@ -72,6 +76,8 @@ struct AppSettingsSnapshot: Codable, Equatable, Sendable {
7276
self.showWorkspaceReflog = showWorkspaceReflog
7377
self.showWorkspacePullRequests = showWorkspacePullRequests
7478
self.showWorkspaceGitLFS = showWorkspaceGitLFS
79+
self.showTags = showTags
80+
self.showWorktrees = showWorktrees
7581
self.showSubmodules = showSubmodules
7682
self.showSubtrees = showSubtrees
7783
self.showHeaderBranchButton = showHeaderBranchButton
@@ -98,6 +104,8 @@ struct AppSettingsSnapshot: Codable, Equatable, Sendable {
98104
showWorkspaceReflog = try container.decodeIfPresent(Bool.self, forKey: .showWorkspaceReflog) ?? true
99105
showWorkspacePullRequests = try container.decodeIfPresent(Bool.self, forKey: .showWorkspacePullRequests) ?? true
100106
showWorkspaceGitLFS = try container.decodeIfPresent(Bool.self, forKey: .showWorkspaceGitLFS) ?? true
107+
showTags = try container.decodeIfPresent(Bool.self, forKey: .showTags) ?? true
108+
showWorktrees = try container.decodeIfPresent(Bool.self, forKey: .showWorktrees) ?? true
101109
showSubmodules = try container.decode(Bool.self, forKey: .showSubmodules)
102110
showSubtrees = try container.decode(Bool.self, forKey: .showSubtrees)
103111
showHeaderBranchButton = try container.decode(Bool.self, forKey: .showHeaderBranchButton)

‎macgit/Services/FirestoreSettingsStore.swift‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ enum CloudSettingsDocument {
3232
"showWorkspaceReflog": snapshot.showWorkspaceReflog,
3333
"showWorkspacePullRequests": snapshot.showWorkspacePullRequests,
3434
"showWorkspaceGitLFS": snapshot.showWorkspaceGitLFS,
35+
"showTags": snapshot.showTags,
36+
"showWorktrees": snapshot.showWorktrees,
3537
"showSubmodules": snapshot.showSubmodules,
3638
"showSubtrees": snapshot.showSubtrees,
3739
"showHeaderBranchButton": snapshot.showHeaderBranchButton,
@@ -81,6 +83,8 @@ enum CloudSettingsDocument {
8183
showWorkspaceReflog: data["showWorkspaceReflog"] as? Bool ?? true,
8284
showWorkspacePullRequests: data["showWorkspacePullRequests"] as? Bool ?? true,
8385
showWorkspaceGitLFS: data["showWorkspaceGitLFS"] as? Bool ?? true,
86+
showTags: data["showTags"] as? Bool ?? true,
87+
showWorktrees: data["showWorktrees"] as? Bool ?? true,
8488
showSubmodules: showSubmodules,
8589
showSubtrees: showSubtrees,
8690
showHeaderBranchButton: data["showHeaderBranchButton"] as? Bool ?? true,

‎macgit/Views/Account/ManageAccountSheet.swift‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ struct SettingsSyncConflictSheet: View {
221221
settingRow("Workspace: Pull Requests", enabled: snapshot.showWorkspacePullRequests)
222222
settingRow("Workspace: Git LFS", enabled: snapshot.showWorkspaceGitLFS)
223223
settingRow("Workspace: Git Flow", enabled: snapshot.showGitFlow)
224+
settingRow("Tags section", enabled: snapshot.showTags)
225+
settingRow("Worktrees section", enabled: snapshot.showWorktrees)
224226
settingRow("Submodules", enabled: snapshot.showSubmodules)
225227
settingRow("Subtrees", enabled: snapshot.showSubtrees)
226228
settingRow("Header: Branch", enabled: snapshot.showHeaderBranchButton)

‎macgit/Views/Common/GeneralSettingsView.swift‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ struct GeneralSettingsView: View {
3434
}
3535

3636
Section {
37+
SettingsToggleRow(
38+
title: "Show Tags",
39+
detail: "Include tags as a dedicated section in the repository sidebar.",
40+
isOn: $appState.showTags
41+
)
42+
43+
SettingsToggleRow(
44+
title: "Show Worktrees",
45+
detail: "Include worktrees as a dedicated section in the repository sidebar.",
46+
isOn: $appState.showWorktrees
47+
)
48+
3749
SettingsToggleRow(
3850
title: "Show Git Flow",
3951
detail: "Include Git Flow in the Workspace section of the repository sidebar.",
@@ -128,6 +140,8 @@ struct GeneralSettingsView: View {
128140
appState.showWorkspaceReflog = true
129141
appState.showWorkspacePullRequests = true
130142
appState.showWorkspaceGitLFS = true
143+
appState.showTags = true
144+
appState.showWorktrees = true
131145
appState.showSubmodules = false
132146
appState.showSubtrees = false
133147
appState.historyIncludeRemotes = false

‎macgit/Views/MainWindow/SidebarView.swift‎

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,12 @@ struct SidebarView: View {
756756
private var sidebarContextMenu: some View {
757757
sidebarCreationMenu
758758
Divider()
759+
Toggle(isOn: $appState.showTags) {
760+
Label("Show Tags", systemImage: "tag")
761+
}
762+
Toggle(isOn: $appState.showWorktrees) {
763+
Label("Show Worktrees", systemImage: "rectangle.3.group")
764+
}
759765
Toggle(isOn: $appState.showSubmodules) {
760766
Label("Show Submodules", systemImage: "folder.badge.gearshape")
761767
}
@@ -802,25 +808,29 @@ struct SidebarView: View {
802808
actions: branchSectionActions
803809
)
804810

805-
SidebarWorktreesSection(
806-
currentRepositoryURL: repositoryURL,
807-
entries: worktreeEntries,
808-
isExpanded: sectionStates.worktreesExpanded,
809-
isLoading: isLoadingWorktrees,
810-
onOpenInNewWindow: onRequestOpenWorktree,
811-
actions: worktreeSectionActions
812-
)
811+
if appState.showWorktrees {
812+
SidebarWorktreesSection(
813+
currentRepositoryURL: repositoryURL,
814+
entries: worktreeEntries,
815+
isExpanded: sectionStates.worktreesExpanded,
816+
isLoading: isLoadingWorktrees,
817+
onOpenInNewWindow: onRequestOpenWorktree,
818+
actions: worktreeSectionActions
819+
)
820+
}
813821

814-
SidebarTagsSection(
815-
rows: visibleTagRows,
816-
isExpanded: sectionStates.tagsExpanded,
817-
isLoading: isLoadingTags,
818-
expandedFolders: expandedTagFolders,
819-
remoteNames: remoteNames,
820-
isHeaderDropTargeted: activeDropTarget == .tagsHeader,
821-
activeDropLabel: activeDropTarget == .tagsHeader ? activeDropLabel : nil,
822-
actions: tagSectionActions
823-
)
822+
if appState.showTags {
823+
SidebarTagsSection(
824+
rows: visibleTagRows,
825+
isExpanded: sectionStates.tagsExpanded,
826+
isLoading: isLoadingTags,
827+
expandedFolders: expandedTagFolders,
828+
remoteNames: remoteNames,
829+
isHeaderDropTargeted: activeDropTarget == .tagsHeader,
830+
activeDropLabel: activeDropTarget == .tagsHeader ? activeDropLabel : nil,
831+
actions: tagSectionActions
832+
)
833+
}
824834

825835
SidebarRemotesSection(
826836
rows: visibleRemoteRows,

‎macgitTests/AppSettingsSnapshotTests.swift‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ final class AppSettingsSnapshotTests: XCTestCase {
2929
showWorkspaceReflog: false,
3030
showWorkspacePullRequests: true,
3131
showWorkspaceGitLFS: false,
32+
showTags: false,
33+
showWorktrees: true,
3234
showSubmodules: true,
3335
showSubtrees: true,
3436
showHeaderBranchButton: false,
@@ -61,6 +63,8 @@ final class AppSettingsSnapshotTests: XCTestCase {
6163
"showWorkspaceReflog",
6264
"showWorkspacePullRequests",
6365
"showWorkspaceGitLFS",
66+
"showTags",
67+
"showWorktrees",
6468
"showSubmodules",
6569
"showSubtrees",
6670
"showHeaderBranchButton",
@@ -124,6 +128,28 @@ final class AppSettingsSnapshotTests: XCTestCase {
124128
XCTAssertTrue(decoded.showWorkspaceGitLFS)
125129
}
126130

131+
func testSnapshotDecodingDefaultsMissingTagsAndWorktreesVisibilityToTrue() throws {
132+
let snapshot = AppSettingsSnapshot(
133+
showToolbarButtonText: true,
134+
showTags: false,
135+
showWorktrees: false,
136+
showSubmodules: false,
137+
showSubtrees: false
138+
)
139+
let encoded = try JSONEncoder().encode(snapshot)
140+
var object = try XCTUnwrap(JSONSerialization.jsonObject(with: encoded) as? [String: Any])
141+
object.removeValue(forKey: "showTags")
142+
object.removeValue(forKey: "showWorktrees")
143+
144+
let decoded = try JSONDecoder().decode(
145+
AppSettingsSnapshot.self,
146+
from: JSONSerialization.data(withJSONObject: object)
147+
)
148+
149+
XCTAssertTrue(decoded.showTags)
150+
XCTAssertTrue(decoded.showWorktrees)
151+
}
152+
127153
func testAppStateApplyChangesOnlyApprovedSettings() {
128154
let suiteName = "AppSettingsSnapshotTests.\(UUID().uuidString)"
129155
let defaults = UserDefaults(suiteName: suiteName)!
@@ -219,6 +245,25 @@ final class AppSettingsSnapshotTests: XCTestCase {
219245
XCTAssertFalse(reloaded.snapshot.showWorkspaceGitLFS)
220246
}
221247

248+
func testTagsAndWorktreesVisibilityDefaultsOnAndPersistsLocally() {
249+
let suiteName = "AppSettingsSnapshotTests.\(UUID().uuidString)"
250+
let defaults = UserDefaults(suiteName: suiteName)!
251+
defer { defaults.removePersistentDomain(forName: suiteName) }
252+
253+
let state = AppState(userDefaults: defaults)
254+
XCTAssertTrue(state.showTags)
255+
XCTAssertTrue(state.showWorktrees)
256+
257+
state.showTags = false
258+
state.showWorktrees = false
259+
260+
let reloaded = AppState(userDefaults: defaults)
261+
XCTAssertFalse(reloaded.showTags)
262+
XCTAssertFalse(reloaded.showWorktrees)
263+
XCTAssertFalse(reloaded.snapshot.showTags)
264+
XCTAssertFalse(reloaded.snapshot.showWorktrees)
265+
}
266+
222267
func testSearchFilterIsDeviceLocalAndPersisted() {
223268
let suiteName = "AppSettingsSnapshotTests.\(UUID().uuidString)"
224269
let defaults = UserDefaults(suiteName: suiteName)!

‎macgitTests/CloudSettingsDocumentTests.swift‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ final class CloudSettingsDocumentTests: XCTestCase {
2828
showWorkspaceReflog: false,
2929
showWorkspacePullRequests: true,
3030
showWorkspaceGitLFS: false,
31+
showTags: false,
32+
showWorktrees: true,
3133
showSubmodules: true,
3234
showSubtrees: false,
3335
showHeaderBranchButton: true,
@@ -60,6 +62,8 @@ final class CloudSettingsDocumentTests: XCTestCase {
6062
"showWorkspaceReflog",
6163
"showWorkspacePullRequests",
6264
"showWorkspaceGitLFS",
65+
"showTags",
66+
"showWorktrees",
6367
"showSubmodules",
6468
"showSubtrees",
6569
"showHeaderBranchButton",
@@ -85,6 +89,8 @@ final class CloudSettingsDocumentTests: XCTestCase {
8589
XCTAssertEqual(document["showWorkspaceReflog"] as? Bool, false)
8690
XCTAssertEqual(document["showWorkspacePullRequests"] as? Bool, true)
8791
XCTAssertEqual(document["showWorkspaceGitLFS"] as? Bool, false)
92+
XCTAssertEqual(document["showTags"] as? Bool, false)
93+
XCTAssertEqual(document["showWorktrees"] as? Bool, true)
8894
XCTAssertEqual(document["showSubmodules"] as? Bool, true)
8995
XCTAssertEqual(document["showSubtrees"] as? Bool, false)
9096
XCTAssertEqual(document["showHeaderBranchButton"] as? Bool, true)
@@ -142,6 +148,17 @@ final class CloudSettingsDocumentTests: XCTestCase {
142148
XCTAssertTrue(decoded.showWorkspaceGitLFS)
143149
}
144150

151+
func testDecodingDefaultsMissingTagsAndWorktreesVisibilityToTrue() throws {
152+
var document = validDocument()
153+
document.removeValue(forKey: "showTags")
154+
document.removeValue(forKey: "showWorktrees")
155+
156+
let decoded = try CloudSettingsDocument.decode(document)
157+
158+
XCTAssertTrue(decoded.showTags)
159+
XCTAssertTrue(decoded.showWorktrees)
160+
}
161+
145162
func testDecodingDefaultsMissingHistoryFilterSettings() throws {
146163
var document = validDocument()
147164
document.removeValue(forKey: "historyBranchFilter")

0 commit comments

Comments
 (0)