diff --git a/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift b/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift index 1f5456b..302bc79 100644 --- a/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift +++ b/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift @@ -24,7 +24,6 @@ struct EpisodeCell: View { let tmdbID: Int? let seasonNumber: Int? - //receives the set of filler episode numbers (from MediaInfoView) let fillerEpisodes: Set? let isMultiSelectMode: Bool @@ -43,14 +42,17 @@ struct EpisodeCell: View { @State private var downloadAnimationScale: CGFloat = 1.0 @State private var activeDownloadTask: AVAssetDownloadTask? +<<<<<<< HEAD @State private var swipeOffset: CGFloat = 0 @State private var isShowingActions: Bool = false @State private var actionButtonWidth: CGFloat = 60 @State private var dragState: DragState = .inactive @State private var dragStart: CGPoint? +======= +>>>>>>> f53cc505fc93609c8cfee2e1306c57094bd93de5 @State private var retryAttempts: Int = 0 - private var malIDFromParent: Int? { malID } + private var malIDFromParent: Int? { malID } private let maxRetryAttempts: Int = 3 private let initialBackoffDelay: TimeInterval = 1.0 @@ -59,8 +61,7 @@ struct EpisodeCell: View { @Environment(\.colorScheme) private var colorScheme @AppStorage("selectedAppearance") private var selectedAppearance: Appearance = .system @AppStorage("remainingTimePercentage") private var remainingTimePercentage: Double = 90.0 - - // Filler state (derived from passed-in fillerEpisodes) + @State private var isFiller: Bool = false init( @@ -84,7 +85,7 @@ struct EpisodeCell: View { seasonNumber: Int? = nil, fillerEpisodes: Set? = nil ) { - + self.episodeIndex = episodeIndex self.episode = episode self.episodeID = episodeID @@ -117,44 +118,43 @@ struct EpisodeCell: View { var body: some View { episodeCellContent - .onAppear { - setupOnAppear() - // set filler state based on passed-in set (if available) - let epNum = episodeID + 1 - if let set = fillerEpisodes { - self.isFiller = set.contains(epNum) + .onAppear { + setupOnAppear() + let epNum = episodeID + 1 + if let set = fillerEpisodes { + self.isFiller = set.contains(epNum) + } } - } - .onChange(of: progress) { _ in updateProgress() } - .onChange(of: itemID) { _ in handleItemIDChange() } - .onChange(of: tmdbID) { _ in - isLoading = true - retryAttempts = 0 - fetchEpisodeDetails() - } - .onChange(of: fillerEpisodes) { newValue in - let epNum = episodeID + 1 - if let set = newValue { - self.isFiller = set.contains(epNum) - } else { - self.isFiller = false + .onChange(of: progress) { _ in updateProgress() } + .onChange(of: itemID) { _ in handleItemIDChange() } + .onChange(of: tmdbID) { _ in + isLoading = true + retryAttempts = 0 + fetchEpisodeDetails() } - } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("downloadProgressChanged"))) { _ in - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + .onChange(of: fillerEpisodes) { newValue in + let epNum = episodeID + 1 + if let set = newValue { + self.isFiller = set.contains(epNum) + } else { + self.isFiller = false + } + } + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("downloadProgressChanged"))) { _ in + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + updateDownloadStatus() + updateProgress() + } + } + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("downloadStatusChanged"))) { _ in updateDownloadStatus() + } + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("downloadCompleted"))) { _ in + updateDownloadStatus() + } + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("episodeProgressChanged"))) { _ in updateProgress() } - } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("downloadStatusChanged"))) { _ in - updateDownloadStatus() - } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("downloadCompleted"))) { _ in - updateDownloadStatus() - } - .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("episodeProgressChanged"))) { _ in - updateProgress() - } } } @@ -218,6 +218,45 @@ private extension EpisodeCell { actionButtonsBackground } +<<<<<<< HEAD +======= + .contentShape(Rectangle()) + .padding(.horizontal, 8) + .padding(.vertical, 8) + .frame(maxWidth: .infinity) + .background(cellBackground) + .clipShape(RoundedRectangle(cornerRadius: 15)) + .contextMenu { contextMenuContent } + .swipeActions(edge: .trailing) { + + Button(action: { downloadEpisode() }) { + Label("Download", systemImage: "arrow.down.circle") + } + .tint(.blue) + + if progress <= remainingTimePercentage { + Button(action: { markAsWatched() }) { + Label("Watched", systemImage: "checkmark.circle") + } + .tint(.green) + } + + if progress != 0 { + Button(action: { resetProgress() }) { + Label("Reset", systemImage: "arrow.counterclockwise") + } + .tint(.orange) + } + + if episodeIndex > 0 { + Button(action: { onMarkAllPrevious() }) { + Label("All Prev", systemImage: "checkmark.circle.fill") + } + .tint(.purple) + } + } + .onTapGesture { handleTap() } +>>>>>>> f53cc505fc93609c8cfee2e1306c57094bd93de5 } var cellBackground: some View { @@ -298,7 +337,7 @@ private extension EpisodeCell { var contextMenuContent: some View { Group { - if progress >= (remainingTimePercentage / 100.0) { + if progress <= remainingTimePercentage { Button(action: markAsWatched) { Label("Mark Episode as Watched", systemImage: "checkmark.circle") } @@ -371,6 +410,7 @@ private extension EpisodeCell { } private extension EpisodeCell { +<<<<<<< HEAD enum DragState { case inactive @@ -466,6 +506,8 @@ private extension EpisodeCell { } } +======= +>>>>>>> f53cc505fc93609c8cfee2e1306c57094bd93de5 func handleTap() { if isShowingActions { withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) { @@ -480,6 +522,7 @@ private extension EpisodeCell { } } +<<<<<<< HEAD func calculateMaxSwipeDistance() -> CGFloat { var buttonCount = 1 @@ -509,6 +552,11 @@ private extension EpisodeCell { } } +======= +} + +private extension EpisodeCell { +>>>>>>> f53cc505fc93609c8cfee2e1306c57094bd93de5 func markAsWatched() { let defaults = UserDefaults.standard let totalTime = 1000.0 @@ -533,7 +581,6 @@ private extension EpisodeCell { } } } - func resetProgress() { let userDefaults = UserDefaults.standard @@ -993,7 +1040,6 @@ private extension EpisodeCell { } }.resume() } - func handleFetchFailure(error: Error) { Logger.shared.log("Episode details fetch error: \(error.localizedDescription)", type: "Error")