Skip to content

Commit 09f2a61

Browse files
committed
+ Searching ^ Individual outdated package selection
1 parent 54c6ca2 commit 09f2a61

5 files changed

Lines changed: 235 additions & 43 deletions

File tree

Cork/Localizable.xcstrings

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24839,7 +24839,7 @@
2483924839
"cs" : {
2484024840
"stringUnit" : {
2484124841
"state" : "translated",
24842-
"value" : "Z pípy %@ máte ještě nainstalované balíčky\u2028Odinstalujte je a zkuste to znovu"
24842+
"value" : "Z pípy %@ máte ještě nainstalované balíčky
Odinstalujte je a zkuste to znovu"
2484324843
}
2484424844
},
2484524845
"de" : {
@@ -27731,6 +27731,14 @@
2773127731
},
2773227732
"shouldTranslate" : false
2773327733
},
27734+
"DEBUG: Test View" : {
27735+
"comment" : "A debug-only text that can be used to test the layout of this view.",
27736+
"isCommentAutoGenerated" : true
27737+
},
27738+
"DEBUG: Test Viwe" : {
27739+
"comment" : "A debug label that can be used to test the layout of this view.",
27740+
"isCommentAutoGenerated" : true
27741+
},
2773427742
"debug.action.activate-demo" : {
2773527743
"localizations" : {
2773627744
"cs" : {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// Toggle Search Field.swift
3+
// Cork
4+
//
5+
// Created by David Bureš - P on 07.01.2026.
6+
//
7+
8+
import SwiftUI
9+
10+
struct ToggleSearchFieldButton: View
11+
{
12+
13+
@Binding var isShowingSearchField: Bool
14+
15+
var body: some View
16+
{
17+
Button
18+
{
19+
isShowingSearchField.toggle()
20+
} label: {
21+
Label("action.show-search-field", systemImage: "magnifyingglass")
22+
.labelStyle(.iconOnly)
23+
}
24+
.accessibilityHint(isShowingSearchField ? Text("action.hide-search-field.hint") : Text("action.show-search-field.hint"))
25+
.buttonStyle(.accessoryBar)
26+
}
27+
}

Cork/Views/Start Page/Sub-Views/Outdated Packages/Outdated Packages List/Outdated Packages List Type/Outdated Packages List - List.swift

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,33 +29,75 @@ struct OutdatedPackagesList_List: View
2929
}
3030
}
3131

32-
/// Check whether all relevant packages are deselected
32+
var sortedRelevantPackages: [OutdatedPackage]
33+
{
34+
return relevantPackages.sorted(by: { $0.package.installedOn! < $1.package.installedOn! })
35+
}
36+
37+
var filteredRelevantPackages: [OutdatedPackage]
38+
{
39+
guard !searchText.isEmpty else
40+
{
41+
return sortedRelevantPackages
42+
}
43+
44+
return sortedRelevantPackages.filter({ $0.package.name.localizedCaseInsensitiveContains(searchText) })
45+
}
46+
47+
/// Check whether all relevant packages are deselected - for `Deselect All` button
3348
var areAnyRelevantPackagesSelected: Bool
3449
{
3550
!relevantPackages.filter({ $0.isMarkedForUpdating }).isEmpty
3651
}
3752

53+
/// Check if there is at least one package that is not selected - for `Select All` button
54+
var areAnyPackagesLeftToSelect: Bool
55+
{
56+
return relevantPackages.filter({ !$0.isMarkedForUpdating }).isEmpty
57+
}
58+
59+
@State private var isShowingSearchField: Bool = false
60+
61+
@State private var searchText: String = ""
62+
3863
var body: some View
3964
{
4065
List
4166
{
4267
Section
4368
{
44-
ForEach(relevantPackages.sorted(by: { $0.package.installedOn! < $1.package.installedOn! }))
69+
ForEach(filteredRelevantPackages)
4570
{ outdatedPackage in
4671
Toggle(isOn: Bindable(outdatedPackage).isMarkedForUpdating)
4772
{
4873
OutdatedPackageListBoxRow(outdatedPackage: outdatedPackage)
4974
}
5075
}
5176
} header: {
52-
// TODO: Implement this
53-
HStack(alignment: .center, spacing: 10)
54-
{
55-
deselectAllButton(packagesToDeselect: relevantPackages)
77+
VStack(alignment: .leading, spacing: 5)
78+
{
79+
HStack(alignment: .center, spacing: 10)
80+
{
81+
deselectAllButton(packagesToDeselect: relevantPackages)
5682

57-
selectAllButton(packagesToSelect: relevantPackages)
58-
}
83+
selectAllButton(packagesToSelect: relevantPackages)
84+
85+
Spacer()
86+
87+
ToggleSearchFieldButton(isShowingSearchField: $isShowingSearchField)
88+
}
89+
90+
if isShowingSearchField
91+
{
92+
Divider()
93+
94+
CustomSearchField(
95+
search: $searchText,
96+
customPromptText: nil
97+
)
98+
.transition(.push(from: .top))
99+
}
100+
}
59101
}
60102
}
61103
.listStyle(.bordered(alternatesRowBackgrounds: true))
@@ -73,7 +115,7 @@ struct OutdatedPackagesList_List: View
73115
} label: {
74116
Text("start-page.updated.action.select-all")
75117
}
76-
.disabled(outdatedPackagesTracker.packagesMarkedForUpdating.isEmpty)
118+
.disabled(areAnyPackagesLeftToSelect)
77119
.buttonStyle(.accessoryBar)
78120
}
79121

