Skip to content

Commit 7fb7d05

Browse files
committed
feat: section index titles
Signed-off-by: Lessica <82flex@gmail.com>
1 parent e4a2c37 commit 7fb7d05

15 files changed

+464
-313
lines changed

TrollFools.xcodeproj/project.pbxproj

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@
4343
614C0C612D7C2EC0007E9184 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614C0C602D7C2EBE007E9184 /* Constants.swift */; };
4444
614C0C622D7C2EC0007E9184 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614C0C602D7C2EBE007E9184 /* Constants.swift */; };
4545
614C0C632D7C2EDE007E9184 /* TrollFoolsStub.m in Sources */ = {isa = PBXBuildFile; fileRef = CCF470722C4A4E81008D8197 /* TrollFoolsStub.m */; };
46-
614C0C682D7C72AD007E9184 /* AppListSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614C0C672D7C72AC007E9184 /* AppListSearchViewModel.swift */; };
46+
614C0C682D7C72AD007E9184 /* AppListSearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614C0C672D7C72AC007E9184 /* AppListSearchModel.swift */; };
4747
614C0C6A2D7CB458007E9184 /* Hashable+Combines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614C0C692D7CB457007E9184 /* Hashable+Combines.swift */; };
48+
614C0C6C2D7D72D4007E9184 /* IndexableScroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614C0C6B2D7D72C7007E9184 /* IndexableScroller.swift */; };
4849
6168001B2D364DD400DF485F /* StripedTextTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6168001A2D364DD400DF485F /* StripedTextTableViewController.m */; };
4950
6168001D2D3652B600DF485F /* LogsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6168001C2D3652B100DF485F /* LogsView.swift */; };
5051
61946B262D2FD8AB009E0AC4 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 61946B252D2FD8AB009E0AC4 /* OrderedCollections */; };
@@ -137,8 +138,9 @@
137138
614C0C3A2D7C243E007E9184 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
138139
614C0C602D7C2EBE007E9184 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
139140
614C0C662D7C407E007E9184 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
140-
614C0C672D7C72AC007E9184 /* AppListSearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListSearchViewModel.swift; sourceTree = "<group>"; };
141+
614C0C672D7C72AC007E9184 /* AppListSearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListSearchModel.swift; sourceTree = "<group>"; };
141142
614C0C692D7CB457007E9184 /* Hashable+Combines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Hashable+Combines.swift"; sourceTree = "<group>"; };
143+
614C0C6B2D7D72C7007E9184 /* IndexableScroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndexableScroller.swift; sourceTree = "<group>"; };
142144
616800192D364DD400DF485F /* StripedTextTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StripedTextTableViewController.h; sourceTree = "<group>"; };
143145
6168001A2D364DD400DF485F /* StripedTextTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StripedTextTableViewController.m; sourceTree = "<group>"; };
144146
6168001C2D3652B100DF485F /* LogsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsView.swift; sourceTree = "<group>"; };
@@ -238,7 +240,7 @@
238240
children = (
239241
6124A06C2CD2322400C52253 /* App.swift */,
240242
6124A06E2CD2324200C52253 /* AppListModel.swift */,
241-
614C0C672D7C72AC007E9184 /* AppListSearchViewModel.swift */,
243+
614C0C672D7C72AC007E9184 /* AppListSearchModel.swift */,
242244
6124A0802CD233DA00C52253 /* EjectListModel.swift */,
243245
6124A0702CD2326500C52253 /* FilterOptions.swift */,
244246
6124A07C2CD233A700C52253 /* InjectedPlugIn.swift */,
@@ -254,6 +256,7 @@
254256
CCF4706D2C4A4BAB008D8197 /* AppListView.swift */,
255257
CC15490D2C4B80AF00A4173E /* EjectListView.swift */,
256258
CC1548D42C4A744300A4173E /* FailureView.swift */,
259+
614C0C6B2D7D72C7007E9184 /* IndexableScroller.swift */,
257260
CCB6A11A2C4A6066000D75B0 /* InjectView.swift */,
258261
6168001C2D3652B100DF485F /* LogsView.swift */,
259262
6124A0772CD232D400C52253 /* OptionCell.swift */,
@@ -277,9 +280,9 @@
277280
6124A0822CD2345300C52253 /* Core */ = {
278281
isa = PBXGroup;
279282
children = (
280-
614C0C602D7C2EBE007E9184 /* Constants.swift */,
281283
CCF5A0A32C4D45160097D48D /* AuxiliaryExecute.swift */,
282284
CCF5A0A52C4D45400097D48D /* AuxiliaryExecute+Spawn.swift */,
285+
614C0C602D7C2EBE007E9184 /* Constants.swift */,
283286
CC1548DA2C4A7C7000A4173E /* Execute.swift */,
284287
61EFA3632D30267200159442 /* InjectorV3.swift */,
285288
61EFA37A2D31165000159442 /* InjectorV3+Backup.swift */,
@@ -592,11 +595,12 @@
592595
6124A06F2CD2324200C52253 /* AppListModel.swift in Sources */,
593596
CCB6A1192C4A58C7000D75B0 /* OptionView.swift in Sources */,
594597
CCF4706E2C4A4BAB008D8197 /* AppListView.swift in Sources */,
598+
614C0C6C2D7D72D4007E9184 /* IndexableScroller.swift in Sources */,
595599
CC1548D32C4A743200A4173E /* SuccessView.swift in Sources */,
596600
61EFA36D2D3027C400159442 /* InjectorV3+Command.swift in Sources */,
597601
CC1548D52C4A744300A4173E /* FailureView.swift in Sources */,
598602
61EFA3732D30347A00159442 /* InjectorV3+MachO.swift in Sources */,
599-
614C0C682D7C72AD007E9184 /* AppListSearchViewModel.swift in Sources */,
603+
614C0C682D7C72AD007E9184 /* AppListSearchModel.swift in Sources */,
600604
614C0C622D7C2EC0007E9184 /* Constants.swift in Sources */,
601605
);
602606
runOnlyForDeploymentPostprocessing = 0;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1620"
4+
version = "1.7">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES"
8+
buildArchitectures = "Automatic">
9+
<BuildActionEntries>
10+
<BuildActionEntry
11+
buildForTesting = "YES"
12+
buildForRunning = "YES"
13+
buildForProfiling = "YES"
14+
buildForArchiving = "YES"
15+
buildForAnalyzing = "YES">
16+
<BuildableReference
17+
BuildableIdentifier = "primary"
18+
BlueprintIdentifier = "614C0C372D7C243E007E9184"
19+
BuildableName = "trollfoolscli"
20+
BlueprintName = "trollfoolscli"
21+
ReferencedContainer = "container:TrollFools.xcodeproj">
22+
</BuildableReference>
23+
</BuildActionEntry>
24+
</BuildActionEntries>
25+
</BuildAction>
26+
<TestAction
27+
buildConfiguration = "Debug"
28+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
29+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
30+
shouldUseLaunchSchemeArgsEnv = "YES"
31+
shouldAutocreateTestPlan = "YES">
32+
</TestAction>
33+
<LaunchAction
34+
buildConfiguration = "Debug"
35+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
36+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
37+
launchStyle = "0"
38+
useCustomWorkingDirectory = "NO"
39+
ignoresPersistentStateOnLaunch = "NO"
40+
debugDocumentVersioning = "YES"
41+
debugServiceExtension = "internal"
42+
allowLocationSimulation = "YES"
43+
viewDebuggingEnabled = "No">
44+
<BuildableProductRunnable
45+
runnableDebuggingMode = "0">
46+
<BuildableReference
47+
BuildableIdentifier = "primary"
48+
BlueprintIdentifier = "614C0C372D7C243E007E9184"
49+
BuildableName = "trollfoolscli"
50+
BlueprintName = "trollfoolscli"
51+
ReferencedContainer = "container:TrollFools.xcodeproj">
52+
</BuildableReference>
53+
</BuildableProductRunnable>
54+
</LaunchAction>
55+
<ProfileAction
56+
buildConfiguration = "Release"
57+
shouldUseLaunchSchemeArgsEnv = "YES"
58+
savedToolIdentifier = ""
59+
useCustomWorkingDirectory = "NO"
60+
debugDocumentVersioning = "YES">
61+
<BuildableProductRunnable
62+
runnableDebuggingMode = "0">
63+
<BuildableReference
64+
BuildableIdentifier = "primary"
65+
BlueprintIdentifier = "614C0C372D7C243E007E9184"
66+
BuildableName = "trollfoolscli"
67+
BlueprintName = "trollfoolscli"
68+
ReferencedContainer = "container:TrollFools.xcodeproj">
69+
</BuildableReference>
70+
</BuildableProductRunnable>
71+
</ProfileAction>
72+
<AnalyzeAction
73+
buildConfiguration = "Debug">
74+
</AnalyzeAction>
75+
<ArchiveAction
76+
buildConfiguration = "Release"
77+
revealArchiveInOrganizer = "YES">
78+
</ArchiveAction>
79+
</Scheme>

TrollFools/AppListCell.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,8 @@ struct AppListCell: View {
5757
Button {
5858
do {
5959
try InjectorV3(app.url).setMetadataDetached(false)
60-
withAnimation {
61-
app.reload()
62-
appList.isRebuildNeeded = true
63-
}
60+
app.reload()
61+
appList.isRebuildNeeded = true
6462
} catch { DDLogError("\(error)", ddlog: InjectorV3.main.logger) }
6563
} label: {
6664
Label(NSLocalizedString("Unlock Version", comment: ""), systemImage: "lock.open")
@@ -69,10 +67,8 @@ struct AppListCell: View {
6967
Button {
7068
do {
7169
try InjectorV3(app.url).setMetadataDetached(true)
72-
withAnimation {
73-
app.reload()
74-
appList.isRebuildNeeded = true
75-
}
70+
app.reload()
71+
appList.isRebuildNeeded = true
7672
} catch { DDLogError("\(error)", ddlog: InjectorV3.main.logger) }
7773
} label: {
7874
Label(NSLocalizedString("Lock Version", comment: ""), systemImage: "lock")

TrollFools/AppListModel.swift

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,43 @@ import OrderedCollections
1010
import SwiftUI
1111

1212
final class AppListModel: ObservableObject {
13+
enum Scope: Int, CaseIterable {
14+
case user
15+
case troll
16+
case system
17+
18+
var localizedShortName: String {
19+
switch self {
20+
case .user:
21+
NSLocalizedString("User", comment: "")
22+
case .troll:
23+
NSLocalizedString("TrollStore", comment: "")
24+
case .system:
25+
NSLocalizedString("System", comment: "")
26+
}
27+
}
28+
29+
var localizedName: String {
30+
switch self {
31+
case .user:
32+
NSLocalizedString("User Applications", comment: "")
33+
case .troll:
34+
NSLocalizedString("TrollStore Applications", comment: "")
35+
case .system:
36+
NSLocalizedString("Injectable System Applications", comment: "")
37+
}
38+
}
39+
}
40+
1341
static let hasTrollStore: Bool = { LSApplicationProxy(forIdentifier: "com.opa334.TrollStore") != nil }()
1442
private var _allApplications: [App] = []
1543

1644
let selectorURL: URL?
1745
var isSelectorMode: Bool { selectorURL != nil }
1846

1947
@Published var filter = FilterOptions()
20-
@Published var userApplications = OrderedDictionary<String, [App]>()
21-
@Published var trollApplications = OrderedDictionary<String, [App]>()
22-
@Published var appleApplications = OrderedDictionary<String, [App]>()
48+
@Published var activeScope: Scope = .user
49+
@Published var activeScopeApps: OrderedDictionary<String, [App]> = [:]
2350

2451
@Published var isPaidProductInstalled: Bool = false
2552
@Published var unsupportedCount: Int = 0
@@ -28,7 +55,6 @@ final class AppListModel: ObservableObject {
2855
private let filzaURL = URL(string: "filza://")
2956

3057
@Published var isRebuildNeeded: Bool = false
31-
@Published var isRebuilding: Bool = false
3258

3359
private let applicationChanged = PassthroughSubject<Void, Never>()
3460
private var cancellables = Set<AnyCancellable>()
@@ -37,21 +63,20 @@ final class AppListModel: ObservableObject {
3763
self.selectorURL = selectorURL
3864
reload()
3965

40-
$filter
41-
.throttle(for: 0.5, scheduler: DispatchQueue.main, latest: true)
42-
.sink { [weak self] _ in
43-
withAnimation {
44-
self?.performFilter()
45-
}
46-
}
47-
.store(in: &cancellables)
66+
Publishers.CombineLatest(
67+
$filter,
68+
$activeScope
69+
)
70+
.throttle(for: 0.5, scheduler: DispatchQueue.main, latest: true)
71+
.sink { [weak self] _ in
72+
self?.performFilter()
73+
}
74+
.store(in: &cancellables)
4875

4976
applicationChanged
5077
.throttle(for: 0.5, scheduler: DispatchQueue.main, latest: true)
5178
.sink { [weak self] _ in
52-
withAnimation {
53-
self?.reload()
54-
}
79+
self?.reload()
5580
}
5681
.store(in: &cancellables)
5782

@@ -100,9 +125,14 @@ final class AppListModel: ObservableObject {
100125
filteredApplications = filteredApplications.filter { $0.isInjected }
101126
}
102127

103-
userApplications = Self.groupedAppList(filteredApplications.filter { $0.isUser })
104-
trollApplications = Self.groupedAppList(filteredApplications.filter { $0.isFromTroll })
105-
appleApplications = Self.groupedAppList(filteredApplications.filter { $0.isFromApple })
128+
switch activeScope {
129+
case .user:
130+
activeScopeApps = Self.groupedAppList(filteredApplications.filter { $0.isUser })
131+
case .troll:
132+
activeScopeApps = Self.groupedAppList(filteredApplications.filter { $0.isFromTroll })
133+
case .system:
134+
activeScopeApps = Self.groupedAppList(filteredApplications.filter { $0.isFromApple })
135+
}
106136
}
107137

108138
private static let excludedIdentifiers: Set<String> = [
@@ -165,7 +195,9 @@ final class AppListModel: ObservableObject {
165195

166196
return filteredApps
167197
}
198+
}
168199

200+
extension AppListModel {
169201
func openInFilza(_ url: URL) {
170202
guard let filzaURL else {
171203
return
@@ -174,11 +206,15 @@ final class AppListModel: ObservableObject {
174206
UIApplication.shared.open(fileURL)
175207
}
176208

177-
func rebuildIconCache() throws {
209+
func rebuildIconCache() {
178210
// Sadly, we can't call `trollstorehelper` directly because only TrollStore can launch it without error.
179-
LSApplicationWorkspace.default().openApplication(withBundleID: "com.opa334.TrollStore")
211+
DispatchQueue.global(qos: .userInitiated).async {
212+
LSApplicationWorkspace.default().openApplication(withBundleID: "com.opa334.TrollStore")
213+
}
180214
}
215+
}
181216

217+
extension AppListModel {
182218
static let allowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ#"
183219
private static let allowedCharacterSet = CharacterSet(charactersIn: allowedCharacters)
184220

TrollFools/AppListSearchViewModel.swift renamed to TrollFools/AppListSearchModel.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// AppListSearchViewModel.swift
2+
// AppListSearchModel.swift
33
// TrollFools
44
//
55
// Created by 82Flex on 3/8/25.
@@ -8,15 +8,15 @@
88
import Combine
99
import UIKit
1010

11-
final class AppListSearchViewModel: NSObject, ObservableObject {
11+
final class AppListSearchModel: NSObject, ObservableObject {
1212
@Published var searchKeyword: String = ""
1313
@Published var searchScopeIndex: Int = 0
1414

1515
weak var searchController: UISearchController?
1616
weak var forwardSearchBarDelegate: (any UISearchBarDelegate)?
1717
}
1818

19-
extension AppListSearchViewModel: UISearchBarDelegate, UISearchResultsUpdating {
19+
extension AppListSearchModel: UISearchBarDelegate, UISearchResultsUpdating {
2020
func updateSearchResults(for searchController: UISearchController) {
2121
searchKeyword = searchController.searchBar.text ?? ""
2222
}

0 commit comments

Comments
 (0)