Skip to content

Commit 6973610

Browse files
committed
Fixed SearchView issue where no message was being displayed when zero search results were found.
1 parent 3e211c5 commit 6973610

File tree

2 files changed

+125
-12
lines changed

2 files changed

+125
-12
lines changed

App/Resources/Localizable.xcstrings

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66
},
77
"%@" : {
88

9+
},
10+
"%lld of %lld" : {
11+
"localizations" : {
12+
"en" : {
13+
"stringUnit" : {
14+
"state" : "new",
15+
"value" : "%1$lld of %2$lld"
16+
}
17+
}
18+
}
919
},
1020
"😕" : {
1121

App/Views/SearchView.swift

Lines changed: 115 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,68 @@ struct SearchResultsView: View {
1919
NavigationView {
2020
ScrollViewReader { proxy in
2121
ScrollView {
22-
LazyVStack(spacing: 12) {
23-
ForEach(model.searchResults) { searchResult in
24-
SearchResultCard(result: searchResult)
25-
.onTapGesture {
26-
model.viewState = .none
27-
AppDelegate.instance.open(route: .post(id: searchResult.postID, .noseen))
22+
if model.searchResults.isEmpty && !model.searchState.resultInfo.isEmpty {
23+
VStack(alignment: .leading, spacing: 12) {
24+
// Parse and display the search info with proper formatting
25+
let lines = model.searchState.resultInfo
26+
.trimmingCharacters(in: .whitespacesAndNewlines)
27+
.components(separatedBy: .newlines)
28+
.map { $0.trimmingCharacters(in: .whitespaces) }
29+
.filter { !$0.isEmpty }
30+
31+
ForEach(Array(lines.enumerated()), id: \.offset) { index, line in
32+
if line.starts(with: "Searched for") {
33+
Text(line)
34+
.font(.headline)
35+
.foregroundColor(theme[color: "listTextColor"])
36+
.padding(.bottom, 4)
37+
.frame(maxWidth: .infinity, alignment: .leading)
38+
} else if line == "following criteria:" {
39+
Text(line)
40+
.font(.headline)
41+
.foregroundColor(theme[color: "listTextColor"])
42+
.frame(maxWidth: .infinity, alignment: .leading)
43+
} else if line.starts(with: "Text contains") || line.starts(with: "Posted by") {
44+
Text(line)
45+
.font(.body)
46+
.foregroundColor(theme[color: "listTextColor"])
47+
.padding(.vertical, 8)
48+
.padding(.leading, 16)
49+
.frame(maxWidth: .infinity, alignment: .leading)
50+
} else if line.starts(with: "There were no results") {
51+
Text(line)
52+
.font(.body)
53+
.fontWeight(.medium)
54+
.foregroundColor(theme[color: "listTextColor"])
55+
.padding(.top, 8)
56+
.frame(maxWidth: .infinity, alignment: .leading)
57+
} else if !line.isEmpty {
58+
Text(line)
59+
.font(.body)
60+
.foregroundColor(theme[color: "listTextColor"])
61+
.frame(maxWidth: .infinity, alignment: .leading)
2862
}
63+
}
64+
}
65+
.frame(maxWidth: .infinity, alignment: .leading)
66+
.padding()
67+
.background(theme[color: "sheetBackgroundColor"]!)
68+
.cornerRadius(12)
69+
.id(topID)
70+
.padding()
71+
} else {
72+
LazyVStack(spacing: 12) {
73+
ForEach(model.searchResults) { searchResult in
74+
SearchResultCard(result: searchResult)
75+
.onTapGesture {
76+
model.viewState = .none
77+
AppDelegate.instance.open(route: .post(id: searchResult.postID, .noseen))
78+
}
79+
}
2980
}
81+
.id(topID)
82+
.padding()
3083
}
31-
.id(topID)
32-
.padding()
3384
}
3485
.background(theme[color: "backgroundColor"]!)
3586
.onChange(of: model.currentPage) { _ in
@@ -59,9 +110,7 @@ struct SearchResultsView: View {
59110
}
60111

61112
ToolbarItem(placement: .bottomBar) {
62-
if model.totalPages > 1 {
63-
paginationControls
64-
}
113+
paginationControls
65114
}
66115
}
67116
.background(NavigationConfigurator(theme: theme))
@@ -84,9 +133,11 @@ struct SearchResultsView: View {
84133

85134
Spacer()
86135

87-
Text("Page \(model.currentPage) of \(model.totalPages)")
136+
Text("\(model.currentPage) of \(model.totalPages)")
88137
.font(.headline)
89138
.foregroundColor(theme[color: "listTextColor"])
139+
.frame(minWidth: 80)
140+
.lineLimit(1)
90141

91142
Spacer()
92143

@@ -404,12 +455,34 @@ struct NavigationConfigurator: UIViewControllerRepresentable {
404455
navAppearance.configureWithOpaqueBackground()
405456
navAppearance.backgroundColor = theme[uicolor: "navigationBarTintColor"]
406457
navAppearance.shadowColor = .clear
458+
459+
// Ensure the custom back indicator image from assets is used
460+
if let backImage = UIImage(named: "back") {
461+
navAppearance.setBackIndicatorImage(backImage, transitionMaskImage: backImage)
462+
}
407463

408464
let textColor = theme[uicolor: "navigationBarTextColor"]!
409465
navAppearance.titleTextAttributes = [.foregroundColor: textColor]
466+
467+
// Ensure text-based bar button items adopt theme font (rounded if enabled)
468+
let buttonFont = UIFont.preferredFontForTextStyle(.body, fontName: nil, sizeAdjustment: 0, weight: .regular)
469+
let buttonAttrs: [NSAttributedString.Key: Any] = [
470+
.foregroundColor: textColor,
471+
.font: buttonFont
472+
]
473+
navAppearance.buttonAppearance.normal.titleTextAttributes = buttonAttrs
474+
navAppearance.buttonAppearance.highlighted.titleTextAttributes = buttonAttrs
475+
navAppearance.doneButtonAppearance.normal.titleTextAttributes = buttonAttrs
476+
navAppearance.doneButtonAppearance.highlighted.titleTextAttributes = buttonAttrs
477+
navAppearance.backButtonAppearance.normal.titleTextAttributes = buttonAttrs
478+
navAppearance.backButtonAppearance.highlighted.titleTextAttributes = buttonAttrs
410479

411480
navigationController.navigationBar.standardAppearance = navAppearance
412481
navigationController.navigationBar.scrollEdgeAppearance = navAppearance
482+
// Drive the bar style from the current theme so status bar
483+
// icons match the theme while Search is presented.
484+
let isLightBackground = (theme["statusBarBackground"] == "light")
485+
navigationController.navigationBar.barStyle = isLightBackground ? .default : .black
413486

414487
// Configure toolbar
415488
let toolbarAppearance = UIToolbarAppearance()
@@ -731,6 +804,13 @@ final class SearchPageViewModel: ObservableObject {
731804
self.totalPages = maxPage
732805
} else if let requestedPage = requestedPage {
733806
self.currentPage = requestedPage
807+
// Preserve totalPages if it was already set and we're just navigating
808+
// Only reset to 1 if it was never set (still at initial value)
809+
if self.totalPages == 1 && requestedPage > 1 {
810+
// If we're on page > 1 but totalPages is still 1,
811+
// we know there must be at least requestedPage pages
812+
self.totalPages = requestedPage
813+
}
734814
}
735815
}
736816
}
@@ -864,5 +944,28 @@ final class SearchHostingController: UIHostingController<SearchView> {
864944
@MainActor required dynamic init?(coder aDecoder: NSCoder) {
865945
fatalError("init(coder:) has not been implemented")
866946
}
947+
948+
override func viewDidLoad() {
949+
super.viewDidLoad()
950+
// Ensure this controller controls status bar appearance so it stays
951+
// consistent with the app's theme while presented
952+
modalPresentationCapturesStatusBarAppearance = true
953+
}
954+
955+
// Keep status bar style in sync with the current theme so opening the
956+
// search view does not change it unexpectedly.
957+
override var preferredStatusBarStyle: UIStatusBarStyle {
958+
let theme = Theme.defaultTheme()
959+
return (theme["statusBarBackground"] == "light") ? .darkContent : .lightContent
960+
}
961+
962+
// Force the system to use this hosting controller's status bar style
963+
// instead of deferring to the child SwiftUI navigation controller.
964+
override var childForStatusBarStyle: UIViewController? { nil }
965+
966+
override func viewWillAppear(_ animated: Bool) {
967+
super.viewWillAppear(animated)
968+
setNeedsStatusBarAppearanceUpdate()
969+
}
867970
}
868971

0 commit comments

Comments
 (0)