Skip to content

Commit

Permalink
Closes #182
Browse files Browse the repository at this point in the history
  • Loading branch information
rasmuslos committed Jan 25, 2025
1 parent 89e9971 commit f890b32
Show file tree
Hide file tree
Showing 22 changed files with 236 additions and 76 deletions.
93 changes: 62 additions & 31 deletions Multiplatform/Audiobook/AudiobookView+Toolbar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ extension AudiobookView {
horizontalSizeClass == .regular
}

private var pdfBinding: Binding<Bool> {
.init { viewModel.presentedPDF != nil } set: {
if !$0 {
viewModel.presentedPDF = nil
}
}
}

func body(content: Content) -> some View {
content
.navigationTitle(viewModel.audiobook.name)
Expand Down Expand Up @@ -53,12 +61,22 @@ extension AudiobookView {
}
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
ToolbarItemGroup(placement: .primaryAction) {
if !viewModel.supplementaryPDFs.isEmpty {
if viewModel.supplementaryPDFs.count == 1 {
if viewModel.loadingPDF {
ProgressIndicator()
} else {
Button("supplementaryPDF.read", systemImage: "book.circle") {
viewModel.presentPDF(viewModel.supplementaryPDFs[0])
}
}
}
}

DownloadButton(item: viewModel.audiobook, downloadingLabel: false, progressIndicator: true)
.labelStyle(.iconOnly)
}

ToolbarItem(placement: .primaryAction) {

Menu {
Button {
viewModel.play()
Expand All @@ -82,38 +100,51 @@ extension AudiobookView {
}

/*
if viewModel.offlineTracker.status == .none {
ProgressButton(item: viewModel.audiobook)
DownloadButton(item: viewModel.audiobook)
} else {
if !viewModel.progressEntity.isFinished {
ProgressButton(item: viewModel.audiobook)
}
Menu {
if viewModel.progressEntity.isFinished {
ProgressButton(item: viewModel.audiobook)
}
if viewModel.progressEntity.startedAt != nil {
Button(role: .destructive) {
viewModel.resetProgress()
} label: {
Label("progress.reset", systemImage: "slash.circle")
}
}
DownloadButton(item: viewModel.audiobook)
} label: {
Text("toolbar.remove")
}
}
if viewModel.offlineTracker.status == .none {
ProgressButton(item: viewModel.audiobook)
DownloadButton(item: viewModel.audiobook)
} else {
if !viewModel.progressEntity.isFinished {
ProgressButton(item: viewModel.audiobook)
}

Menu {
if viewModel.progressEntity.isFinished {
ProgressButton(item: viewModel.audiobook)
}

if viewModel.progressEntity.startedAt != nil {
Button(role: .destructive) {
viewModel.resetProgress()
} label: {
Label("progress.reset", systemImage: "slash.circle")
}
}

DownloadButton(item: viewModel.audiobook)
} label: {
Text("toolbar.remove")
}
}
*/
} label: {
Image(systemName: "ellipsis.circle")
// Image(systemName: "ellipsis.circle")
Label("more", systemImage: "ellipsis.circle")
}
}
}
.fullScreenCover(isPresented: pdfBinding) {
NavigationStack {
PDFViewer(viewModel.presentedPDF!)
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("done") {
viewModel.presentedPDF = nil
}
}
}
}
}
}
}
}
3 changes: 2 additions & 1 deletion Multiplatform/Audiobook/AudiobookView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ struct AudiobookView: View {
.padding(.top, 8)
}
.disclosureGroupStyle(BetterDisclosureGroupStyle(horizontalLabelPadding: 20))
.padding(.bottom, 16)

