Fix for native navigation (#233)
Some checks failed
Build and Release / Build IPA (push) Has been cancelled
Build and Release / Build macOS App (push) Has been cancelled

Co-authored-by: cranci <100066266+cranci1@users.noreply.github.com>
This commit is contained in:
Ek20000 2025-08-15 15:35:58 +02:00 committed by GitHub
parent 8b2f9c08cd
commit 122e248d9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 112 additions and 99 deletions

View file

@ -52,7 +52,7 @@ struct ContentView: View {
} }
} }
} }
.searchable(text: $searchQuery) //.searchable(text: $searchQuery)
} else { } else {
ZStack(alignment: .bottom) { ZStack(alignment: .bottom) {
ZStack { ZStack {

View file

@ -632,10 +632,12 @@ struct MediaInfoView: View {
@ViewBuilder @ViewBuilder
private var flatEpisodeList: some View { private var flatEpisodeList: some View {
VStack(spacing: 15) { ScrollView {
ForEach(currentEpisodeList.indices.filter { selectedRange.contains($0) }, id: \.self) { i in VStack(spacing: 15) {
let ep = currentEpisodeList[i] ForEach(currentEpisodeList.indices.filter { selectedRange.contains($0) }, id: \.self) { i in
createEpisodeCell(episode: ep, index: i, season: isGroupedBySeasons ? selectedSeason + 1 : 1) let ep = currentEpisodeList[i]
createEpisodeCell(episode: ep, index: i, season: isGroupedBySeasons ? selectedSeason + 1 : 1)
}
} }
} }
} }
@ -644,10 +646,12 @@ struct MediaInfoView: View {
private var seasonsEpisodeList: some View { private var seasonsEpisodeList: some View {
let seasons = groupedEpisodes() let seasons = groupedEpisodes()
if !seasons.isEmpty, selectedSeason < seasons.count { if !seasons.isEmpty, selectedSeason < seasons.count {
VStack(spacing: 15) { ScrollView {
ForEach(seasons[selectedSeason].indices.filter { selectedRange.contains($0) }, id: \.self) { i in VStack(spacing: 15) {
let ep = seasons[selectedSeason][i] ForEach(seasons[selectedSeason].indices.filter { selectedRange.contains($0) }, id: \.self) { i in
createEpisodeCell(episode: ep, index: i, season: selectedSeason + 1) let ep = seasons[selectedSeason][i]
createEpisodeCell(episode: ep, index: i, season: selectedSeason + 1)
}
} }
} }
} else { } else {

View file

@ -69,3 +69,46 @@ struct SearchHistoryRow: View {
} }
} }
} }
struct SearchBar: View {
@Binding var text: String
@Binding var isSearching: Bool
@FocusState private var isFocused: Bool
var body: some View {
HStack {
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.gray)
TextField("Search", text: $text)
.focused($isFocused)
.submitLabel(.search)
.autocorrectionDisabled(true)
.autocapitalization(.none)
.disableAutocorrection(true)
.textInputAutocapitalization(.never)
.onChange(of: text) { _ in
// This triggers search while typing
NotificationCenter.default.post(name: .tabBarSearchQueryUpdated, object: nil, userInfo: ["searchQuery": text])
}
.onSubmit {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
if !text.isEmpty {
Button(action: {
text = ""
isFocused = false
}) {
Image(systemName: "xmark.circle.fill")
.foregroundColor(.gray)
}
}
}
.padding(8)
.background(Color(.systemGray6))
.cornerRadius(10)
}
}
}

View file

@ -20,6 +20,7 @@ struct SearchView: View {
@AppStorage("selectedModuleId") private var selectedModuleId: String? @AppStorage("selectedModuleId") private var selectedModuleId: String?
@AppStorage("mediaColumnsPortrait") private var mediaColumnsPortrait: Int = 2 @AppStorage("mediaColumnsPortrait") private var mediaColumnsPortrait: Int = 2
@AppStorage("mediaColumnsLandscape") private var mediaColumnsLandscape: Int = 4 @AppStorage("mediaColumnsLandscape") private var mediaColumnsLandscape: Int = 4
@AppStorage("useNativeTabBar") private var useNativeTabBar: Bool = false
@StateObject private var jsController = JSController.shared @StateObject private var jsController = JSController.shared
@EnvironmentObject var moduleManager: ModuleManager @EnvironmentObject var moduleManager: ModuleManager
@ -74,51 +75,64 @@ struct SearchView: View {
} }
private var mainContent: some View { private var mainContent: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
HStack { HStack {
Text(LocalizedStringKey("Search")) Text(LocalizedStringKey("Search"))
.font(.largeTitle) .font(.largeTitle)
.fontWeight(.bold) .fontWeight(.bold)
Spacer()
ModuleSelectorMenu(
selectedModule: selectedModule,
moduleGroups: getModuleLanguageGroups(),
modulesByLanguage: getModulesByLanguage(),
selectedModuleId: selectedModuleId,
onModuleSelected: { moduleId in
selectedModuleId = moduleId
}
)
}
.padding(.horizontal, 20)
.padding(.top, 20)
Spacer() if useNativeTabBar {
SearchBar(text: $searchQuery, isSearching: $isSearching)
.padding(.horizontal, 20)
.padding(.top, 10)
}
ModuleSelectorMenu( ScrollView(showsIndicators: false) {
selectedModule: selectedModule, SearchContent(
moduleGroups: getModuleLanguageGroups(), selectedModule: selectedModule,
modulesByLanguage: getModulesByLanguage(), searchQuery: searchQuery,
selectedModuleId: selectedModuleId, searchHistory: searchHistory,
onModuleSelected: { moduleId in searchItems: searchItems,
selectedModuleId = moduleId isSearching: isSearching,
} hasNoResults: hasNoResults,
) columns: columns,
} columnsCount: columnsCount,
.padding(.horizontal, 20) cellWidth: cellWidth,
.padding(.top, 20) onHistoryItemSelected: { query in
searchQuery = query
ScrollView(showsIndicators: false) { searchDebounceTimer?.invalidate()
SearchContent(
selectedModule: selectedModule, UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
searchQuery: searchQuery, NotificationCenter.default.post(name: .tabBarSearchQueryUpdated, object: nil, userInfo: ["searchQuery": query])
searchHistory: searchHistory,
searchItems: searchItems, performSearch()
isSearching: isSearching, },
hasNoResults: hasNoResults, onHistoryItemDeleted: { index in
columns: columns, removeFromHistory(at: index)
columnsCount: columnsCount, },
cellWidth: cellWidth, onClearHistory: clearSearchHistory
onHistoryItemSelected: { query in )
searchQuery = query }
searchDebounceTimer?.invalidate() .scrollViewBottomPadding()
.simultaneousGesture(
DragGesture().onChanged { _ in
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
NotificationCenter.default.post(name: .tabBarSearchQueryUpdated, object: nil, userInfo: ["searchQuery": query]) }
performSearch()
},
onHistoryItemDeleted: { index in
removeFromHistory(at: index)
},
onClearHistory: clearSearchHistory
) )
} }
.scrollViewBottomPadding() .scrollViewBottomPadding()
@ -128,8 +142,6 @@ struct SearchView: View {
} }
) )
} }
.navigationBarHidden(true)
}
var body: some View { var body: some View {
Group { Group {
@ -348,49 +360,3 @@ struct SearchView: View {
return getModulesByLanguage()[language] ?? [] return getModulesByLanguage()[language] ?? []
} }
} }
struct SearchBar: View {
@State private var debounceTimer: Timer?
@Binding var text: String
@Binding var isFocused: Bool
var onSearchButtonClicked: () -> Void
var body: some View {
HStack {
TextField(LocalizedStringKey("Search..."), text: $text, onEditingChanged: { isEditing in
isFocused = isEditing
}, onCommit: onSearchButtonClicked)
.padding(7)
.padding(.horizontal, 25)
.background(Color(.systemGray6))
.cornerRadius(8)
.onChange(of: text) { newValue in
debounceTimer?.invalidate()
if !newValue.isEmpty {
debounceTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { _ in
onSearchButtonClicked()
}
}
}
.overlay(
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.secondary)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 8)
if !text.isEmpty {
Button(action: {
self.text = ""
}) {
Image(systemName: "multiply.circle.fill")
.foregroundColor(.secondary)
.padding(.trailing, 8)
}
}
}
)
}
}
}