Cork/Views/Start Page/Sub-Views/Outdated Packages/Outdated Packages List/Outdated Packages List Type/Outdated Packages List - Table.swift

Lines changed: 122 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -29,55 +29,144 @@ struct OutdatedPackagesList_Table: View
2929
}
3030
}
3131

32-
/// Check whether all relevant packages are deselected
32+
var sortedRelevantPackages: [OutdatedPackage]
33+
{
34+
return relevantPackages.sorted(by: { $0.package.installedOn! < $1.package.installedOn! })
35+
}
36+
37+
var filteredRelevantPackages: [OutdatedPackage]
38+
{
39+
guard !searchText.isEmpty else
40+
{
41+
return sortedRelevantPackages
42+
}
43+
44+
return sortedRelevantPackages.filter({ $0.package.name.localizedCaseInsensitiveContains(searchText) })
45+
}
46+
47+
/// Check whether all relevant packages are deselected - for `Deselect All` button
3348
var areAnyRelevantPackagesSelected: Bool
3449
{
3550
!relevantPackages.filter({ $0.isMarkedForUpdating }).isEmpty
3651
}
3752

53+
/// Check if there is at least one package that is not selected - for `Select All` button
54+
var areAnyPackagesLeftToSelect: Bool
55+
{
56+
return relevantPackages.filter({ !$0.isMarkedForUpdating }).isEmpty
57+
}
58+
59+
@State private var isShowingSearchField: Bool = false
60+
61+
@State private var searchText: String = ""
62+
3863
var body: some View
3964
{
40-
Table(of: OutdatedPackage.self)
65+
GroupBox
4166
{
42-
TableColumn("start-page.updates.action")
43-
{ outdatedPackage in
44-
Toggle(isOn: Bindable(outdatedPackage).isMarkedForUpdating) {
45-
EmptyView()
67+
VStack(spacing: 0)
68+
{
69+
VStack(spacing: 2)
70+
{
71+
HStack(alignment: .center)
72+
{
73+
selectAllButton(packagesToSelect: relevantPackages)
74+
75+
deselectAllButton(packagesToDeselect: relevantPackages)
76+
77+
Spacer()
78+
79+
ToggleSearchFieldButton(isShowingSearchField: $isShowingSearchField)
80+
}
81+
82+
if isShowingSearchField
83+
{
84+
VStack(spacing: 2)
85+
{
86+
Divider()
87+
88+
CustomSearchField(search: $searchText, customPromptText: nil)
89+
.animation(.smooth, value: isShowingSearchField)
90+
}
91+
}
4692
}
93+
.padding([.horizontal, .top], 4)
4794
}
48-
.width(45)
95+
96+
Table(of: OutdatedPackage.self)
97+
{
98+
TableColumn("start-page.updates.action")
99+
{ outdatedPackage in
100+
Toggle(isOn: Bindable(outdatedPackage).isMarkedForUpdating) {
101+
EmptyView()
102+
}
103+
}
104+
.width(45)
49105

50-
TableColumn("package-details.dependencies.results.name", value: \.package.name)
106+
TableColumn("package-details.dependencies.results.name", value: \.package.name)
51107

52-
TableColumn("start-page.updates.installed-version")
53-
{ outdatedPackage in
54-
Text(outdatedPackage.installedVersions.formatted(.list(type: .and)))
55-
}
108+
TableColumn("start-page.updates.installed-version")
109+
{ outdatedPackage in
110+
Text(outdatedPackage.installedVersions.formatted(.list(type: .and)))
111+
}
56112

57-
TableColumn("start-page.updates.newest-version")
58-
{ outdatedPackage in
59-
Text(outdatedPackage.newerVersion)
60-
}
113+
TableColumn("start-page.updates.newest-version")
114+
{ outdatedPackage in
115+
Text(outdatedPackage.newerVersion)
116+
}
61117

62-
TableColumn("package-details.type")
63-
{ outdatedPackage in
64-
Text(outdatedPackage.package.type.description)
65-
}
118+
TableColumn("package-details.type")
119+
{ outdatedPackage in
120+
Text(outdatedPackage.package.type.description)
121+
}
66122

67-
} rows: {
68-
ForEach(relevantPackages.sorted(by: { $0.package.installedOn! < $1.package.installedOn! }))
69-
{ outdatedPackage in
70-
TableRow(outdatedPackage)
71-
.contextMenu
72-
{
73-
PreviewPackageButton(packageToPreview: .init(
74-
name: outdatedPackage.package.name,
75-
type: outdatedPackage.package.type,
76-
installedIntentionally: outdatedPackage.package.installedIntentionally)
77-
)
78-
}
123+
} rows: {
124+
ForEach(filteredRelevantPackages)
125+
{ outdatedPackage in
126+
TableRow(outdatedPackage)
127+
.contextMenu
128+
{
129+
PreviewPackageButton(packageToPreview: .init(
130+
name: outdatedPackage.package.name,
131+
type: outdatedPackage.package.type,
132+
installedIntentionally: outdatedPackage.package.installedIntentionally)
133+
)
134+
}
135+
}
136+
}
137+
.tableStyle(.bordered)
138+
}
139+
}
140+
141+
@ViewBuilder
142+
func selectAllButton(packagesToSelect: Set<OutdatedPackage>) -> some View
143+
{
144+
Button
145+
{
146+
relevantPackages.forEach
147+
{
148+
$0.changeMarkedState(to: true)
149+
}
150+
} label: {
151+
Text("start-page.updated.action.select-all")
152+
}
153+
.disabled(areAnyPackagesLeftToSelect)
154+
.buttonStyle(.accessoryBar)
155+
}
156+
157+
@ViewBuilder
158+
func deselectAllButton(packagesToDeselect: Set<OutdatedPackage>) -> some View
159+
{
160+
Button
161+
{
162+
relevantPackages.forEach
163+
{
164+
$0.changeMarkedState(to: false)
79165
}
166+
} label: {
167+
Text("start-page.updated.action.deselect-all")
80168
}
81-
.tableStyle(.bordered)
169+
.disabled(!areAnyRelevantPackagesSelected)
170+
.buttonStyle(.accessoryBar)
82171
}
83172
}

Cork/Views/Start Page/Sub-Views/Outdated Packages/Outdated Packages List/Outdated Packages List.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ struct OutdatedPackagesList: View
1616

1717
@Environment(OutdatedPackagesTracker.self) var outdatedPackagesTracker: OutdatedPackagesTracker
1818

19+
var areThereAnySelfManagedUpdatesAvailable: Bool
20+
{
21+
return !outdatedPackagesTracker.packagesThatUpdateThemselves.isEmpty
22+
}
23+
24+
var numberOfSelfManagedUpdates: Int
25+
{
26+
return outdatedPackagesTracker.packagesThatUpdateThemselves.count
27+
}
28+
1929
var body: some View
2030
{
2131
if outdatedPackagesTracker.packagesManagedByHomebrew.isEmpty
@@ -30,8 +40,24 @@ struct OutdatedPackagesList: View
3040
{
3141
case .none, .versionOnly:
3242
OutdatedPackagesList_List(packageUpdatingType: .homebrew)
43+
44+
if areThereAnySelfManagedUpdatesAvailable
45+
{
46+
DisclosureGroup("start-page.updates.self-updating.\(numberOfSelfManagedUpdates).list")
47+
{
48+
OutdatedPackagesList_List(packageUpdatingType: .selfUpdating)
49+
}
50+
}
3351
case .all:
3452
OutdatedPackagesList_Table(packageUpdatingType: .homebrew)
53+
54+
if areThereAnySelfManagedUpdatesAvailable
55+
{
56+
DisclosureGroup("start-page.updates.self-updating.\(numberOfSelfManagedUpdates).list")
57+
{
58+
OutdatedPackagesList_Table(packageUpdatingType: .selfUpdating)
59+
}
60+
}
3561
}
3662
}
3763
}

0 commit comments

Comments
 (0)