diff --git a/Multiplatform/Account/Account+Downloads.swift b/Multiplatform/Account/Account+Downloads.swift new file mode 100644 index 00000000..070ac477 --- /dev/null +++ b/Multiplatform/Account/Account+Downloads.swift @@ -0,0 +1,106 @@ +// +// Account+Downloads.swift +// Multiplatform +// +// Created by Rasmus Krämer on 06.05.24. +// + +import SwiftUI +import SPBase +import SPOffline +import SPOfflineExtended + +extension AccountSheet { + struct Downloads: View { + @State private var downloadStatus: OfflineManager.DownloadStatus? + + var body: some View { + Section("account.downloads") { + if let downloadStatus = downloadStatus, !(downloadStatus.0.isEmpty && downloadStatus.1.isEmpty) { + ForEach(Array(downloadStatus.0.keys).sorted { $0.name.localizedStandardCompare($1.name) == .orderedDescending }) { audiobook in + HStack { + ItemImage(image: audiobook.image) + .frame(width: 55) + + VStack(alignment: .leading) { + Text(audiobook.name) + .modifier(SerifModifier()) + if let author = audiobook.author { + Text(author) + .font(.subheadline) + .foregroundStyle(.secondary) + } + } + .lineLimit(1) + + Spacer() + + if let status = downloadStatus.0[audiobook] { + if status.0 == 0 && status.1 == 1 { + ProgressIndicator() + } else { + Text(verbatim: "\(status.0)/\(status.1)") + .fontDesign(.rounded) + .foregroundStyle(.secondary) + } + } + } + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + Button(role: .destructive) { + OfflineManager.shared.delete(audiobookId: audiobook.id) + } label: { + Label("download.remove", systemImage: "trash.fill") + .labelStyle(.iconOnly) + } + } + } + + ForEach(Array(downloadStatus.1.keys).sorted { $0.name.localizedStandardCompare($1.name) == .orderedDescending }) { podcast in + HStack { + ItemImage(image: podcast.image) + .frame(width: 55) + + VStack(alignment: .leading) { + Text(podcast.name) + + if let author = podcast.author { + Text(author) + .font(.subheadline) + .foregroundStyle(.secondary) + } + } + .lineLimit(1) + + Spacer() + + if let status = downloadStatus.1[podcast] { + Text(verbatim: "\(status.0)/\(status.1)") + .fontDesign(.rounded) + .foregroundStyle(.secondary) + } + } + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + Button(role: .destructive) { + try! OfflineManager.shared.delete(podcastId: podcast.id) + } label: { + Label("download.remove", systemImage: "trash.fill") + .labelStyle(.iconOnly) + } + } + } + } else { + Text("accounts.downloads.empty") + .foregroundStyle(.secondary) + } + } + .task { + downloadStatus = try? await OfflineManager.shared.getDownloadStatus() + } + .onReceive(NotificationCenter.default.publisher(for: PlayableItem.downloadStatusUpdatedNotification)) { _ in + Task.detached { @MainActor in + downloadStatus = try? await OfflineManager.shared.getDownloadStatus() + } + } + } + } +} diff --git a/Multiplatform/Account/AccountSheet.swift b/Multiplatform/Account/AccountSheet.swift index 7e5e24ff..ca08a2e8 100644 --- a/Multiplatform/Account/AccountSheet.swift +++ b/Multiplatform/Account/AccountSheet.swift @@ -16,8 +16,6 @@ struct AccountSheet: View { @Default(.customSleepTimer) private var customSleepTimer @State private var username: String? - @State private var downloadStatus: OfflineManager.DownloadStatus? - @State private var notificationPermission: UNAuthorizationStatus = .notDetermined var body: some View { @@ -32,11 +30,13 @@ struct AccountSheet: View { username = try? await AudiobookshelfClient.shared.username() } } + Button(role: .destructive) { OfflineManager.shared.deleteProgressEntities() AudiobookshelfClient.shared.logout() } label: { - Text("account.logout") + Label("account.logout", systemImage: "person.crop.circle.badge.minus") + .foregroundStyle(.red) } } header: { Text("account.user") @@ -45,155 +45,52 @@ struct AccountSheet: View { } Section { - Button { - UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!) - } label: { - Text("account.settings") - } - - Button { - Task { - try? await BackgroundTaskHandler.runAutoDownload() + Group { + Button { + UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!) + } label: { + Label("account.settings", systemImage: "gear") } - } label: { - Text("account.newEpisodes.check") - } - - switch notificationPermission { - case .notDetermined: - Button { - Task { - try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge]) - notificationPermission = await UNUserNotificationCenter.current().notificationSettings().authorizationStatus - } - } label: { - Text("account.notifications.request") - } - .task { - notificationPermission = await UNUserNotificationCenter.current().notificationSettings().authorizationStatus - } - case .denied: - Text("account.notifications.denied") - .foregroundStyle(.red) - case .authorized: - Text("account.notifications.granted") - .foregroundStyle(.secondary) - default: - Text("account.notifications.unknown") - .foregroundStyle(.red) - } - } footer: { - Text("account.notifications.footer") - } - - Section { - Button(role: .destructive) { - OfflineManager.shared.deleteDownloads() - } label: { - Text("account.delete.downloads") - } - - Button(role: .destructive) { - ImagePipeline.shared.cache.removeAll() - OfflineManager.shared.deleteProgressEntities() - NotificationCenter.default.post(name: Library.libraryChangedNotification, object: nil, userInfo: [ - "offline": false, - ]) - } label: { - Text("account.delete.cache") - } - } footer: { - Text("account.delete.footer") - } - - Section("account.downloads") { - if let downloadStatus = downloadStatus, !(downloadStatus.0.isEmpty && downloadStatus.1.isEmpty) { - ForEach(Array(downloadStatus.0.keys).sorted { $0.name.localizedStandardCompare($1.name) == .orderedDescending }) { audiobook in - HStack { - ItemImage(image: audiobook.image) - .frame(width: 55) - - VStack(alignment: .leading) { - Text(audiobook.name) - .modifier(SerifModifier()) - if let author = audiobook.author { - Text(author) - .font(.subheadline) - .foregroundStyle(.secondary) - } - } - .lineLimit(1) - - Spacer() - - if let status = downloadStatus.0[audiobook] { - if status.0 == 0 && status.1 == 1 { - ProgressIndicator() - } else { - Text(verbatim: "\(status.0)/\(status.1)") - .fontDesign(.rounded) - .foregroundStyle(.secondary) + switch notificationPermission { + case .notDetermined: + Button { + Task { + try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge]) + notificationPermission = await UNUserNotificationCenter.current().notificationSettings().authorizationStatus } - } - } - .swipeActions(edge: .trailing, allowsFullSwipe: true) { - Button(role: .destructive) { - OfflineManager.shared.delete(audiobookId: audiobook.id) } label: { - Label("download.remove", systemImage: "trash.fill") - .labelStyle(.iconOnly) + Label("account.notifications.request", systemImage: "bell.badge.waveform.fill") } - } + .task { + notificationPermission = await UNUserNotificationCenter.current().notificationSettings().authorizationStatus + } + case .denied: + Text("account.notifications.denied") + .foregroundStyle(.red) + case .authorized: + Text("account.notifications.granted") + .foregroundStyle(.secondary) + default: + Text("account.notifications.unknown") + .foregroundStyle(.red) } - ForEach(Array(downloadStatus.1.keys).sorted { $0.name.localizedStandardCompare($1.name) == .orderedDescending }) { podcast in - HStack { - ItemImage(image: podcast.image) - .frame(width: 55) - - VStack(alignment: .leading) { - Text(podcast.name) - - if let author = podcast.author { - Text(author) - .font(.subheadline) - .foregroundStyle(.secondary) - } - } - .lineLimit(1) - - Spacer() - - if let status = downloadStatus.1[podcast] { - Text(verbatim: "\(status.0)/\(status.1)") - .fontDesign(.rounded) - .foregroundStyle(.secondary) - } - } - .swipeActions(edge: .trailing, allowsFullSwipe: true) { - Button(role: .destructive) { - try! OfflineManager.shared.delete(podcastId: podcast.id) - } label: { - Label("download.remove", systemImage: "trash.fill") - .labelStyle(.iconOnly) - } + Button { + Task { + try? await BackgroundTaskHandler.runAutoDownload() } + } label: { + Label("account.newEpisodes.check", systemImage: "antenna.radiowaves.left.and.right") } - } else { - Text("accounts.downloads.empty") - .foregroundStyle(.secondary) - } - } - .task { - downloadStatus = try? await OfflineManager.shared.getDownloadStatus() - } - .onReceive(NotificationCenter.default.publisher(for: PlayableItem.downloadStatusUpdatedNotification)) { _ in - Task.detached { @MainActor in - downloadStatus = try? await OfflineManager.shared.getDownloadStatus() } + .foregroundStyle(.primary) + } footer: { + Text("account.notifications.footer") } + Downloads() + Section { let hours = customSleepTimer / 60 let minutes = customSleepTimer % 60 @@ -214,34 +111,59 @@ struct AccountSheet: View { } Section { - NavigationLink(destination: AccentColorSelectionView()) { - Label("account.tint", systemImage: "circle.dashed") - } + TintMenu() + .menuStyle(.borderlessButton) NavigationLink(destination: CustomHeaderEditView()) { Label("login.customHTTPHeaders", systemImage: "network.badge.shield.half.filled") } } - .foregroundStyle(.secondary) + .foregroundStyle(.primary) Section { Button { UIApplication.shared.open(URL(string: "https://github.com/rasmuslos/ShelfPlayer")!) } label: { - Text("account.github") + Label("account.github", systemImage: "chevron.left.forwardslash.chevron.right") } Button { UIApplication.shared.open(URL(string: "https://rfk.io/support.htm")!) } label: { - Text("account.support") + Label("account.support", systemImage: "lifepreserver") + } + } + .foregroundStyle(.primary) + + Section { + Group { + Button(role: .destructive) { + OfflineManager.shared.deleteDownloads() + } label: { + Label("account.delete.downloads", systemImage: "slash.circle") + } + + Button(role: .destructive) { + ImagePipeline.shared.cache.removeAll() + OfflineManager.shared.deleteProgressEntities() + + NotificationCenter.default.post(name: Library.libraryChangedNotification, object: nil, userInfo: [ + "offline": false, + ]) + } label: { + Label("account.delete.cache", systemImage: "square.stack.3d.up.slash") + } } + .foregroundStyle(.red) + } footer: { + Text("account.delete.footer") } Group { Section("account.server") { - Text(AudiobookshelfClient.shared.token) Text(AudiobookshelfClient.shared.serverUrl.absoluteString) + Text(AudiobookshelfClient.shared.clientId) } + .fontDesign(.monospaced) Section { Text("account.version \(AudiobookshelfClient.shared.clientVersion) (\(AudiobookshelfClient.shared.clientBuild))") @@ -249,19 +171,6 @@ struct AccountSheet: View { } .font(.caption) .foregroundStyle(.secondary) - - #if !ENABLE_ALL_FEATURES - Section { - HStack { - Spacer() - Text("developedBy") - .font(.caption) - .foregroundStyle(.secondary) - Spacer() - } - } - .listRowBackground(Color.clear) - #endif } .navigationTitle("account.manage") } diff --git a/Multiplatform/Account/CustomHeaderEditView.swift b/Multiplatform/Account/CustomHeaderEditView.swift index 5ea68ef5..2e2a7b80 100644 --- a/Multiplatform/Account/CustomHeaderEditView.swift +++ b/Multiplatform/Account/CustomHeaderEditView.swift @@ -34,25 +34,29 @@ struct CustomHeaderEditView: View { } } - Button { - current.append(.init(key: "", value: "")) - } label: { - Text("login.customHTTPHeaders.add") - } - Button { - AudiobookshelfClient.shared.customHTTPHeaders = current - callback?() - } label: { - Text("login.customHTTPHeaders.save") + Group { + Button { + current.append(.init(key: "", value: "")) + } label: { + Label("login.customHTTPHeaders.add", systemImage: "plus") + } + Button { + AudiobookshelfClient.shared.customHTTPHeaders = current + callback?() + } label: { + Label("login.customHTTPHeaders.save", systemImage: "checkmark") + } } + .foregroundStyle(.primary) if let callback = callback { Button(role: .destructive) { AudiobookshelfClient.shared.customHTTPHeaders = current callback() } label: { - Text("login.customHTTPHeaders.discard") + Label("login.customHTTPHeaders.discard", systemImage: "minus") } + .foregroundStyle(.red) } } .navigationTitle("login.customHTTPHeaders") @@ -62,3 +66,9 @@ struct CustomHeaderEditView: View { #Preview { CustomHeaderEditView() } + +#Preview { + CustomHeaderEditView() { + + } +} diff --git a/Multiplatform/Account/AccentColorSelectionView.swift b/Multiplatform/Account/TintMenu.swift similarity index 67% rename from Multiplatform/Account/AccentColorSelectionView.swift rename to Multiplatform/Account/TintMenu.swift index 2bbce85e..d7b37481 100644 --- a/Multiplatform/Account/AccentColorSelectionView.swift +++ b/Multiplatform/Account/TintMenu.swift @@ -8,24 +8,25 @@ import SwiftUI import Defaults -struct AccentColorSelectionView: View { +struct TintMenu: View { var body: some View { - List { - Section { - Row(tint: .shelfPlayer) - } + Menu { + Row(tint: .shelfPlayer) + + Divider() ForEach(TintColor.allCases.filter { $0 != .shelfPlayer }, id: \.hashValue) { Row(tint: $0) } + } label: { + Label("account.tint", systemImage: "circle.dashed") } - .navigationTitle("account.tint") } struct Row: View { @Default(.tintColor) private var tintColor - let tint: AccentColorSelectionView.TintColor + let tint: TintMenu.TintColor private var active: Bool { tint == tintColor @@ -35,32 +36,16 @@ struct AccentColorSelectionView: View { Button { Defaults[.tintColor] = tint } label: { - HStack { - Rectangle() - .foregroundStyle(tint.color) - .aspectRatio(1, contentMode: .fit) - .frame(height: 20) - .clipShape(RoundedRectangle(cornerRadius: 10000)) - .padding(.trailing, 5) - - Text(tint.title) - - Spacer() - - if active { - - Label("active", systemImage: "checkmark") - .labelStyle(.iconOnly) - } - } - .contentShape(.hoverMenuInteraction, Rectangle()) + Label(tint.title, systemImage: active ? "checkmark" : "circle.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(tint.color) } .buttonStyle(.plain) } } } -extension AccentColorSelectionView { +extension TintMenu { enum TintColor: CaseIterable, Codable, _DefaultsSerializable { case shelfPlayer @@ -76,7 +61,7 @@ extension AccentColorSelectionView { } } -extension AccentColorSelectionView.TintColor { +extension TintMenu.TintColor { var title: LocalizedStringKey { switch self { case .shelfPlayer: @@ -129,7 +114,7 @@ extension AccentColorSelectionView.TintColor { } #Preview { - NavigationStack { - AccentColorSelectionView() + List { + TintMenu() } } diff --git a/Multiplatform/Localizable.xcstrings b/Multiplatform/Localizable.xcstrings index 230126c6..07176446 100644 --- a/Multiplatform/Localizable.xcstrings +++ b/Multiplatform/Localizable.xcstrings @@ -318,7 +318,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Benachrichtigung-Berechtigung verweigert" + "value" : "Mitteilungen verweigert" } }, "en" : { @@ -334,7 +334,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Erteile die Berechtigung um eine Benachrichtigung bei neuen Episoden zu erhalten" + "value" : "Erteile die Berechtigung um eine Mitteilung bei neuen Episoden zu erhalten" } }, "en" : { @@ -350,7 +350,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Benachrichtigung-Berechtigung erlaubt" + "value" : "Mitteilungen aktiviert" } }, "en" : { @@ -366,7 +366,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Benachrichtigung-Berechtigung erhalten" + "value" : "Sendet Mitteilungen" } }, "en" : { @@ -382,7 +382,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Benachrichtigung-Berechtigung unbekannt" + "value" : "Berechtigung unbekannt" } }, "en" : { @@ -2384,7 +2384,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "ShelfPlayer sendet Push-Benachrichtigungen wenn neue Episoden verfügbar sind" + "value" : "ShelfPlayer sendet Push-Mitteilungen wenn neue Episoden verfügbar sind" } }, "en" : { @@ -2400,7 +2400,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Benachrichtigungen aktivieren" + "value" : "Mitteilungen aktivieren" } }, "en" : { diff --git a/Multiplatform/Utility/Extensions/Defaults+Keys.swift b/Multiplatform/Utility/Extensions/Defaults+Keys.swift index efc29c5c..c61af2f8 100644 --- a/Multiplatform/Utility/Extensions/Defaults+Keys.swift +++ b/Multiplatform/Utility/Extensions/Defaults+Keys.swift @@ -24,7 +24,7 @@ extension Defaults.Keys { static let customSleepTimer = Key("customSleepTimer", default: 0) static let useSerifFont = Key("useSerifFont", default: true) - static let tintColor = Key("tintColor", default: .shelfPlayer) + static let tintColor = Key("tintColor", default: .shelfPlayer) static let lockSeekBar = Key("lockSeekBar", default: false) static let siriOfflineMode = Key("siriOfflineMode", default: false) diff --git a/ShelfPlayer.xcodeproj/project.pbxproj b/ShelfPlayer.xcodeproj/project.pbxproj index e8fcb07a..6ffdd8ef 100644 --- a/ShelfPlayer.xcodeproj/project.pbxproj +++ b/ShelfPlayer.xcodeproj/project.pbxproj @@ -38,6 +38,8 @@ 3A2BC1952AD8121400054727 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3A2BC1942AD8121400054727 /* Settings.bundle */; }; 3A337E3A2B0E13CE009C932A /* EpisodeContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A337E392B0E13CE009C932A /* EpisodeContextMenuModifier.swift */; }; 3A36D78B2ACF2C1D002EEC54 /* AudiobookGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A36D78A2ACF2C1D002EEC54 /* AudiobookGrid.swift */; }; + 3A36ED3B2BE8E9B700AECE9C /* Account+Downloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A36ED3A2BE8E9B700AECE9C /* Account+Downloads.swift */; }; + 3A36ED3D2BE8EC0E00AECE9C /* AccountSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A36ED3C2BE8EC0E00AECE9C /* AccountSheet.swift */; }; 3A38A1582AD4715100D533F3 /* PlayButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A38A1572AD4715100D533F3 /* PlayButton.swift */; }; 3A48D2512AD0661500991139 /* LibrarySelectModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A48D2502AD0661500991139 /* LibrarySelectModifier.swift */; }; 3A48D2542AD074DC00991139 /* PodcastEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A48D2532AD074DC00991139 /* PodcastEntryView.swift */; }; @@ -95,14 +97,13 @@ 3AA19BA22AD6C80000C468EA /* EpisodeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA19BA12AD6C80000C468EA /* EpisodeList.swift */; }; 3AA19BA62AD6E94B00C468EA /* PodcastGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA19BA52AD6E94B00C468EA /* PodcastGrid.swift */; }; 3AA19BA82AD6EE3F00C468EA /* NotableMomentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA19BA72AD6EE3F00C468EA /* NotableMomentsView.swift */; }; - 3AA444CF2BDFED25000C4C53 /* AccentColorSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA444CE2BDFED25000C4C53 /* AccentColorSelectionView.swift */; }; + 3AA444CF2BDFED25000C4C53 /* TintMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA444CE2BDFED25000C4C53 /* TintMenu.swift */; }; 3AA94F8D2BB02DF90012E731 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3A2657EE2BA3402C000C62E3 /* PrivacyInfo.xcprivacy */; }; 3AA9541B2AD290380031C20D /* PodcastList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA9541A2AD290380031C20D /* PodcastList.swift */; }; 3AA9541F2AD2925C0031C20D /* RowTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA9541E2AD2925C0031C20D /* RowTitle.swift */; }; 3AA954252AD29A0A0031C20D /* EpisodeGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA954242AD29A0A0031C20D /* EpisodeGrid.swift */; }; 3AA9542B2AD2A87E0031C20D /* EpisodeFeaturedGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA9542A2AD2A87E0031C20D /* EpisodeFeaturedGrid.swift */; }; 3AA954302AD2AE520031C20D /* EpisodePlayButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA9542F2AD2AE520031C20D /* EpisodePlayButton.swift */; }; - 3AB141DD2ADDC800007F68CB /* AccountSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB141DC2ADDC7FF007F68CB /* AccountSheet.swift */; }; 3AB4529B2ACF4B4B005D5DBA /* SeriesLoadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB4529A2ACF4B4B005D5DBA /* SeriesLoadView.swift */; }; 3AB4529D2ACF4D0D005D5DBA /* SeriesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB4529C2ACF4D0D005D5DBA /* SeriesView.swift */; }; 3AB4CBCE2BB3467000C2F898 /* CustomHeaderEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB4CBCD2BB3467000C2F898 /* CustomHeaderEditView.swift */; }; @@ -216,6 +217,8 @@ 3A2BC1942AD8121400054727 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; 3A337E392B0E13CE009C932A /* EpisodeContextMenuModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodeContextMenuModifier.swift; sourceTree = ""; }; 3A36D78A2ACF2C1D002EEC54 /* AudiobookGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudiobookGrid.swift; sourceTree = ""; }; + 3A36ED3A2BE8E9B700AECE9C /* Account+Downloads.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account+Downloads.swift"; sourceTree = ""; }; + 3A36ED3C2BE8EC0E00AECE9C /* AccountSheet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountSheet.swift; sourceTree = ""; }; 3A38A1572AD4715100D533F3 /* PlayButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayButton.swift; sourceTree = ""; }; 3A48D2502AD0661500991139 /* LibrarySelectModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySelectModifier.swift; sourceTree = ""; }; 3A48D2532AD074DC00991139 /* PodcastEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PodcastEntryView.swift; sourceTree = ""; }; @@ -273,13 +276,12 @@ 3AA19BA52AD6E94B00C468EA /* PodcastGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PodcastGrid.swift; sourceTree = ""; }; 3AA19BA72AD6EE3F00C468EA /* NotableMomentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotableMomentsView.swift; sourceTree = ""; }; 3AA36E502B0A97FD0036038D /* ShelfPlayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShelfPlayer.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 3AA444CE2BDFED25000C4C53 /* AccentColorSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccentColorSelectionView.swift; sourceTree = ""; }; + 3AA444CE2BDFED25000C4C53 /* TintMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TintMenu.swift; sourceTree = ""; }; 3AA9541A2AD290380031C20D /* PodcastList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PodcastList.swift; sourceTree = ""; }; 3AA9541E2AD2925C0031C20D /* RowTitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowTitle.swift; sourceTree = ""; }; 3AA954242AD29A0A0031C20D /* EpisodeGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodeGrid.swift; sourceTree = ""; }; 3AA9542A2AD2A87E0031C20D /* EpisodeFeaturedGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodeFeaturedGrid.swift; sourceTree = ""; }; 3AA9542F2AD2AE520031C20D /* EpisodePlayButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodePlayButton.swift; sourceTree = ""; }; - 3AB141DC2ADDC7FF007F68CB /* AccountSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSheet.swift; sourceTree = ""; }; 3AB4529A2ACF4B4B005D5DBA /* SeriesLoadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeriesLoadView.swift; sourceTree = ""; }; 3AB4529C2ACF4D0D005D5DBA /* SeriesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeriesView.swift; sourceTree = ""; }; 3AB4CBCD2BB3467000C2F898 /* CustomHeaderEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomHeaderEditView.swift; sourceTree = ""; }; @@ -415,11 +417,12 @@ 3A0AE6B62AB624CC00ACCD68 /* Account */ = { isa = PBXGroup; children = ( + 3AA444CE2BDFED25000C4C53 /* TintMenu.swift */, + 3A36ED3C2BE8EC0E00AECE9C /* AccountSheet.swift */, 3A0AE6B92AB624E700ACCD68 /* LoginView.swift */, - 3AB141DC2ADDC7FF007F68CB /* AccountSheet.swift */, + 3A36ED3A2BE8E9B700AECE9C /* Account+Downloads.swift */, 3ACD19B82ACB5B32007D01FE /* SessionsImportView.swift */, 3AB4CBCD2BB3467000C2F898 /* CustomHeaderEditView.swift */, - 3AA444CE2BDFED25000C4C53 /* AccentColorSelectionView.swift */, ); path = Account; sourceTree = ""; @@ -658,11 +661,11 @@ 3A9874212B5995CE00B2704F /* Collections */ = { isa = PBXGroup; children = ( - 3AA0D9C82BD6BF2100498546 /* Series */, + 3AA0D9CC2BD6BF4900498546 /* Audiobook */, 3AA0D9C92BD6BF2B00498546 /* Author */, + 3AA0D9C82BD6BF2100498546 /* Series */, 3AA0D9CB2BD6BF3E00498546 /* Episode */, 3AA0D9CA2BD6BF3500498546 /* Podcast */, - 3AA0D9CC2BD6BF4900498546 /* Audiobook */, ); path = Collections; sourceTree = ""; @@ -1098,6 +1101,7 @@ 3ABCC7E22AD4659F004AEADF /* Description.swift in Sources */, 3AF240F12ADA99940065C0A4 /* AuthorGrid.swift in Sources */, 3AB8BBBF2BD7B76B0012023A /* PodcastLatestView.swift in Sources */, + 3A36ED3D2BE8EC0E00AECE9C /* AccountSheet.swift in Sources */, 3A8810FB2AD2CEE7009696D1 /* PodcastView+Toolbar.swift in Sources */, 3A48D2542AD074DC00991139 /* PodcastEntryView.swift in Sources */, 3A0AE6A62AB623F400ACCD68 /* ContentView.swift in Sources */, @@ -1106,7 +1110,6 @@ 3AFA7F9D2ACD440A00B7FF4B /* Triangle.swift in Sources */, 3AA9541B2AD290380031C20D /* PodcastList.swift in Sources */, 3AA0B08B2AD32D4E0009FBB4 /* EpisodeView+Header.swift in Sources */, - 3AB141DD2ADDC800007F68CB /* AccountSheet.swift in Sources */, 3AB8BBB72BD7B5EF0012023A /* AudiobookListenNowView.swift in Sources */, 3A5A4DAB2B5EF6A400E72E8B /* PlayMediaHandler.swift in Sources */, 3ABCC7E02AD4647D004AEADF /* EpisodeView+Toolbar.swift in Sources */, @@ -1119,7 +1122,7 @@ 3A1507992AD4494300D7D6CF /* UINavigationController+Gesture.swift in Sources */, 3ACFD7272BD14B2500C7998B /* SelectLibraryTip.swift in Sources */, 3A0D2D0F2BE6C3860001EC15 /* ButtonHoverEffectModifier.swift in Sources */, - 3AA444CF2BDFED25000C4C53 /* AccentColorSelectionView.swift in Sources */, + 3AA444CF2BDFED25000C4C53 /* TintMenu.swift in Sources */, 3A0AE6A42AB623F400ACCD68 /* MultiplatformApp.swift in Sources */, 3A77B7092ACDF8F200F22C7F /* FullscreenBackButton.swift in Sources */, 3A36D78B2ACF2C1D002EEC54 /* AudiobookGrid.swift in Sources */, @@ -1128,6 +1131,7 @@ 3ACFD7232BD14AC500C7998B /* ViewBookmarkTip.swift in Sources */, 3A17E6912AD5F21B00E59464 /* Slider.swift in Sources */, 3A5159562AD0374600CD5680 /* AudiobookSortFilter.swift in Sources */, + 3A36ED3B2BE8E9B700AECE9C /* Account+Downloads.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };