slide to seek and searchbar fix (#255)
Some checks failed
Build and Release / Build IPA (push) Has been cancelled
Build and Release / Build macOS App (push) Has been cancelled

This commit is contained in:
Mousica 2025-11-28 18:33:59 +02:00 committed by GitHub
parent 611646902c
commit 0361eb9aed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 115 additions and 42 deletions

View file

@ -2021,12 +2021,12 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
let remainingPercentage = (self.duration - self.currentTimeVal) / self.duration let remainingPercentage = (self.duration - self.currentTimeVal) / self.duration
let remainingTimePercentage = UserDefaults.standard.object(forKey: "remainingTimePercentage") != nil ? UserDefaults.standard.double(forKey: "remainingTimePercentage") : 90.0 let remainingTimePercentage = UserDefaults.standard.object(forKey: "remainingTimePercentage") != nil ? UserDefaults.standard.double(forKey: "remainingTimePercentage") : 90.0
let threshold = (100.0 - remainingTimePercentage) / 100.0 let threshold = (100.0 - remainingTimePercentage) / 100.0
if remainingPercentage <= threshold { if remainingPercentage <= threshold {
if self.aniListID != 0 && !self.aniListUpdatedSuccessfully && !self.aniListUpdateImpossible { if self.aniListID != 0 && !self.aniListUpdatedSuccessfully && !self.aniListUpdateImpossible {
self.tryAniListUpdate() self.tryAniListUpdate()
} }
if let tmdbId = self.tmdbID, tmdbId > 0, !self.traktUpdateSent { if let tmdbId = self.tmdbID, tmdbId > 0, !self.traktUpdateSent {
self.sendTraktUpdate(tmdbId: tmdbId) self.sendTraktUpdate(tmdbId: tmdbId)
} }

View file

@ -324,12 +324,12 @@ class VideoPlayerViewController: UIViewController {
let remainingPercentage = (duration - currentTime) / duration let remainingPercentage = (duration - currentTime) / duration
let remainingTimePercentage = UserDefaults.standard.object(forKey: "remainingTimePercentage") != nil ? UserDefaults.standard.double(forKey: "remainingTimePercentage") : 90.0 let remainingTimePercentage = UserDefaults.standard.object(forKey: "remainingTimePercentage") != nil ? UserDefaults.standard.double(forKey: "remainingTimePercentage") : 90.0
let threshold = (100.0 - remainingTimePercentage) / 100.0 let threshold = (100.0 - remainingTimePercentage) / 100.0
if remainingPercentage <= threshold { if remainingPercentage <= threshold {
if self.aniListID != 0 && !self.aniListUpdateSent { if self.aniListID != 0 && !self.aniListUpdateSent {
self.sendAniListUpdate() self.sendAniListUpdate()
} }
if let tmdbId = self.tmdbID, tmdbId > 0, !self.traktUpdateSent { if let tmdbId = self.tmdbID, tmdbId > 0, !self.traktUpdateSent {
self.sendTraktUpdate(tmdbId: tmdbId) self.sendTraktUpdate(tmdbId: tmdbId)
} }

View file

@ -25,8 +25,8 @@ struct CircularProgressBar: View {
.animation(.linear, value: progress) .animation(.linear, value: progress)
let remainingTimePercentage = UserDefaults.standard.object(forKey: "remainingTimePercentage") != nil ? UserDefaults.standard.double(forKey: "remainingTimePercentage") : 90.0 let remainingTimePercentage = UserDefaults.standard.object(forKey: "remainingTimePercentage") != nil ? UserDefaults.standard.double(forKey: "remainingTimePercentage") : 90.0
let threshold = (100.0 - remainingTimePercentage) / 100.0 let threshold = remainingTimePercentage / 100.0
if progress >= threshold { if progress >= threshold {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.font(.system(size: 12)) .font(.system(size: 12))

View file

@ -223,7 +223,7 @@ private extension EpisodeCell {
} }
.tint(.blue) .tint(.blue)
if progress <= remainingTimePercentage { if progress >= remainingTimePercentage / 100.0 {
Button(action: { markAsWatched() }) { Button(action: { markAsWatched() }) {
Label("Watched", systemImage: "checkmark.circle") Label("Watched", systemImage: "checkmark.circle")
} }
@ -325,7 +325,7 @@ private extension EpisodeCell {
var contextMenuContent: some View { var contextMenuContent: some View {
Group { Group {
if progress <= remainingTimePercentage { if progress >= remainingTimePercentage / 100.0 {
Button(action: markAsWatched) { Button(action: markAsWatched) {
Label("Mark Episode as Watched", systemImage: "checkmark.circle") Label("Mark Episode as Watched", systemImage: "checkmark.circle")
} }

View file

@ -40,6 +40,7 @@ struct SearchView: View {
@State private var saveDebounceTimer: Timer? @State private var saveDebounceTimer: Timer?
@State private var searchDebounceTimer: Timer? @State private var searchDebounceTimer: Timer?
@State private var isActive: Bool = false @State private var isActive: Bool = false
@State private var currentSearchTask: Task<Void, Never>?
init(searchQuery: Binding<String>) { init(searchQuery: Binding<String>) {
self._searchQuery = searchQuery self._searchQuery = searchQuery
@ -244,40 +245,60 @@ struct SearchView: View {
hasNoResults = false hasNoResults = false
return return
} }
isSearchFieldFocused = false isSearchFieldFocused = false
currentSearchTask?.cancel()
currentSearchTask = nil
isSearching = true isSearching = true
hasNoResults = false hasNoResults = false
searchItems = [] searchItems = []
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { currentSearchTask = Task {
Task { do {
do { try await Task.sleep(nanoseconds: 500_000_000) // 0.5 seconds
let jsContent = try moduleManager.getModuleContent(module) guard !Task.isCancelled else { return }
jsController.loadScript(jsContent)
if module.metadata.asyncJS == true { let jsContent = try moduleManager.getModuleContent(module)
jsController.fetchJsSearchResults(keyword: searchQuery, module: module) { items in jsController.loadScript(jsContent)
DispatchQueue.main.async {
searchItems = items guard !Task.isCancelled else { return }
hasNoResults = items.isEmpty
isSearching = false if module.metadata.asyncJS == true {
} jsController.fetchJsSearchResults(keyword: searchQuery, module: module) { items in
} guard !Task.isCancelled else { return }
} else { DispatchQueue.main.async {
jsController.fetchSearchResults(keyword: searchQuery, module: module) { items in let uniqueItems = items.reduce(into: [String: SearchItem]()) { dict, item in
DispatchQueue.main.async { dict[item.href] = item
searchItems = items }.values
hasNoResults = items.isEmpty searchItems = Array(uniqueItems)
isSearching = false hasNoResults = uniqueItems.isEmpty
} isSearching = false
currentSearchTask = nil
} }
} }
} catch { } else {
jsController.fetchSearchResults(keyword: searchQuery, module: module) { items in
guard !Task.isCancelled else { return }
DispatchQueue.main.async {
let uniqueItems = items.reduce(into: [String: SearchItem]()) { dict, item in
dict[item.href] = item
}.values
searchItems = Array(uniqueItems)
hasNoResults = uniqueItems.isEmpty
isSearching = false
currentSearchTask = nil
}
}
}
} catch {
if !Task.isCancelled {
Logger.shared.log("Error loading module: \(error)", type: "Error") Logger.shared.log("Error loading module: \(error)", type: "Error")
DispatchQueue.main.async { DispatchQueue.main.async {
isSearching = false isSearching = false
hasNoResults = true hasNoResults = true
currentSearchTask = nil
} }
} }
} }

View file

@ -156,7 +156,7 @@ fileprivate struct SettingsStepperRow: View {
let step: Double let step: Double
var formatter: (Double) -> String = { "\(Int($0))" } var formatter: (Double) -> String = { "\(Int($0))" }
var showDivider: Bool = true var showDivider: Bool = true
init(icon: String, title: String, value: Binding<Double>, range: ClosedRange<Double>, step: Double, formatter: @escaping (Double) -> String = { "\(Int($0))" }, showDivider: Bool = true) { init(icon: String, title: String, value: Binding<Double>, range: ClosedRange<Double>, step: Double, formatter: @escaping (Double) -> String = { "\(Int($0))" }, showDivider: Bool = true) {
self.icon = icon self.icon = icon
self.title = title self.title = title
@ -166,24 +166,77 @@ fileprivate struct SettingsStepperRow: View {
self.formatter = formatter self.formatter = formatter
self.showDivider = showDivider self.showDivider = showDivider
} }
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
HStack { HStack {
Image(systemName: icon) Image(systemName: icon)
.frame(width: 24, height: 24) .frame(width: 24, height: 24)
.foregroundStyle(.primary) .foregroundStyle(.primary)
Text(title) Text(title)
.foregroundStyle(.primary) .foregroundStyle(.primary)
Spacer() Spacer()
Stepper(formatter(value), value: $value, in: range, step: step) Stepper(formatter(value), value: $value, in: range, step: step)
} }
.padding(.horizontal, 16) .padding(.horizontal, 16)
.padding(.vertical, 12) .padding(.vertical, 12)
if showDivider {
Divider()
.padding(.horizontal, 16)
}
}
}
}
fileprivate struct SettingsTextFieldRow: View {
let icon: String
let title: String
@Binding var value: Double
let range: ClosedRange<Double>
var showDivider: Bool = true
init(icon: String, title: String, value: Binding<Double>, range: ClosedRange<Double>, showDivider: Bool = true) {
self.icon = icon
self.title = title
self._value = value
self.range = range
self.showDivider = showDivider
}
var body: some View {
VStack(spacing: 0) {
HStack {
Image(systemName: icon)
.frame(width: 24, height: 24)
.foregroundStyle(.primary)
Text(title)
.foregroundStyle(.primary)
Spacer()
TextField("", value: $value, format: .number)
.keyboardType(.decimalPad)
.multilineTextAlignment(.trailing)
.frame(width: 60)
.onChange(of: value) { newValue in
if newValue < range.lowerBound {
value = range.lowerBound
} else if newValue > range.upperBound {
value = range.upperBound
}
}
Text("%")
.foregroundStyle(.gray)
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
if showDivider { if showDivider {
Divider() Divider()
.padding(.horizontal, 16) .padding(.horizontal, 16)
@ -247,12 +300,11 @@ struct SettingsViewPlayer: View {
showDivider: true showDivider: true
) )
SettingsPickerRow( SettingsTextFieldRow(
icon: "timer", icon: "timer",
title: NSLocalizedString("Completion Percentage", comment: ""), title: NSLocalizedString("Completion Percentage", comment: ""),
options: [60.0, 70.0, 80.0, 90.0, 95.0, 100.0], value: $remainingTimePercentage,
optionToString: { "\(Int($0))%" }, range: 0...100,
selection: $remainingTimePercentage,
showDivider: false showDivider: false
) )
} }