Mark all previous watched option added (#34)
Some checks are pending
Build and Release IPA / Build IPA (push) Waiting to run

This commit is contained in:
cranci 2025-03-14 06:40:28 +01:00 committed by GitHub
commit 801390ca70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 78 additions and 39 deletions

View file

@ -758,3 +758,4 @@ class CustomMediaPlayerViewController: UIViewController {
// yes? Like the plural of the famous american rapper ye? -IBHRAD
// low taper fade the meme is massive -cranci
// cranci still doesnt have a job -seiike

View file

@ -32,9 +32,9 @@ struct LibraryView: View {
Image(systemName: "play.circle")
.font(.largeTitle)
.foregroundColor(.secondary)
Text("No items to continue watching")
Text("No items to continue watching.")
.font(.headline)
Text("Recently watched content will appear here")
Text("Recently watched content will appear here.")
.font(.caption)
.foregroundColor(.secondary)
}
@ -58,9 +58,9 @@ struct LibraryView: View {
Image(systemName: "magazine")
.font(.largeTitle)
.foregroundColor(.secondary)
Text("No Items saved")
Text("You have no items saved.")
.font(.headline)
Text("Bookmark items for easy access later")
Text("Bookmark items for an easier access later.")
.font(.caption)
.foregroundColor(.secondary)
}

View file

@ -15,44 +15,31 @@ struct EpisodeLink: Identifiable {
}
struct EpisodeCell: View {
let episodeIndex: Int
let episode: String
let episodeID: Int
let progress: Double
let itemID: Int
let onTap: (String) -> Void
let onMarkAllPrevious: () -> Void
@State private var episodeTitle: String = ""
@State private var episodeImageUrl: String = ""
@State private var isLoading: Bool = true
@State private var currentProgress: Double = 0.0
let onTap: (String) -> Void
private func markAsWatched() {
UserDefaults.standard.set(99999999.0, forKey: "lastPlayedTime_\(episode)")
UserDefaults.standard.set(99999999.0, forKey: "totalTime_\(episode)")
updateProgress()
}
private func resetProgress() {
UserDefaults.standard.set(0.0, forKey: "lastPlayedTime_\(episode)")
UserDefaults.standard.set(0.0, forKey: "totalTime_\(episode)")
updateProgress()
}
private func updateProgress() {
let lastPlayedTime = UserDefaults.standard.double(forKey: "lastPlayedTime_\(episode)")
let totalTime = UserDefaults.standard.double(forKey: "totalTime_\(episode)")
currentProgress = totalTime > 0 ? lastPlayedTime / totalTime : 0
}
var body: some View {
HStack {
ZStack {
KFImage(URL(string: episodeImageUrl.isEmpty ? "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/main/assets/banner2.png" : episodeImageUrl))
.resizable()
.aspectRatio(16/9, contentMode: .fill)
.frame(width: 100, height: 56)
.cornerRadius(8)
KFImage(URL(string: episodeImageUrl.isEmpty
? "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/main/assets/banner2.png"
: episodeImageUrl))
.resizable()
.aspectRatio(16/9, contentMode: .fill)
.frame(width: 100, height: 56)
.cornerRadius(8)
if isLoading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle())
@ -76,31 +63,56 @@ struct EpisodeCell: View {
}
.contentShape(Rectangle())
.contextMenu {
if currentProgress <= 0.9 {
if progress <= 0.9 {
Button(action: markAsWatched) {
Label("Mark as Watched", systemImage: "checkmark.circle")
}
}
if currentProgress != 0 {
// Only show reset if progress is nonzero
if progress != 0 {
Button(action: resetProgress) {
Label("Reset Progress", systemImage: "arrow.counterclockwise")
}
}
if episodeIndex > 0 {
Button(action: onMarkAllPrevious) {
Label("Mark All Previous Watched", systemImage: "checkmark.circle.fill")
}
}
}
.onAppear {
if UserDefaults.standard.object(forKey: "fetchEpisodeMetadata") == nil ||
UserDefaults.standard.bool(forKey: "fetchEpisodeMetadata") {
if UserDefaults.standard.object(forKey: "fetchEpisodeMetadata") == nil
|| UserDefaults.standard.bool(forKey: "fetchEpisodeMetadata") {
fetchEpisodeDetails()
}
updateProgress()
currentProgress = progress
}
.onTapGesture {
onTap(episodeImageUrl)
}
}
func fetchEpisodeDetails() {
private func markAsWatched() {
UserDefaults.standard.set(99999999.0, forKey: "lastPlayedTime_\(episode)")
UserDefaults.standard.set(99999999.0, forKey: "totalTime_\(episode)")
updateProgress()
}
private func resetProgress() {
UserDefaults.standard.set(0.0, forKey: "lastPlayedTime_\(episode)")
UserDefaults.standard.set(0.0, forKey: "totalTime_\(episode)")
updateProgress()
}
private func updateProgress() {
let lastPlayedTime = UserDefaults.standard.double(forKey: "lastPlayedTime_\(episode)")
let totalTime = UserDefaults.standard.double(forKey: "totalTime_\(episode)")
currentProgress = totalTime > 0 ? lastPlayedTime / totalTime : 0
}
private func fetchEpisodeDetails() {
guard let url = URL(string: "https://api.ani.zip/mappings?anilist_id=\(itemID)") else {
isLoading = false
return

View file

@ -34,6 +34,8 @@ struct MediaInfoView: View {
@State var isRefetching: Bool = true
@State var isFetchingEpisode: Bool = false
@State private var refreshTrigger: Bool = false
@State private var selectedEpisodeNumber: Int = 0
@State private var selectedEpisodeImage: String = ""
@ -54,6 +56,8 @@ struct MediaInfoView: View {
} else {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
// MARK: - Top media info
HStack(alignment: .top, spacing: 10) {
KFImage(URL(string: imageUrl))
.placeholder {
@ -121,6 +125,7 @@ struct MediaInfoView: View {
}
}
// MARK: - Synopsis section
if !synopsis.isEmpty {
VStack(alignment: .leading, spacing: 2) {
HStack(alignment: .center) {
@ -144,6 +149,7 @@ struct MediaInfoView: View {
}
}
// MARK: - Action buttons
HStack {
Button(action: {
playFirstUnwatchedEpisode()
@ -204,21 +210,41 @@ struct MediaInfoView: View {
}
}
//MARK: - Mark all prevoius logic
ForEach(episodeLinks.indices.filter { selectedRange.contains($0) }, id: \.self) { i in
let ep = episodeLinks[i]
let lastPlayedTime = UserDefaults.standard.double(forKey: "lastPlayedTime_\(ep.href)")
let totalTime = UserDefaults.standard.double(forKey: "totalTime_\(ep.href)")
let progress = totalTime > 0 ? lastPlayedTime / totalTime : 0
EpisodeCell(episode: ep.href, episodeID: ep.number - 1, progress: progress, itemID: itemID ?? 0, onTap: { imageUrl in
EpisodeCell(
episodeIndex: i,
episode: ep.href,
episodeID: ep.number - 1,
progress: progress,
itemID: itemID ?? 0,
onTap: { imageUrl in
if !isFetchingEpisode {
selectedEpisodeNumber = ep.number
selectedEpisodeImage = imageUrl
fetchStream(href: ep.href)
AnalyticsManager.shared.sendEvent(event: "watch", additionalData: ["title": title, "episode": ep.number])
AnalyticsManager.shared.sendEvent(
event: "watch",
additionalData: ["title": title, "episode": ep.number]
)
}
},
onMarkAllPrevious: {
for idx in 0..<i {
let href = episodeLinks[idx].href
UserDefaults.standard.set(99999999.0, forKey: "lastPlayedTime_\(href)")
UserDefaults.standard.set(99999999.0, forKey: "totalTime_\(href)")
}
refreshTrigger.toggle() // Force the UI to refresh
Logger.shared.log("Marked \(ep.number) episodes watched within anime \"\(title)\".", type: "General")
}
)
.id(refreshTrigger) // Attaches the refresh trigger so that changes re-create the cell
.disabled(isFetchingEpisode)
}
}
@ -262,7 +288,7 @@ struct MediaInfoView: View {
}
.onAppear {
if !hasFetched {
DropManager.shared.showDrop(title: "Fetching Data", subtitle: "Please wait while fetching", duration: 1.0, icon: UIImage(systemName: "arrow.triangle.2.circlepath"))
DropManager.shared.showDrop(title: "Fetching Data", subtitle: "Please wait while fetching.", duration: 1.0, icon: UIImage(systemName: "arrow.triangle.2.circlepath"))
fetchDetails()
fetchItemID(byTitle: title) { result in
switch result {