mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-11 17:45:37 +00:00
slide to seek and searchbar fix (#255)
This commit is contained in:
parent
611646902c
commit
0361eb9aed
6 changed files with 115 additions and 42 deletions
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue