Skip to content

Commit a9c2eeb

Browse files
committed
Add search view
1 parent d2176f6 commit a9c2eeb

File tree

9 files changed

+154
-1
lines changed

9 files changed

+154
-1
lines changed

Localization/Localizations/Localizable.xcstrings

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5894,6 +5894,9 @@
58945894
}
58955895
}
58965896
}
5897+
},
5898+
"Running..." : {
5899+
58975900
},
58985901
"Save" : {
58995902
"localizations" : {
@@ -6015,6 +6018,9 @@
60156018
}
60166019
}
60176020
}
6021+
},
6022+
"Search" : {
6023+
60186024
},
60196025
"Seeding" : {
60206026
"localizations" : {
@@ -6735,6 +6741,9 @@
67356741
}
67366742
}
67376743
}
6744+
},
6745+
"Start" : {
6746+
67386747
},
67396748
"State" : {
67406749
"localizations" : {

qBitControl.xcodeproj/project.pbxproj

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@
6363
90726B772CDA221B0032993E /* TorrentListHelperViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90726B712CDA221B0032993E /* TorrentListHelperViewModel.swift */; };
6464
90726B782CDA221B0032993E /* MainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90726B732CDA221B0032993E /* MainViewModel.swift */; };
6565
90726B7A2CDA24960032993E /* TrackersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90726B792CDA24820032993E /* TrackersViewModel.swift */; };
66+
907C63282DAED94A004B1F41 /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 907C63262DAED94A004B1F41 /* SearchView.swift */; };
67+
907C632A2DAEDACA004B1F41 /* SearchStartResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 907C63292DAEDAC7004B1F41 /* SearchStartResult.swift */; };
68+
907C632D2DAEDC28004B1F41 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 907C632C2DAEDC1C004B1F41 /* SearchViewModel.swift */; };
6669
9082FA2329080E53006C3960 /* qBitControlApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9082FA2229080E53006C3960 /* qBitControlApp.swift */; };
6770
9082FA2729080E55006C3960 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9082FA2629080E55006C3960 /* Assets.xcassets */; };
6871
9082FA2A29080E55006C3960 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9082FA2929080E55006C3960 /* Preview Assets.xcassets */; };
@@ -157,6 +160,9 @@
157160
90726B712CDA221B0032993E /* TorrentListHelperViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TorrentListHelperViewModel.swift; sourceTree = "<group>"; };
158161
90726B732CDA221B0032993E /* MainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewModel.swift; sourceTree = "<group>"; };
159162
90726B792CDA24820032993E /* TrackersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackersViewModel.swift; sourceTree = "<group>"; };
163+
907C63262DAED94A004B1F41 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
164+
907C63292DAEDAC7004B1F41 /* SearchStartResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchStartResult.swift; sourceTree = "<group>"; };
165+
907C632C2DAEDC1C004B1F41 /* SearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = "<group>"; };
160166
9082FA1F29080E53006C3960 /* qBitManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = qBitManager.app; sourceTree = BUILT_PRODUCTS_DIR; };
161167
9082FA2229080E53006C3960 /* qBitControlApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = qBitControlApp.swift; sourceTree = "<group>"; };
162168
9082FA2629080E55006C3960 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -252,6 +258,7 @@
252258
90726B1E2CDA22160032993E /* Models */ = {
253259
isa = PBXGroup;
254260
children = (
261+
907C63292DAEDAC7004B1F41 /* SearchStartResult.swift */,
255262
90E1D0532D30438C00B81F12 /* Version.swift */,
256263
90726B0C2CDA22160032993E /* AlertIdentifier.swift */,
257264
90726B0E2CDA22160032993E /* Category.swift */,
@@ -333,6 +340,7 @@
333340
90726B522CDA22190032993E /* Views */ = {
334341
isa = PBXGroup;
335342
children = (
343+
907C63272DAED94A004B1F41 /* SearchViews */,
336344
90726B382CDA22190032993E /* ServersViews */,
337345
90726B342CDA22190032993E /* RSSViews */,
338346
90726B502CDA22190032993E /* TorrentViews */,
@@ -364,6 +372,7 @@
364372
90726B742CDA221B0032993E /* ViewModels */ = {
365373
isa = PBXGroup;
366374
children = (
375+
907C632B2DAEDC15004B1F41 /* SearchView */,
367376
90AE89302CDB6EF5000E0276 /* RSSView */,
368377
90726B6F2CDA221B0032993E /* ServersView */,
369378
90726B722CDA221B0032993E /* TorrentView */,
@@ -372,6 +381,22 @@
372381
path = ViewModels;
373382
sourceTree = "<group>";
374383
};
384+
907C63272DAED94A004B1F41 /* SearchViews */ = {
385+
isa = PBXGroup;
386+
children = (
387+
907C63262DAED94A004B1F41 /* SearchView.swift */,
388+
);
389+
path = SearchViews;
390+
sourceTree = "<group>";
391+
};
392+
907C632B2DAEDC15004B1F41 /* SearchView */ = {
393+
isa = PBXGroup;
394+
children = (
395+
907C632C2DAEDC1C004B1F41 /* SearchViewModel.swift */,
396+
);
397+
path = SearchView;
398+
sourceTree = "<group>";
399+
};
375400
9082FA1629080E53006C3960 = {
376401
isa = PBXGroup;
377402
children = (
@@ -568,6 +593,7 @@
568593
isa = PBXSourcesBuildPhase;
569594
buildActionMask = 2147483647;
570595
files = (
596+
907C632D2DAEDC28004B1F41 /* SearchViewModel.swift in Sources */,
571597
90E86FFE2C81C89A00F4EA01 /* qBitDataClass.swift in Sources */,
572598
90EF6A4D29093142001E9E7F /* qBitRequestClass.swift in Sources */,
573599
907023302CDA5A56007B2199 /* TorrentListViewModel.swift in Sources */,
@@ -581,6 +607,7 @@
581607
90726B552CDA22190032993E /* RSSView.swift in Sources */,
582608
90726B562CDA22190032993E /* ServerAddView.swift in Sources */,
583609
9070232C2CDA4869007B2199 /* TorrentDetailsViewModel.swift in Sources */,
610+
907C63282DAED94A004B1F41 /* SearchView.swift in Sources */,
584611
90726B572CDA22190032993E /* ServerRowView.swift in Sources */,
585612
90726B582CDA22190032993E /* ServersView.swift in Sources */,
586613
90726B592CDA22190032993E /* CustomLabelView.swift in Sources */,
@@ -596,6 +623,7 @@
596623
90726B622CDA22190032993E /* AboutView.swift in Sources */,
597624
90726B632CDA22190032993E /* TorrentAddView.swift in Sources */,
598625
90726B642CDA22190032993E /* FilesView.swift in Sources */,
626+
907C632A2DAEDACA004B1F41 /* SearchStartResult.swift in Sources */,
599627
90726B652CDA22190032993E /* PeersRowView.swift in Sources */,
600628
90726B662CDA22190032993E /* PeersView.swift in Sources */,
601629
90726B672CDA22190032993E /* TrackerRow.swift in Sources */,

qBitControl/Classes/qBitRequestClass.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,20 @@ class qBitRequest {
9595
}.resume()
9696
}
9797

98+
static func requestSearchStart(request: URLRequest, completionHandler: @escaping (SearchStartResult) -> Void) {
99+
URLSession.shared.dataTask(with: request) {
100+
data, response, error in
101+
if let data = data {
102+
do {
103+
let json = try JSONDecoder().decode(SearchStartResult.self, from: data)
104+
completionHandler(json)
105+
} catch {
106+
print(error)
107+
}
108+
}
109+
}.resume()
110+
}
111+
98112
static func requestGlobalTransferInfo(request: URLRequest, completionHandler: @escaping (GlobalTransferInfo) -> Void) {
99113
URLSession.shared.dataTask(with: request) {
100114
data, response, error in

qBitControl/Classes/qBittorrentClass.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,16 @@ class qBittorrent {
288288
qBitRequest.requestPreferencesJSON(request: request, completionHandler: completionHandler)
289289
}
290290

291+
static func getSearchStart(pattern: String, category: String, plugins: Bool = true, completionHandler: @escaping (SearchStartResult) -> Void) {
292+
let request = qBitRequest.prepareURLRequest(path: "/api/v2/search/start", queryItems: [
293+
URLQueryItem(name: "pattern", value: pattern),
294+
URLQueryItem(name: "category", value: category),
295+
URLQueryItem(name: "plugins", value: plugins ? "enabled" : "disabled")
296+
])
297+
298+
qBitRequest.requestSearchStart(request: request, completionHandler: completionHandler)
299+
}
300+
291301
static func getCategories(completionHandler: @escaping ([String: Category]) -> Void) {
292302
let request = qBitRequest.prepareURLRequest(path: "/api/v2/torrents/categories")
293303

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
struct SearchStartResult: Decodable {
2+
let id: Int
3+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import SwiftUI
2+
3+
class SearchViewModel: ObservableObject {
4+
@Published var query: String = ""
5+
@Published var category: String = "all"
6+
7+
@Published var searchId: Int?
8+
9+
var isRunning: Bool {
10+
searchId != nil
11+
}
12+
13+
private var timer: Timer?
14+
15+
func startSearch() {
16+
if(isRunning) { return }
17+
18+
qBittorrent.getSearchStart(pattern: self.query, category: self.category, completionHandler: { result in
19+
DispatchQueue.main.async {
20+
self.searchId = result.id
21+
22+
self.timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { timer in
23+
self.monitorSearchResults()
24+
}
25+
}
26+
})
27+
}
28+
29+
private func endSearch() {
30+
self.searchId = nil
31+
}
32+
33+
private func monitorSearchResults() {
34+
self.validateTimer()
35+
36+
print("Do something")
37+
38+
self.endSearch()
39+
}
40+
41+
private func validateTimer() {
42+
if(searchId == nil) {
43+
self.timer?.invalidate()
44+
}
45+
}
46+
}

qBitControl/Views/RSSViews/RSSNodeView.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ struct RSSNodeView: View {
1414
@State private var newRenameName = ""
1515
@State private var oldRenamePath = ""
1616

17+
var isRootView: Bool {
18+
self.path.count == 1
19+
}
20+
1721
func getItemPath(item: String) -> String {
1822
var path = self.path + [item]
1923
path.removeFirst()
@@ -22,6 +26,16 @@ struct RSSNodeView: View {
2226

2327
var body: some View {
2428
List {
29+
if isRootView {
30+
Section(header: Text("Search")) {
31+
NavigationLink {
32+
SearchView()
33+
} label: {
34+
Label("Search", systemImage: "magnifyingglass")
35+
}
36+
}
37+
}
38+
2539
Section(header: sectionHeader()) {
2640
ForEach(rssNode.nodes, id: \.id) { node in
2741
NavigationLink {

qBitControl/Views/RSSViews/RSSView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import SwiftUI
88
struct RSSView: View {
99
var body: some View {
1010
VStack {
11-
NavigationStack {
11+
NavigationStack {
1212
RSSNodeView(path: ["RSS"])
1313
}
1414
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import SwiftUI
2+
3+
struct SearchView: View {
4+
@StateObject var viewModel = SearchViewModel()
5+
6+
var body: some View {
7+
List {
8+
Section(header: Text("Search")) {
9+
HStack {
10+
TextField("Search", text: $viewModel.query)
11+
.autocorrectionDisabled(true)
12+
.autocapitalization(.none)
13+
.keyboardType(.default)
14+
15+
Button {
16+
viewModel.startSearch()
17+
} label: {
18+
if viewModel.isRunning {
19+
Text("Running...")
20+
.foregroundStyle(.gray)
21+
} else {
22+
Text("Start")
23+
}
24+
}
25+
}
26+
}
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)