if !viewModel.supplementaryPDFs.isEmpty {
DisclosureGroup("audiobooks.pdfs", isExpanded: $viewModel.supplementaryPDFsVisible) {
Expand All @@ -62,7 +63,7 @@ struct AudiobookView: View {
}
}
.listStyle(.plain)
.frame(height: minimumHeight * CGFloat(viewModel.chapters.count))
.frame(height: minimumHeight * CGFloat(viewModel.supplementaryPDFs.count))
}
.disclosureGroupStyle(BetterDisclosureGroupStyle(horizontalLabelPadding: 20))
}
Expand Down
43 changes: 43 additions & 0 deletions Multiplatform/Audiobook/AudiobookViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ final class AudiobookViewModel: Sendable {

var library: Library!

var presentedPDF: Data?

var toolbarVisible: Bool
var chaptersVisible: Bool
var sessionsVisible: Bool
Expand All @@ -39,6 +41,8 @@ final class AudiobookViewModel: Sendable {
private(set) var progressEntity: ProgressEntity.UpdatingProgressEntity?
private(set) var downloadTracker: DownloadTracker?

private(set) var loadingPDF: Bool

private(set) var sessions: [SessionPayload]

private(set) var notifyError: Bool
Expand All @@ -64,6 +68,8 @@ final class AudiobookViewModel: Sendable {
sameSeries = [:]
sameNarrator = [:]

loadingPDF = false

sessions = []

notifyError = false
Expand Down Expand Up @@ -97,10 +103,21 @@ extension AudiobookViewModel {
}
}

func presentPDF(_ pdf: PlayableItem.SupplementaryPDF) {
presentedPDF = nil
loadingPDF = true

loadPDF(pdf)
}

nonisolated func play() {
Task {
do {
try await AudioPlayer.shared.play(audiobook)

await MainActor.run {
notifySuccess.toggle()
}
} catch {
await MainActor.run {
notifyError.toggle()
Expand All @@ -113,6 +130,10 @@ extension AudiobookViewModel {
Task {
do {
try await PersistenceManager.shared.progress.delete(itemID: audiobook.id)

await MainActor.run {
notifySuccess.toggle()
}
} catch {
await MainActor.run {
notifyError.toggle()
Expand All @@ -123,6 +144,28 @@ extension AudiobookViewModel {
}

private extension AudiobookViewModel {
nonisolated func loadPDF(_ pdf: PlayableItem.SupplementaryPDF) {
Task {
let audiobookID = await audiobook.id

do {
let data = try await ABSClient[audiobookID.connectionID].pdf(from: audiobookID, ino: pdf.ino)

await MainActor.withAnimation {
notifySuccess.toggle()

self.loadingPDF = false
self.presentedPDF = data
}
} catch {
await MainActor.run {
loadingPDF = false
notifyError.toggle()
}
}
}
}

nonisolated func loadAudiobook() async {
guard let (item, _, chapters, supplementaryPDFs) = try? await ABSClient[audiobook.id.connectionID].playableItem(itemID: audiobook.id) else {
return
Expand Down
13 changes: 7 additions & 6 deletions Multiplatform/Connection/ConnectionAddView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,10 @@ struct ConnectionAddView: View {
SecureField("Password", text: $viewModel.password)
.textContentType(.password)

if viewModel.loading {
ProgressIndicator()
} else {
Button("connection.login") {
viewModel.proceed()
}
Button("connection.login") {
viewModel.proceed()
}
.disabled(viewModel.loading)
case .openID:
ProgressIndicator()
}
Expand Down Expand Up @@ -239,6 +236,10 @@ private final class ViewModel: Sendable {

let client = APIClient(connectionID: "temporary", host: url, headers: headers.compactMap(\.materialized))

#if DEBUG
client.verbose = true
#endif

do {
withAnimation {
loading = true
Expand Down
7 changes: 7 additions & 0 deletions Multiplatform/Control/Satellite.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//
// Satellite.swift
// Multiplatform
//
// Created by Rasmus Krämer on 25.01.25.
//

7 changes: 6 additions & 1 deletion Multiplatform/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -2804,7 +2804,6 @@
}
},
"done" : {
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
Expand Down Expand Up @@ -4656,6 +4655,9 @@
}
}
}
},
"more" : {

},
"Name" : {

Expand Down Expand Up @@ -7202,6 +7204,9 @@
}
}
}
},
"supplementaryPDF.read" : {

},
"timeline" : {
"localizations" : {
Expand Down
40 changes: 40 additions & 0 deletions Multiplatform/Utility/PDFViewer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// PDFViewer.swift
// Multiplatform
//
// Created by Rasmus Krämer on 25.01.25.
//

import PDFKit
import SwiftUI

struct PDFViewer: UIViewRepresentable {
typealias UIViewType = PDFView

let data: Data
let singlePage: Bool

init(_ data: Data, singlePage: Bool = false) {
self.data = data
self.singlePage = singlePage
}

func makeUIView(context _: UIViewRepresentableContext<PDFViewer>) -> UIViewType {
let pdfView = PDFView()

pdfView.document = PDFDocument(data: data)

pdfView.autoScales = true
pdfView.displaysAsBook = true

if singlePage {
pdfView.displayMode = .singlePage
}

return pdfView
}

func updateUIView(_ pdfView: UIViewType, context _: UIViewRepresentableContext<PDFViewer>) {
pdfView.document = PDFDocument(data: data)
}
}
24 changes: 24 additions & 0 deletions ShelfPlayer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
3A0E42EE2C819FEA0034873F /* QueueButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0E42ED2C819FE80034873F /* QueueButton.swift */; };
3A0E42F02C81A1B10034873F /* PodcastViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0E42EF2C81A1AB0034873F /* PodcastViewModel.swift */; };
3A0F99F22BE8C08700F6B7B4 /* UIScreen+Radius.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0F99F12BE8C08700F6B7B4 /* UIScreen+Radius.swift */; };
3A102EAA2D4511340075FA0B /* PDFViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A102EA92D45112F0075FA0B /* PDFViewer.swift */; };
3A102EAD2D4511B20075FA0B /* Satellite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A102EAC2D4511A90075FA0B /* Satellite.swift */; };
3A1507952AD348E700D7D6CF /* PodcastView+EpisodeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1507942AD348E700D7D6CF /* PodcastView+EpisodeList.swift */; };
3A1507992AD4494300D7D6CF /* UINavigationController+Gesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1507982AD4494300D7D6CF /* UINavigationController+Gesture.swift */; };
3A1EAB932ACC80690064C80C /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 3A1EAB922ACC80690064C80C /* NukeUI */; };
Expand Down Expand Up @@ -205,6 +207,8 @@
3A0E42ED2C819FE80034873F /* QueueButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueButton.swift; sourceTree = "<group>"; };
3A0E42EF2C81A1AB0034873F /* PodcastViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PodcastViewModel.swift; sourceTree = "<group>"; };
3A0F99F12BE8C08700F6B7B4 /* UIScreen+Radius.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScreen+Radius.swift"; sourceTree = "<group>"; };
3A102EA92D45112F0075FA0B /* PDFViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PDFViewer.swift; sourceTree = "<group>"; };
3A102EAC2D4511A90075FA0B /* Satellite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Satellite.swift; sourceTree = "<group>"; };
3A1507942AD348E700D7D6CF /* PodcastView+EpisodeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PodcastView+EpisodeList.swift"; sourceTree = "<group>"; };
3A1507982AD4494300D7D6CF /* UINavigationController+Gesture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Gesture.swift"; sourceTree = "<group>"; };
3A1EAB952ACC808A0064C80C /* ItemImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemImage.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -387,6 +391,8 @@
3A0AE6A22AB623F400ACCD68 /* Multiplatform */ = {
isa = PBXGroup;
children = (
3A102EA82D4511280075FA0B /* Utility */,
3A102EAB2D4511A20075FA0B /* Control */,
3A1EAB972ACC822F0064C80C /* Extensions */,
3A31299F2D2413F000D143B4 /* Connection */,
3AADC0352B6D0D3D00C4088D /* Navigation */,
Expand Down Expand Up @@ -443,6 +449,22 @@
path = Audiobook;
sourceTree = "<group>";
};
3A102EA82D4511280075FA0B /* Utility */ = {
isa = PBXGroup;
children = (
3A102EA92D45112F0075FA0B /* PDFViewer.swift */,
);
path = Utility;
sourceTree = "<group>";
};
3A102EAB2D4511A20075FA0B /* Control */ = {
isa = PBXGroup;
children = (
3A102EAC2D4511A90075FA0B /* Satellite.swift */,
);
path = Control;
sourceTree = "<group>";
};
3A1EAB972ACC822F0064C80C /* Extensions */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -942,6 +964,7 @@
3A9824722B6EB0DE0098C06A /* OfflinePodcastView.swift in Sources */,
3A7F84A52CAD1F4A00A51EEE /* UIWindow+Shake.swift in Sources */,
3A47D1872CB991BC0045C2BB /* SecondaryShadow.swift in Sources */,
3A102EAA2D4511340075FA0B /* PDFViewer.swift in Sources */,
3A98246E2B6EAC7F0098C06A /* ProgressIndicator.swift in Sources */,
3AB8BBBD2BD7B7290012023A /* PodcastHomePanel.swift in Sources */,
3A0E42F02C81A1B10034873F /* PodcastViewModel.swift in Sources */,
Expand Down Expand Up @@ -995,6 +1018,7 @@
3A0E42EA2C81927D0034873F /* EpisodeViewModel.swift in Sources */,
3A27F0F12CA15E0F005DE87D /* TabRouter.swift in Sources */,
3AC41F912CC52F3C009ABBFA /* CarPlayPodcastLibraryController.swift in Sources */,
3A102EAD2D4511B20075FA0B /* Satellite.swift in Sources */,
3AA097872C7C5E0600EA32D7 /* Keys+Entries.swift in Sources */,
3AEFA9532B8BB3CA00220CA3 /* String+Random.swift in Sources */,
3A5891152CD3F1EF005C3289 /* SeriesMenu.swift in Sources */,
Expand Down
Loading

0 comments on commit f890b32

Please sign in to comment.