From 29c3382ee22b0b707d66058bf17570541d76b04e Mon Sep 17 00:00:00 2001 From: Brian Douglas Moakley Date: Fri, 29 Oct 2021 15:49:43 -0400 Subject: [PATCH 01/69] Changes UI to reflect light mode changes --- Emitron/Emitron.xcodeproj/project.pbxproj | 4 ++ .../xcshareddata/swiftpm/Package.resolved | 6 +-- Emitron/Emitron/UI/App Root/TabNavView.swift | 2 - .../Filtering/AppliedFilterTagButton.swift | 4 -- .../Emitron/UI/NavigationConfigurator.swift | 43 +++++++++++++++++++ Emitron/Emitron/UI/Shared/Tags/TagView.swift | 4 -- 6 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 Emitron/Emitron/UI/NavigationConfigurator.swift diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index 582dac96..f3abc404 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -332,6 +332,7 @@ B6FC15D722CB817C0078CEDB /* BookmarksService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FC15D622CB817C0078CEDB /* BookmarksService.swift */; }; D31CEF2C26E2C421008083D0 /* FilterGroupTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D31CEF2B26E2C421008083D0 /* FilterGroupTest.swift */; }; D3D9C5C72645DA9A002FD479 /* EmitronApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D9C5C62645DA9A002FD479 /* EmitronApp.swift */; }; + D3DE936027234CDF0071CB8D /* NavigationConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3DE935F27234CDF0071CB8D /* NavigationConfigurator.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -681,6 +682,7 @@ B6FC15D622CB817C0078CEDB /* BookmarksService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksService.swift; sourceTree = ""; }; D31CEF2B26E2C421008083D0 /* FilterGroupTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterGroupTest.swift; sourceTree = ""; }; D3D9C5C62645DA9A002FD479 /* EmitronApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmitronApp.swift; sourceTree = ""; }; + D3DE935F27234CDF0071CB8D /* NavigationConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationConfigurator.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1520,6 +1522,7 @@ children = ( 22C3F154242795A1002812CB /* PortraitHostingController.swift */, B6D7DC3022C79743006DD325 /* SceneDelegate.swift */, + D3DE935F27234CDF0071CB8D /* NavigationConfigurator.swift */, B62B9A7C22DF764900122CE8 /* App Root */, B6FC15AA22CB52430078CEDB /* Downloads */, 2213193D23E4E83300F15816 /* Empty States */, @@ -2049,6 +2052,7 @@ 223D77D523B1AA02005BE95D /* ContentSummaryState.swift in Sources */, 22BFE76823DAF5B000495BA9 /* PersistenceStore+Synchronisation.swift in Sources */, 22C4EADD23DB8B33001A3FDA /* WatchStatsRequest.swift in Sources */, + D3DE936027234CDF0071CB8D /* NavigationConfigurator.swift in Sources */, B6FC15C522CB55D30078CEDB /* JSONAPIError.swift in Sources */, 22BFE75823D9910600495BA9 /* MessageBus.swift in Sources */, 22B8266423AF5CF900D4BA23 /* BookmarkAdapter.swift in Sources */, diff --git a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index fd6e2005..0715a05c 100644 --- a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,9 +41,9 @@ "package": "Kingfisher", "repositoryURL": "https://github.com/onevcat/Kingfisher", "state": { - "branch": null, - "revision": "1a0c2df04b31ed7aa318354f3583faea24f006fc", - "version": "5.15.8" + "branch": "master", + "revision": "131c2c2737602dd8227fdf7ef68252662dc2752f", + "version": null } }, { diff --git a/Emitron/Emitron/UI/App Root/TabNavView.swift b/Emitron/Emitron/UI/App Root/TabNavView.swift index d8854acd..ad0120c8 100644 --- a/Emitron/Emitron/UI/App Root/TabNavView.swift +++ b/Emitron/Emitron/UI/App Root/TabNavView.swift @@ -54,8 +54,6 @@ struct TabNavView< if #available(iOS 15.0, *) { let barAppearance = UIBarAppearance() barAppearance.configureWithOpaqueBackground() - UINavigationBar.appearance().scrollEdgeAppearance = .init(barAppearance: barAppearance) - UITabBar.appearance().scrollEdgeAppearance = .init(barAppearance: barAppearance) } } diff --git a/Emitron/Emitron/UI/Library/Filtering/AppliedFilterTagButton.swift b/Emitron/Emitron/UI/Library/Filtering/AppliedFilterTagButton.swift index 7aa754da..a4513681 100644 --- a/Emitron/Emitron/UI/Library/Filtering/AppliedFilterTagButton.swift +++ b/Emitron/Emitron/UI/Library/Filtering/AppliedFilterTagButton.swift @@ -101,10 +101,6 @@ struct AppliedFilterTagButton: View { .background( RoundedRectangle(cornerRadius: Layout.cornerRadius) .fill(type.backgroundColor) - .overlay( - RoundedRectangle(cornerRadius: Layout.cornerRadius) - .stroke(type.borderColor, lineWidth: 2) - ) ) } .padding(1) diff --git a/Emitron/Emitron/UI/NavigationConfigurator.swift b/Emitron/Emitron/UI/NavigationConfigurator.swift new file mode 100644 index 00000000..a179e0f5 --- /dev/null +++ b/Emitron/Emitron/UI/NavigationConfigurator.swift @@ -0,0 +1,43 @@ +// Copyright (c) 2021 Razeware LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, +// distribute, sublicense, create a derivative work, and/or sell copies of the +// Software in any work that is designed, intended, or marketed for pedagogical or +// instructional purposes related to programming, coding, application development, +// or information technology. Permission for such use, copying, modification, +// merger, publication, distribution, sublicensing, creation of derivative works, +// or sale is expressly withheld. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import SwiftUI + +struct NavigationConfigurator: UIViewControllerRepresentable { + var configure: (UINavigationController) -> Void = { _ in } + + func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIViewController { + UIViewController() + } + func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext) { + if let navConfigurator = uiViewController.navigationController { + self.configure(navConfigurator) + } + } +} diff --git a/Emitron/Emitron/UI/Shared/Tags/TagView.swift b/Emitron/Emitron/UI/Shared/Tags/TagView.swift index 0c134652..7dcd8b46 100644 --- a/Emitron/Emitron/UI/Shared/Tags/TagView.swift +++ b/Emitron/Emitron/UI/Shared/Tags/TagView.swift @@ -66,10 +66,6 @@ struct TagView: View { .padding([.vertical], 5) .padding([.horizontal], 7) .background(backgroundColor) - .overlay( - RoundedRectangle(cornerRadius: 6) - .stroke(borderColor, lineWidth: 4) - ) .cornerRadius(6) // This is a bit hacky. .onPreferenceChange(SizeKey.self) { size in height = size?.height From 101031aca3c3ac89d0e2ced8f77c4b08fe236457 Mon Sep 17 00:00:00 2001 From: jason goodney Date: Sun, 9 Jan 2022 11:42:48 -0500 Subject: [PATCH 02/69] Adds an action sheet to confirm a request to sign out. --- Emitron/Emitron/UI/Settings/SettingsView.swift | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index deca1af5..0427c9b9 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -39,6 +39,7 @@ struct SettingsView: View { @EnvironmentObject var tabViewModel: TabViewModel @ObservedObject private var settingsManager: SettingsManager @State private var licensesPresented = false + @State private var showLogoutConfirmation = false init(settingsManager: SettingsManager) { self.settingsManager = settingsManager @@ -84,10 +85,20 @@ struct SettingsView: View { } MainButtonView(title: "Sign Out", type: .destructive(withArrow: true)) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - sessionController.logout() - tabViewModel.selectedTab = .library + showLogoutConfirmation = true } } + .actionSheet(isPresented: $showLogoutConfirmation) { + ActionSheet(title: Text("Are you sure you want to sign out?"), + buttons: [ + .cancel(), + .destructive(Text("Sign Out")) { + sessionController.logout() + tabViewModel.selectedTab = .library + } + ] + ) + } } .padding([.bottom, .horizontal], 18) } From 972f72ce3dbad17b311d5794779ca950d8ec2b87 Mon Sep 17 00:00:00 2001 From: jason goodney Date: Fri, 14 Jan 2022 11:45:25 -0500 Subject: [PATCH 03/69] Moves Bookmarks to the middle and Completed to the trailing position --- Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift | 2 +- Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift index eafb30b4..0bff0594 100644 --- a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift +++ b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift @@ -30,8 +30,8 @@ import SwiftUI enum MyTutorialsState: String { case inProgress - case completed case bookmarked + case completed var displayString: String { switch self { diff --git a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift index fdc530f2..2bec83fd 100644 --- a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift +++ b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift @@ -41,9 +41,9 @@ struct ToggleControlView: View { .frame(height: 2) HStack { - toggleButton(for: .inProgress) - toggleButton(for: .completed) - toggleButton(for: .bookmarked) + ForEach(MyTutorialsState.allCases, id: \.self) { state in + toggleButton(for: state) + } } } } From 953041258711f9eaa74f4df3db01971b5fb46502 Mon Sep 17 00:00:00 2001 From: jason goodney Date: Sat, 15 Jan 2022 20:24:36 -0500 Subject: [PATCH 04/69] Adds a row to link to the users account --- Emitron/Emitron/UI/Settings/SettingsView.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index deca1af5..1b51a6ae 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -46,6 +46,11 @@ struct SettingsView: View { var body: some View { VStack { + Link(destination: URL(string: "https://accounts.raywenderlich.com")!) { + SettingsDisclosureRow(title: "My Account", value: "") + } + .padding(.horizontal, 20) + SettingsList( settingsManager: _settingsManager, canDownload: sessionController.user?.canDownload ?? false From afd273c56f545ddcc0150052b5f539082276723c Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 18 Jan 2022 12:39:07 -0500 Subject: [PATCH 05/69] Newline + indent --- Emitron/Emitron/UI/Settings/SettingsView.swift | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 0427c9b9..ed20162f 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -89,14 +89,15 @@ struct SettingsView: View { } } .actionSheet(isPresented: $showLogoutConfirmation) { - ActionSheet(title: Text("Are you sure you want to sign out?"), - buttons: [ - .cancel(), - .destructive(Text("Sign Out")) { - sessionController.logout() - tabViewModel.selectedTab = .library - } - ] + ActionSheet( + title: Text("Are you sure you want to sign out?"), + buttons: [ + .cancel(), + .destructive(Text("Sign Out")) { + sessionController.logout() + tabViewModel.selectedTab = .library + } + ] ) } } From db8259751c067b1a3377b0fdf11afa77de9e9a59 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 18 Jan 2022 12:40:30 -0500 Subject: [PATCH 06/69] Rename variable to present-tense `showingSignOutConfirmation` --- Emitron/Emitron/UI/Settings/SettingsView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index ed20162f..09a189c5 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -39,7 +39,7 @@ struct SettingsView: View { @EnvironmentObject var tabViewModel: TabViewModel @ObservedObject private var settingsManager: SettingsManager @State private var licensesPresented = false - @State private var showLogoutConfirmation = false + @State private var showingSignOutConfirmation = false init(settingsManager: SettingsManager) { self.settingsManager = settingsManager @@ -85,10 +85,10 @@ struct SettingsView: View { } MainButtonView(title: "Sign Out", type: .destructive(withArrow: true)) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - showLogoutConfirmation = true + showingSignOutConfirmation = true } } - .actionSheet(isPresented: $showLogoutConfirmation) { + .actionSheet(isPresented: $showingSignOutConfirmation) { ActionSheet( title: Text("Are you sure you want to sign out?"), buttons: [ From 2c9e14850daf209fe6ac36a9a050bcce62d5d2c2 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 18 Jan 2022 12:41:43 -0500 Subject: [PATCH 07/69] Make environment objects private --- Emitron/Emitron/UI/Settings/SettingsView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 09a189c5..8b70976c 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -35,8 +35,8 @@ enum SettingsLayout { } struct SettingsView: View { - @EnvironmentObject var sessionController: SessionController - @EnvironmentObject var tabViewModel: TabViewModel + @EnvironmentObject private var sessionController: SessionController + @EnvironmentObject private var tabViewModel: TabViewModel @ObservedObject private var settingsManager: SettingsManager @State private var licensesPresented = false @State private var showingSignOutConfirmation = false From 5164873e4e8cdaa33e2a647243f8c730953fb28a Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 18 Jan 2022 12:43:59 -0500 Subject: [PATCH 08/69] Delay the sign-out instead of the dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I don't know why the delay is there! Let's ask Sam sometime. 😺 --- Emitron/Emitron/UI/Settings/SettingsView.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 8b70976c..6d1411cc 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -84,9 +84,7 @@ struct SettingsView: View { .foregroundColor(.contentText) } MainButtonView(title: "Sign Out", type: .destructive(withArrow: true)) { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - showingSignOutConfirmation = true - } + showingSignOutConfirmation = true } .actionSheet(isPresented: $showingSignOutConfirmation) { ActionSheet( @@ -94,8 +92,10 @@ struct SettingsView: View { buttons: [ .cancel(), .destructive(Text("Sign Out")) { - sessionController.logout() - tabViewModel.selectedTab = .library + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + sessionController.logout() + tabViewModel.selectedTab = .library + } } ] ) From 41fa6d24339c8396b9f08cdf47af32443836f429 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 18 Jan 2022 12:47:59 -0500 Subject: [PATCH 09/69] Use implicit typing for initializers --- Emitron/Emitron/UI/Settings/SettingsView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 6d1411cc..292c8a6f 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -87,11 +87,11 @@ struct SettingsView: View { showingSignOutConfirmation = true } .actionSheet(isPresented: $showingSignOutConfirmation) { - ActionSheet( - title: Text("Are you sure you want to sign out?"), + .init( + title: .init("Are you sure you want to sign out?"), buttons: [ .cancel(), - .destructive(Text("Sign Out")) { + .destructive(.init("Sign Out")) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { sessionController.logout() tabViewModel.selectedTab = .library From ced2e67002178aa8b2b4351e3c8ee4d476a9445f Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 18 Jan 2022 12:49:02 -0500 Subject: [PATCH 10/69] Rearrange buttons in code to reflect onscreen representations --- Emitron/Emitron/UI/Settings/SettingsView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 292c8a6f..ed64f295 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -90,13 +90,13 @@ struct SettingsView: View { .init( title: .init("Are you sure you want to sign out?"), buttons: [ - .cancel(), .destructive(.init("Sign Out")) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { sessionController.logout() tabViewModel.selectedTab = .library } - } + }, + .cancel() ] ) } From a9c14ac009787d1ec86e60f1a6f4c038c379b17f Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 18 Jan 2022 12:53:28 -0500 Subject: [PATCH 11/69] Extract constants --- .../Emitron/UI/Settings/SettingsView.swift | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index ed64f295..a89f83b8 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -87,15 +87,19 @@ struct SettingsView: View { showingSignOutConfirmation = true } .actionSheet(isPresented: $showingSignOutConfirmation) { - .init( - title: .init("Are you sure you want to sign out?"), + let dialogTitle = "Are you sure you want to sign out?" + let buttonTitle = "Sign Out" + let action = { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + sessionController.logout() + tabViewModel.selectedTab = .library + } + } + + return .init( + title: .init(dialogTitle), buttons: [ - .destructive(.init("Sign Out")) { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - sessionController.logout() - tabViewModel.selectedTab = .library - } - }, + .destructive(.init(buttonTitle), action: action), .cancel() ] ) From f7f460934bd247a8b0a23ebed514a3c7fa100d06 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 18 Jan 2022 12:55:37 -0500 Subject: [PATCH 12/69] Add View.modifier overload --- Emitron/Emitron/Extensions/View+Extensions.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Emitron/Emitron/Extensions/View+Extensions.swift b/Emitron/Emitron/Extensions/View+Extensions.swift index 3eb209ec..516d5bab 100644 --- a/Emitron/Emitron/Extensions/View+Extensions.swift +++ b/Emitron/Emitron/Extensions/View+Extensions.swift @@ -44,4 +44,18 @@ extension View { self } } + + /// Modify a view with a `ViewBuilder` closure. + /// + /// This represents a streamlining of the + /// [`modifier`](https://developer.apple.com/documentation/swiftui/view/modifier(_:)) + /// \+ [`ViewModifier`](https://developer.apple.com/documentation/swiftui/viewmodifier) + /// pattern. + /// - Note: Useful only when you don't need to reuse the closure. + /// If you do, turn the closure into an extension! ♻️ + func modifier( + @ViewBuilder body: (_ content: Self) -> ModifiedContent + ) -> ModifiedContent { + body(self) + } } From b5040f76ccd4d8a57695ca8164e3d9201e55f884 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 18 Jan 2022 12:57:31 -0500 Subject: [PATCH 13/69] Incorporate `modifier` --- Emitron/Emitron/UI/Settings/SettingsView.swift | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index a89f83b8..96e532cf 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -86,7 +86,7 @@ struct SettingsView: View { MainButtonView(title: "Sign Out", type: .destructive(withArrow: true)) { showingSignOutConfirmation = true } - .actionSheet(isPresented: $showingSignOutConfirmation) { + .modifier { let dialogTitle = "Are you sure you want to sign out?" let buttonTitle = "Sign Out" let action = { @@ -96,13 +96,15 @@ struct SettingsView: View { } } - return .init( - title: .init(dialogTitle), - buttons: [ - .destructive(.init(buttonTitle), action: action), - .cancel() - ] - ) + $0.actionSheet(isPresented: $showingSignOutConfirmation) { + .init( + title: .init(dialogTitle), + buttons: [ + .destructive(.init(buttonTitle), action: action), + .cancel() + ] + ) + } } } .padding([.bottom, .horizontal], 18) From 1b487464ff5a528c6763e1564d36c8c3bbda55fc Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 18 Jan 2022 13:00:04 -0500 Subject: [PATCH 14/69] Add iOS 15 alternative Action sheets are deprecated. --- .../Emitron/UI/Settings/SettingsView.swift | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 96e532cf..d69f311f 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -96,14 +96,24 @@ struct SettingsView: View { } } - $0.actionSheet(isPresented: $showingSignOutConfirmation) { - .init( - title: .init(dialogTitle), - buttons: [ - .destructive(.init(buttonTitle), action: action), - .cancel() - ] - ) + if #available(iOS 15, *) { + $0.confirmationDialog( + dialogTitle, + isPresented: $showingSignOutConfirmation, + titleVisibility: .visible + ) { + Button(buttonTitle, role: .destructive, action: action) + } + } else { + $0.actionSheet(isPresented: $showingSignOutConfirmation) { + .init( + title: .init(dialogTitle), + buttons: [ + .destructive(.init(buttonTitle), action: action), + .cancel() + ] + ) + } } } } From 1ce3a698236ccb66c6660df8af0d7ca3baf90101 Mon Sep 17 00:00:00 2001 From: jason goodney Date: Tue, 18 Jan 2022 22:08:03 -0500 Subject: [PATCH 15/69] Removing message posts on bookmark status change --- Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift index 81442352..fa7bf645 100644 --- a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift @@ -177,7 +177,6 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable if bookmarked { do { try syncAction.deleteBookmark(for: contentId) - messageBus.post(message: Message(level: .success, message: .bookmarkDeleted)) } catch { messageBus.post(message: Message(level: .error, message: .bookmarkDeletedError)) Failure @@ -187,7 +186,6 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable } else { do { try syncAction.createBookmark(for: contentId) - messageBus.post(message: Message(level: .success, message: .bookmarkCreated)) } catch { messageBus.post(message: Message(level: .error, message: .bookmarkCreatedError)) Failure From 74b9d34adca65e98baaa5db2d29964af5406b0ab Mon Sep 17 00:00:00 2001 From: jason goodney Date: Tue, 18 Jan 2022 22:28:39 -0500 Subject: [PATCH 16/69] Adds haptic feedback indicating bookmark was successfully created. --- Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift index fa7bf645..b75aee20 100644 --- a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift @@ -186,6 +186,8 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable } else { do { try syncAction.createBookmark(for: contentId) + let generator = UINotificationFeedbackGenerator() + generator.notificationOccurred(.success) } catch { messageBus.post(message: Message(level: .error, message: .bookmarkCreatedError)) Failure From 816973bbe465ed5f3946b4fa8b7350bb378858d0 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 19 Jan 2022 08:11:25 +0300 Subject: [PATCH 17/69] Change "Downloads (Wifi Only)" text in Settings to Allow Downloads Over Cellular --- Emitron/Emitron/Constants.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emitron/Emitron/Constants.swift b/Emitron/Emitron/Constants.swift index 0547fe4d..053d55a6 100644 --- a/Emitron/Emitron/Constants.swift +++ b/Emitron/Emitron/Constants.swift @@ -101,7 +101,7 @@ extension String { // MARK: Settings screens static let settingsPlaybackSpeedLabel = "Video Playback Speed" - static let settingsWifiOnlyDownloadsLabel = "Downloads (WiFi only)" + static let settingsWifiOnlyDownloadsLabel = "Allow Downloads Over Cellular" static let settingsDownloadQualityLabel = "Downloads Quality" static let settingsClosedCaptionOnLabel = "Subtitles" } From ae52ccd3c901dc1f5d90e005fb4c5981106f3509 Mon Sep 17 00:00:00 2001 From: jason goodney Date: Sun, 23 Jan 2022 11:18:11 -0500 Subject: [PATCH 18/69] Adds description property --- Emitron/Emitron/Displayable/ContentDisplayable.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Emitron/Emitron/Displayable/ContentDisplayable.swift b/Emitron/Emitron/Displayable/ContentDisplayable.swift index 039ebf48..57444109 100644 --- a/Emitron/Emitron/Displayable/ContentDisplayable.swift +++ b/Emitron/Emitron/Displayable/ContentDisplayable.swift @@ -146,6 +146,7 @@ extension ContentListDisplayable { protocol ChildContentListDisplayable: Ownable { var id: Int { get } var name: String { get } + var descriptionPlainText: String { get } var ordinal: Int? { get } var duration: Int { get } var groupId: Int? { get } From 59934433ff654d457ebe15c4b30befa5f716fff8 Mon Sep 17 00:00:00 2001 From: jason goodney Date: Sun, 23 Jan 2022 11:29:46 -0500 Subject: [PATCH 19/69] Adds the episodes description to view and a toggle for the descriptions line limit. --- .../Content Detail/TextListItemView.swift | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift index 636c943e..905e75d6 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift @@ -40,6 +40,7 @@ struct TextListItemView: View { @EnvironmentObject var settingsManager: SettingsManager @EnvironmentObject var messageBus: MessageBus @State private var deletionConfirmation: DownloadDeletionConfirmation? + @State private var descriptionHasLineLimit = true @ObservedObject var dynamicContentViewModel: DynamicContentViewModel var content: ChildContentListDisplayable @@ -59,13 +60,35 @@ struct TextListItemView: View { VStack(spacing: 15) { HStack { VStack(alignment: .leading, spacing: 5) { - Text(content.name) - .font(.uiTitle5) - .kerning(-0.5) - .lineSpacing(3) - .foregroundColor(.titleText) - .fixedSize(horizontal: false, vertical: true) + HStack { + Text(content.name) + .font(.uiTitle5) + .kerning(-0.5) + .lineSpacing(3) + .foregroundColor(.titleText) + .fixedSize(horizontal: false, vertical: true) + + Spacer() + + Image(systemName: descriptionHasLineLimit ? "chevron.up" : "chevron.down") + .foregroundColor(.contentText) + .font(.uiLabelBold) + .onTapGesture { + withAnimation { + descriptionHasLineLimit.toggle() + } + } + } + Text(content.descriptionPlainText) + .multilineTextAlignment(.leading) + .font(.uiCaption) + .fixedSize(horizontal: false, vertical: true) + .lineLimit(descriptionHasLineLimit ? 1 : nil) + .lineSpacing(3) + .foregroundColor(.contentText) + .padding(.bottom, 5) + Text(content.duration.minuteSecondTimeFromSeconds) .font(.uiFootnote) .foregroundColor(.contentText) From 335d7070102ac99270afc5b3452aa6f67623114d Mon Sep 17 00:00:00 2001 From: Brian Douglas Moakley Date: Mon, 24 Jan 2022 08:52:02 -0500 Subject: [PATCH 20/69] Adds back scrollbar appearance --- .../xcshareddata/swiftpm/Package.resolved | 14 ++++---------- Emitron/Emitron/UI/App Root/TabNavView.swift | 2 ++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cb19c86c..03e0923e 100644 --- a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/groue/GRDB.swift.git", "state": { "branch": null, - "revision": "32b2923e890df320906e64cbd0faca22a8bfda14", - "version": "5.12.0" + "revision": "dfca044433050bb3e297761bf827e01ef376f5d9", + "version": "5.18.0" } }, { @@ -32,15 +32,9 @@ "package": "Kingfisher", "repositoryURL": "https://github.com/onevcat/Kingfisher", "state": { -<<<<<<< HEAD - "branch": "master", - "revision": "131c2c2737602dd8227fdf7ef68252662dc2752f", - "version": null -======= "branch": null, - "revision": "318e319998bf555c3e914d5c3adb6da05af86a32", - "version": "7.1.1" ->>>>>>> dc6a7db3c605753e4cd73bc76ae44be1626ce20f + "revision": "0c02c46cfdc0656ce74fd0963a75e5000a0b7f23", + "version": "7.1.2" } }, { diff --git a/Emitron/Emitron/UI/App Root/TabNavView.swift b/Emitron/Emitron/UI/App Root/TabNavView.swift index ad0120c8..488034f7 100644 --- a/Emitron/Emitron/UI/App Root/TabNavView.swift +++ b/Emitron/Emitron/UI/App Root/TabNavView.swift @@ -54,6 +54,8 @@ struct TabNavView< if #available(iOS 15.0, *) { let barAppearance = UIBarAppearance() barAppearance.configureWithOpaqueBackground() + UITabBar.appearance().scrollEdgeAppearance = .init(barAppearance: barAppearance) + UINavigationBar.appearance().scrollEdgeAppearance = .init(barAppearance: barAppearance) } } From 8ff87f45bfba062b217f482a6f9451fb400f12b7 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 26 Jan 2022 10:24:18 +0300 Subject: [PATCH 21/69] Update keychain-swift to clear warning "Conversion to Swift 5 is available" caused by swift-tools-version --- Emitron/Emitron.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index 9c7e58ee..7991839f 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -2679,7 +2679,7 @@ repositoryURL = "https://github.com/evgenyneu/keychain-swift"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 19.0.0; + minimumVersion = 20.0.0; }; }; 22C0512323A4C99E004D1223 /* XCRemoteSwiftPackageReference "Kingfisher" */ = { diff --git a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index ecb7f48f..7e272ea7 100644 --- a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/evgenyneu/keychain-swift", "state": { "branch": null, - "revision": "96fb84f45a96630e7583903bd7e08cf095c7a7ef", - "version": "19.0.0" + "revision": "d108a1fa6189e661f91560548ef48651ed8d93b9", + "version": "20.0.0" } }, { From 9282fbfd818263a95c2db2a1cd81c95d399f1fd5 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 27 Jan 2022 12:34:35 +0300 Subject: [PATCH 22/69] increased Test Timer --- .../Persistence/PersistenceStore+DownloadsTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift b/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift index a8aa2bfa..4fb7d24f 100644 --- a/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift +++ b/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift @@ -113,7 +113,7 @@ class PersistenceStore_DownloadsTest: XCTestCase { collectionExpectation.fulfill() } - wait(for: [collectionExpectation], timeout: 10) + wait(for: [collectionExpectation], timeout: 15) } func testTransitionEpisodeToDownloadedUpdatesCollection() throws { From 0bcafa3ac2fa1cd7c84451a046312de32536c8cf Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 02:34:53 -0500 Subject: [PATCH 23/69] Remove NavigationConfigurator It was not being used in this branch. --- Emitron/Emitron.xcodeproj/project.pbxproj | 4 -- .../Emitron/UI/NavigationConfigurator.swift | 43 ------------------- 2 files changed, 47 deletions(-) delete mode 100644 Emitron/Emitron/UI/NavigationConfigurator.swift diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index 03a55d66..9c7e58ee 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -332,7 +332,6 @@ B6FC15D722CB817C0078CEDB /* BookmarksService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FC15D622CB817C0078CEDB /* BookmarksService.swift */; }; D31CEF2C26E2C421008083D0 /* FilterGroupTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D31CEF2B26E2C421008083D0 /* FilterGroupTest.swift */; }; D3D9C5C72645DA9A002FD479 /* EmitronApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D9C5C62645DA9A002FD479 /* EmitronApp.swift */; }; - D3DE936027234CDF0071CB8D /* NavigationConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3DE935F27234CDF0071CB8D /* NavigationConfigurator.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -682,7 +681,6 @@ B6FC15D622CB817C0078CEDB /* BookmarksService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksService.swift; sourceTree = ""; }; D31CEF2B26E2C421008083D0 /* FilterGroupTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterGroupTest.swift; sourceTree = ""; }; D3D9C5C62645DA9A002FD479 /* EmitronApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmitronApp.swift; sourceTree = ""; }; - D3DE935F27234CDF0071CB8D /* NavigationConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationConfigurator.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1522,7 +1520,6 @@ children = ( 22C3F154242795A1002812CB /* PortraitHostingController.swift */, B6D7DC3022C79743006DD325 /* SceneDelegate.swift */, - D3DE935F27234CDF0071CB8D /* NavigationConfigurator.swift */, B62B9A7C22DF764900122CE8 /* App Root */, B6FC15AA22CB52430078CEDB /* Downloads */, 2213193D23E4E83300F15816 /* Empty States */, @@ -2052,7 +2049,6 @@ 223D77D523B1AA02005BE95D /* ContentSummaryState.swift in Sources */, 22BFE76823DAF5B000495BA9 /* PersistenceStore+Synchronisation.swift in Sources */, 22C4EADD23DB8B33001A3FDA /* WatchStatsRequest.swift in Sources */, - D3DE936027234CDF0071CB8D /* NavigationConfigurator.swift in Sources */, B6FC15C522CB55D30078CEDB /* JSONAPIError.swift in Sources */, 22BFE75823D9910600495BA9 /* MessageBus.swift in Sources */, 22B8266423AF5CF900D4BA23 /* BookmarkAdapter.swift in Sources */, diff --git a/Emitron/Emitron/UI/NavigationConfigurator.swift b/Emitron/Emitron/UI/NavigationConfigurator.swift deleted file mode 100644 index a179e0f5..00000000 --- a/Emitron/Emitron/UI/NavigationConfigurator.swift +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2021 Razeware LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, -// distribute, sublicense, create a derivative work, and/or sell copies of the -// Software in any work that is designed, intended, or marketed for pedagogical or -// instructional purposes related to programming, coding, application development, -// or information technology. Permission for such use, copying, modification, -// merger, publication, distribution, sublicensing, creation of derivative works, -// or sale is expressly withheld. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -import Foundation -import SwiftUI - -struct NavigationConfigurator: UIViewControllerRepresentable { - var configure: (UINavigationController) -> Void = { _ in } - - func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIViewController { - UIViewController() - } - func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext) { - if let navConfigurator = uiViewController.navigationController { - self.configure(navConfigurator) - } - } -} From ac455b72ab9e2ba37b9531e3bc32cf4217442deb Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 03:46:53 -0500 Subject: [PATCH 24/69] Mirror new case order in switch statements --- .../UI/My Tutorials/MyTutorialsView.swift | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift index 0bff0594..958874d5 100644 --- a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift +++ b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift @@ -37,10 +37,10 @@ enum MyTutorialsState: String { switch self { case .inProgress: return "In Progress" - case .completed: - return "Completed" case .bookmarked: return "Bookmarks" + case .completed: + return "Completed" } } } @@ -113,16 +113,16 @@ private extension MyTutorialsView { contentRepository: inProgressRepository, contentScreen: .inProgress ) - case .completed: - makeContentListView( - contentRepository: completedRepository, - contentScreen: .completed - ) case .bookmarked: makeContentListView( contentRepository: bookmarkRepository, contentScreen: .bookmarked ) + case .completed: + makeContentListView( + contentRepository: completedRepository, + contentScreen: .completed + ) } } @@ -143,16 +143,16 @@ private extension MyTutorialsView { inProgressRepository.reload() reloadProgression = false } - case .completed: - if reloadCompleted { - completedRepository.reload() - reloadCompleted = false - } case .bookmarked: if reloadBookmarks { bookmarkRepository.reload() reloadBookmarks = false } + case .completed: + if reloadCompleted { + completedRepository.reload() + reloadCompleted = false + } } }) .padding(.top, .sidePadding) From f759460acc37d43a4dc6aa1efb01194c3475e7a1 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 03:56:47 -0500 Subject: [PATCH 25/69] Match new case order in ToggleControlView previews --- Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift index 2bec83fd..d54e4a6c 100644 --- a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift +++ b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift @@ -75,9 +75,9 @@ struct ToggleControlView: View { struct ToggleControlView_Previews: PreviewProvider { static var previews: some View { VStack(spacing: 40) { - ToggleControlView(toggleState: .inProgress) - ToggleControlView(toggleState: .completed) - ToggleControlView(toggleState: .bookmarked) + ForEach(MyTutorialsState.allCases, id: \.self) { + ToggleControlView(toggleState: $0) + } } .padding([.vertical], 40) .padding([.horizontal], 10) From 4ffd776ae9eccb1ffe3091ca7bc8327456c6cb13 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 03:57:13 -0500 Subject: [PATCH 26/69] Use `toggleButton` method directly --- Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift index d54e4a6c..34bee3bd 100644 --- a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift +++ b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift @@ -41,9 +41,7 @@ struct ToggleControlView: View { .frame(height: 2) HStack { - ForEach(MyTutorialsState.allCases, id: \.self) { state in - toggleButton(for: state) - } + ForEach(MyTutorialsState.allCases, id: \.self, content: toggleButton) } } } From 37c6805b0364fedb75acc43c3e7e6a716fe61b7d Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 03:59:00 -0500 Subject: [PATCH 27/69] Make MyTutorialsState Identifiable --- Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift | 5 +++++ Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift index 958874d5..6e9d637c 100644 --- a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift +++ b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift @@ -61,6 +61,11 @@ extension MyTutorialsState: CaseIterable { } } +// MARK: - Identifiable +extension MyTutorialsState: Identifiable { + var id: Self { self } +} + // MARK: - struct MyTutorialsView { init( diff --git a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift index 34bee3bd..84c67eaa 100644 --- a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift +++ b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift @@ -41,7 +41,7 @@ struct ToggleControlView: View { .frame(height: 2) HStack { - ForEach(MyTutorialsState.allCases, id: \.self, content: toggleButton) + ForEach(MyTutorialsState.allCases, content: toggleButton) } } } From 7b707355a57f677ef69179f15d28760df26cb1f2 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 04:09:28 -0500 Subject: [PATCH 28/69] Organize ToggleControlView --- .../UI/My Tutorials/ToggleControlView.swift | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift index 84c67eaa..236d2e8d 100644 --- a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift +++ b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift @@ -28,12 +28,23 @@ import SwiftUI -struct ToggleControlView: View { - @State var toggleState: MyTutorialsState - @EnvironmentObject var messageBus: MessageBus +struct ToggleControlView { + @State private var toggleState: MyTutorialsState + @EnvironmentObject private var messageBus: MessageBus - var toggleUpdated: ((MyTutorialsState) -> Void)? - + private let toggleUpdated: ((MyTutorialsState) -> Void)? + + init( + toggleState: MyTutorialsState, + toggleUpdated: ((MyTutorialsState) -> Void)? = nil + ) { + self.toggleState = toggleState + self.toggleUpdated = toggleUpdated + } +} + +// MARK: - View +extension ToggleControlView: View { var body: some View { ZStack(alignment: .bottom) { RoundedRectangle(cornerRadius: 1) @@ -45,7 +56,24 @@ struct ToggleControlView: View { } } } - +} + +struct ToggleControlView_Previews: PreviewProvider { + static var previews: some View { + VStack(spacing: 40) { + ForEach(MyTutorialsState.allCases, id: \.self) { + ToggleControlView(toggleState: $0) + } + } + .padding([.vertical], 40) + .padding([.horizontal], 10) + .background(Color.background) + .inAllColorSchemes + } +} + +// MARK: - private +private extension ToggleControlView { private func toggleButton(for state: MyTutorialsState) -> some View { Button { guard state != toggleState else { return } @@ -69,17 +97,3 @@ struct ToggleControlView: View { } } } - -struct ToggleControlView_Previews: PreviewProvider { - static var previews: some View { - VStack(spacing: 40) { - ForEach(MyTutorialsState.allCases, id: \.self) { - ToggleControlView(toggleState: $0) - } - } - .padding([.vertical], 40) - .padding([.horizontal], 10) - .background(Color.background) - .inAllColorSchemes - } -} From 89dc592917e8318ea2e462bf1f2fa6aa13e1efb3 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 05:43:24 -0500 Subject: [PATCH 29/69] Ignore .DS_Store files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 77f0536f..ebf28711 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ DerivedData/ *.perspectivev3 !default.perspectivev3 xcuserdata/ +.DS_Store ## Other *.moved-aside From 69f1f3aab12d79a104aa4412c527b19e96037536 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 06:22:24 -0500 Subject: [PATCH 30/69] Tidy DynamicContentViewModel --- .../ViewModels/DynamicContentViewModel.swift | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift index b75aee20..b4dbb191 100644 --- a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift @@ -72,27 +72,6 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable configureSubscriptions() } - private func configureSubscriptions() { - repository - .contentDynamicState(for: contentId) - .removeDuplicates() - .sink(receiveCompletion: { [weak self] completion in - self?.state = .failed - Failure - .repositoryLoad(from: "DynamicContentViewModel", reason: "Unable to retrieve dynamic download content: \(completion)") - .log() - }) { [weak self] contentState in - guard let self = self else { return } - - self.viewProgress = ContentViewProgressDisplayable(progression: contentState.progression) - self.downloadProgress = DownloadProgressDisplayable(download: contentState.download) - self.bookmarked = contentState.bookmark != nil - self.dynamicContentState = contentState - self.state = .hasData - } - .store(in: &subscriptions) - } - func downloadTapped() -> DownloadDeletionConfirmation? { guard state == .hasData else { return nil } @@ -171,8 +150,10 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable } func bookmarkTapped() { - guard state == .hasData, - let syncAction = syncAction else { return } + guard + state == .hasData, + let syncAction = syncAction + else { return } if bookmarked { do { @@ -186,8 +167,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable } else { do { try syncAction.createBookmark(for: contentId) - let generator = UINotificationFeedbackGenerator() - generator.notificationOccurred(.success) + UINotificationFeedbackGenerator().notificationOccurred(.success) } catch { messageBus.post(message: Message(level: .error, message: .bookmarkCreatedError)) Failure @@ -240,3 +220,27 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable ) } } + +// MARK: - private +private extension DynamicContentViewModel { + func configureSubscriptions() { + repository + .contentDynamicState(for: contentId) + .removeDuplicates() + .sink(receiveCompletion: { [weak self] completion in + self?.state = .failed + Failure + .repositoryLoad(from: "DynamicContentViewModel", reason: "Unable to retrieve dynamic download content: \(completion)") + .log() + }) { [weak self] contentState in + guard let self = self else { return } + + self.viewProgress = ContentViewProgressDisplayable(progression: contentState.progression) + self.downloadProgress = DownloadProgressDisplayable(download: contentState.download) + self.bookmarked = contentState.bookmark != nil + self.dynamicContentState = contentState + self.state = .hasData + } + .store(in: &subscriptions) + } +} From d895fe7e86d0d028faa301be70b1853e47dcc88d Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 07:22:12 -0500 Subject: [PATCH 31/69] Rename constants --- Emitron/Emitron/Constants.swift | 2 +- Emitron/Emitron/Models/SettingsOption.swift | 8 ++++---- Emitron/Emitron/UI/Settings/SettingsList.swift | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Emitron/Emitron/Constants.swift b/Emitron/Emitron/Constants.swift index 053d55a6..cd167605 100644 --- a/Emitron/Emitron/Constants.swift +++ b/Emitron/Emitron/Constants.swift @@ -101,7 +101,7 @@ extension String { // MARK: Settings screens static let settingsPlaybackSpeedLabel = "Video Playback Speed" - static let settingsWifiOnlyDownloadsLabel = "Allow Downloads Over Cellular" + static let settingsAllowDownloadsOverCellularLabel = "Allow Downloads Over Cellular" static let settingsDownloadQualityLabel = "Downloads Quality" static let settingsClosedCaptionOnLabel = "Subtitles" } diff --git a/Emitron/Emitron/Models/SettingsOption.swift b/Emitron/Emitron/Models/SettingsOption.swift index deb31bb2..3b7c75bd 100644 --- a/Emitron/Emitron/Models/SettingsOption.swift +++ b/Emitron/Emitron/Models/SettingsOption.swift @@ -30,7 +30,7 @@ import Combine enum SettingsOption: Int, Identifiable, CaseIterable { case playbackSpeed - case wifiOnlyDownloads + case allowDownloadsOverCellular case downloadQuality case closedCaptionOn @@ -40,8 +40,8 @@ enum SettingsOption: Int, Identifiable, CaseIterable { switch self { case .playbackSpeed: return .settingsPlaybackSpeedLabel - case .wifiOnlyDownloads: - return .settingsWifiOnlyDownloadsLabel + case .allowDownloadsOverCellular: + return .settingsAllowDownloadsOverCellularLabel case .downloadQuality: return .settingsDownloadQualityLabel case .closedCaptionOn: @@ -77,7 +77,7 @@ enum SettingsOption: Int, Identifiable, CaseIterable { var isToggle: Bool { switch self { - case .wifiOnlyDownloads, .closedCaptionOn: + case .allowDownloadsOverCellular, .closedCaptionOn: return true default: return false diff --git a/Emitron/Emitron/UI/Settings/SettingsList.swift b/Emitron/Emitron/UI/Settings/SettingsList.swift index a5909742..274479a1 100644 --- a/Emitron/Emitron/UI/Settings/SettingsList.swift +++ b/Emitron/Emitron/UI/Settings/SettingsList.swift @@ -70,7 +70,7 @@ private extension SettingsList { title: option.title, isOn: $settingsManager.closedCaptionOn ) - case .wifiOnlyDownloads: + case .allowDownloadsOverCellular: SettingsToggleRow( title: option.title, isOn: $settingsManager.wifiOnlyDownloads From 7be0de800e7212f1215dffd6cbb6cf24467d8c37 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 07:22:39 -0500 Subject: [PATCH 32/69] Negate the binding --- Emitron/Emitron.xcodeproj/project.pbxproj | 4 ++ .../Extensions/Binding+Extensions.swift | 39 +++++++++++++++++++ .../Emitron/UI/Settings/SettingsList.swift | 2 +- 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 Emitron/Emitron/Extensions/Binding+Extensions.swift diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index 9c7e58ee..30690804 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -244,6 +244,7 @@ 22F2C45F23EF79F9007ED4A1 /* ContentSubscriptionPlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22F2C45E23EF79F9007ED4A1 /* ContentSubscriptionPlan.swift */; }; 22F2C46123EF7A27007ED4A1 /* ContentSubscriptionPlan+Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22F2C46023EF7A27007ED4A1 /* ContentSubscriptionPlan+Request.swift */; }; 22FDB2EE23CAC7E6001F883E /* ChildContentListingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22FDB2ED23CAC7E6001F883E /* ChildContentListingView.swift */; }; + 492E632627A6B96900CD1F19 /* Binding+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492E632527A6B96900CD1F19 /* Binding+Extensions.swift */; }; 493DA0A127266049006ED195 /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = 493DA0A027266049006ED195 /* GRDB */; }; 494A79A82465C8C90097E8F4 /* RefreshableTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 494A79A72465C8C90097E8F4 /* RefreshableTestCase.swift */; }; 8B283DEF23169A1F001F1B17 /* ProgressBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B283DEE23169A1E001F1B17 /* ProgressBarView.swift */; }; @@ -584,6 +585,7 @@ 22F2C45E23EF79F9007ED4A1 /* ContentSubscriptionPlan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentSubscriptionPlan.swift; sourceTree = ""; }; 22F2C46023EF7A27007ED4A1 /* ContentSubscriptionPlan+Request.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContentSubscriptionPlan+Request.swift"; sourceTree = ""; }; 22FDB2ED23CAC7E6001F883E /* ChildContentListingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChildContentListingView.swift; sourceTree = ""; }; + 492E632527A6B96900CD1F19 /* Binding+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Extensions.swift"; sourceTree = ""; }; 494A79A72465C8C90097E8F4 /* RefreshableTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshableTestCase.swift; sourceTree = ""; }; 8B283DEE23169A1E001F1B17 /* ProgressBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressBarView.swift; sourceTree = ""; }; 8B7E96DC2357A65F0083DA38 /* ProTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProTag.swift; sourceTree = ""; }; @@ -1412,6 +1414,7 @@ B6D4529622CAB3E700BFB812 /* Extensions */ = { isa = PBXGroup; children = ( + 492E632527A6B96900CD1F19 /* Binding+Extensions.swift */, B6C0F0DC22D5FA1C00012839 /* ContentDetailModel+Extensions.swift */, B6D4529B22CAB67900BFB812 /* Date+Extensions.swift */, B6D4529722CAB3F600BFB812 /* DateFormatter+Extensions.swift */, @@ -2136,6 +2139,7 @@ 22C4EAED23DEF910001A3FDA /* SettingsManager.swift in Sources */, 229F0ABC23BADDB00004DD4F /* ContentPersistableState.swift in Sources */, B6C0F0D522D5D47600012839 /* LibraryView.swift in Sources */, + 492E632627A6B96900CD1F19 /* Binding+Extensions.swift in Sources */, 22C0513823A4FBA5004D1223 /* Category+Persistence.swift in Sources */, B6C0F0D322D5D46D00012839 /* DownloadsView.swift in Sources */, 8B283DEF23169A1F001F1B17 /* ProgressBarView.swift in Sources */, diff --git a/Emitron/Emitron/Extensions/Binding+Extensions.swift b/Emitron/Emitron/Extensions/Binding+Extensions.swift new file mode 100644 index 00000000..c27efaa2 --- /dev/null +++ b/Emitron/Emitron/Extensions/Binding+Extensions.swift @@ -0,0 +1,39 @@ +// Copyright (c) 2022 Razeware LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, +// distribute, sublicense, create a derivative work, and/or sell copies of the +// Software in any work that is designed, intended, or marketed for pedagogical or +// instructional purposes related to programming, coding, application development, +// or information technology. Permission for such use, copying, modification, +// merger, publication, distribution, sublicensing, creation of derivative works, +// or sale is expressly withheld. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import struct SwiftUI.Binding + +public extension Binding where Value == Bool { + prefix static func !(binding: Self) -> Self { + .init( + get: { !binding.wrappedValue }, + set: { binding.wrappedValue = !$0 } + ) + } +} + diff --git a/Emitron/Emitron/UI/Settings/SettingsList.swift b/Emitron/Emitron/UI/Settings/SettingsList.swift index 274479a1..de41816b 100644 --- a/Emitron/Emitron/UI/Settings/SettingsList.swift +++ b/Emitron/Emitron/UI/Settings/SettingsList.swift @@ -73,7 +73,7 @@ private extension SettingsList { case .allowDownloadsOverCellular: SettingsToggleRow( title: option.title, - isOn: $settingsManager.wifiOnlyDownloads + isOn: !$settingsManager.wifiOnlyDownloads ) case .downloadQuality: NavigationLink( From 92dd304da15fc416b112cf3c0915d4f0e1fdb561 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 07:22:53 -0500 Subject: [PATCH 33/69] Clean up related files --- Emitron/Emitron/Models/SettingsOption.swift | 26 ------------------- .../Emitron/UI/Settings/SettingsList.swift | 18 ++++++------- .../Content Detail/TextListItemView.swift | 5 +--- 3 files changed, 10 insertions(+), 39 deletions(-) diff --git a/Emitron/Emitron/Models/SettingsOption.swift b/Emitron/Emitron/Models/SettingsOption.swift index 3b7c75bd..4063777a 100644 --- a/Emitron/Emitron/Models/SettingsOption.swift +++ b/Emitron/Emitron/Models/SettingsOption.swift @@ -49,32 +49,6 @@ enum SettingsOption: Int, Identifiable, CaseIterable { } } - var key: SettingsKey { - switch self { - case .playbackSpeed: - return .playbackSpeed - case .wifiOnlyDownloads: - return .wifiOnlyDownloads - case .downloadQuality: - return .downloadQuality - case .closedCaptionOn: - return .closedCaptionOn - } - } - - var detail: [String] { - switch self { - case .playbackSpeed: - return PlaybackSpeed.allCases.map(\.display) - case .wifiOnlyDownloads: - return [String.yes, String.no] - case .downloadQuality: - return Attachment.Kind.downloads.map(\.display) - case .closedCaptionOn: - return [String.yes, String.no] - } - } - var isToggle: Bool { switch self { case .allowDownloadsOverCellular, .closedCaptionOn: diff --git a/Emitron/Emitron/UI/Settings/SettingsList.swift b/Emitron/Emitron/UI/Settings/SettingsList.swift index de41816b..acf09f34 100644 --- a/Emitron/Emitron/UI/Settings/SettingsList.swift +++ b/Emitron/Emitron/UI/Settings/SettingsList.swift @@ -30,7 +30,15 @@ import SwiftUI struct SettingsList { @ObservedObject private var settingsManager: SettingsManager - private var canDownload: Bool + private let canDownload: Bool +} + +// MARK: - internal +extension SettingsList { + init(settingsManager: ObservedObject, canDownload: Bool) { + _settingsManager = settingsManager + self.canDownload = canDownload + } } // MARK: - View @@ -53,14 +61,6 @@ struct SettingsList_Previews: PreviewProvider { } } -// MARK: - internal -extension SettingsList { - init(settingsManager: ObservedObject, canDownload: Bool) { - _settingsManager = settingsManager - self.canDownload = canDownload - } -} - // MARK: - private private extension SettingsList { @ViewBuilder subscript(option: SettingsOption) -> some View { diff --git a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift index 636c943e..b3f216d6 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift @@ -103,10 +103,7 @@ struct TextListItemView: View { SCNetworkReachabilityGetFlags(reachability, &flags) let isReachable = flags.contains(.reachable) let isCellular = flags.contains(.isWWAN) - if isReachable && isCellular && settingsManager.wifiOnlyDownloads { - return true - } - return false + return [isReachable, isCellular, settingsManager.wifiOnlyDownloads].allSatisfy { $0 } } } From 1141973554c5f14d89684ddf9edac512122f6dcb Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 22:56:40 -0500 Subject: [PATCH 34/69] Move chevrons down and add "More/Less" --- .../Content Detail/TextListItemView.swift | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift index b3f0470f..114c61ef 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift @@ -60,25 +60,12 @@ struct TextListItemView: View { VStack(spacing: 15) { HStack { VStack(alignment: .leading, spacing: 5) { - HStack { - Text(content.name) - .font(.uiTitle5) - .kerning(-0.5) - .lineSpacing(3) - .foregroundColor(.titleText) - .fixedSize(horizontal: false, vertical: true) - - Spacer() - - Image(systemName: descriptionHasLineLimit ? "chevron.up" : "chevron.down") - .foregroundColor(.contentText) - .font(.uiLabelBold) - .onTapGesture { - withAnimation { - descriptionHasLineLimit.toggle() - } - } - } + Text(content.name) + .font(.uiTitle5) + .kerning(-0.5) + .lineSpacing(3) + .foregroundColor(.titleText) + .fixedSize(horizontal: false, vertical: true) Text(content.descriptionPlainText) .multilineTextAlignment(.leading) @@ -88,10 +75,29 @@ struct TextListItemView: View { .lineSpacing(3) .foregroundColor(.contentText) .padding(.bottom, 5) + + HStack { + Text(content.duration.minuteSecondTimeFromSeconds) - Text(content.duration.minuteSecondTimeFromSeconds) - .font(.uiFootnote) - .foregroundColor(.contentText) + Spacer() + + Button { + withAnimation { + descriptionHasLineLimit.toggle() + } + } label: { + let (title, systemName) = + descriptionHasLineLimit + ? ("More", "chevron.down") + : ("Less", "chevron.up") + + Text(title) + Image(systemName: systemName) + } + } + .font(.uiFootnote) + .foregroundColor(.contentText) + .padding(.trailing) } Spacer() From 6cf96f52026ee2c7e5ccb2f9f2e08e338ede113f Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sun, 30 Jan 2022 22:57:02 -0500 Subject: [PATCH 35/69] Make `wifiOnlyOnCellular` a computed property --- .../UI/Shared/Content Detail/TextListItemView.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift index 114c61ef..8074c140 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift @@ -107,8 +107,13 @@ struct TextListItemView: View { Spacer() DownloadIcon(downloadProgress: dynamicContentViewModel.downloadProgress) .onTapGesture { - if wifiOnlyOnCellular() { - messageBus.post(message: Message(level: .error, message: "To download the episode, either reconnect to a Wifi network or disable 'Downloads (Wifi Only)' in the settings.")) + if wifiOnlyOnCellular { + messageBus.post( + message: .init( + level: .error, + message: "To download the episode, either reconnect to a Wifi network or disable 'Downloads (Wifi Only)' in the settings." + ) + ) } else { download() } @@ -124,7 +129,7 @@ struct TextListItemView: View { } } - private func wifiOnlyOnCellular() -> Bool { + private var wifiOnlyOnCellular: Bool { guard let reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, "www.raywenderlich.com") else { return false } From 5e507de133cded3d7c8a5b2f20740e35b51e9762 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Mon, 31 Jan 2022 01:40:49 -0500 Subject: [PATCH 36/69] Shorten `EmitronApp` to `App` --- Emitron/Emitron.xcodeproj/project.pbxproj | 8 ++--- .../Emitron/{EmitronApp.swift => App.swift} | 35 +++++++++++-------- .../Library/Filtering/FiltersHeaderView.swift | 4 +-- .../Emitron/UI/Settings/SettingsList.swift | 2 +- .../Emitron/UI/Settings/SettingsView.swift | 4 +-- .../Downloads/DownloadProcessorTest.swift | 2 +- .../Downloads/DownloadQueueManagerTest.swift | 2 +- .../Downloads/DownloadServiceTest.swift | 8 ++--- 8 files changed, 36 insertions(+), 29 deletions(-) rename Emitron/Emitron/{EmitronApp.swift => App.swift} (93%) diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index 3bc7ffde..5c55fbfb 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -332,7 +332,7 @@ B6FC15D522CB68E20078CEDB /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FC15D422CB68E20078CEDB /* Service.swift */; }; B6FC15D722CB817C0078CEDB /* BookmarksService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FC15D622CB817C0078CEDB /* BookmarksService.swift */; }; D31CEF2C26E2C421008083D0 /* FilterGroupTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D31CEF2B26E2C421008083D0 /* FilterGroupTest.swift */; }; - D3D9C5C72645DA9A002FD479 /* EmitronApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D9C5C62645DA9A002FD479 /* EmitronApp.swift */; }; + D3D9C5C72645DA9A002FD479 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D9C5C62645DA9A002FD479 /* App.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -682,7 +682,7 @@ B6FC15D422CB68E20078CEDB /* Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = ""; }; B6FC15D622CB817C0078CEDB /* BookmarksService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksService.swift; sourceTree = ""; }; D31CEF2B26E2C421008083D0 /* FilterGroupTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterGroupTest.swift; sourceTree = ""; }; - D3D9C5C62645DA9A002FD479 /* EmitronApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmitronApp.swift; sourceTree = ""; }; + D3D9C5C62645DA9A002FD479 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1459,7 +1459,7 @@ 22B8265E23AF5B3400D4BA23 /* Data */, 22BFE75D23DACEC000495BA9 /* Data Synchronisation */, B6D7DC2E22C79743006DD325 /* AppDelegate.swift */, - D3D9C5C62645DA9A002FD479 /* EmitronApp.swift */, + D3D9C5C62645DA9A002FD479 /* App.swift */, B6D7DC3422C79745006DD325 /* Assets.xcassets */, 223DECF1240989430017F7CA /* App Icons */, 22D177032350748E0038333D /* Configuration */, @@ -2039,7 +2039,7 @@ 2278AE48240A4BBC00855221 /* IconView.swift in Sources */, B6419BB322EF5622003AC14E /* Logger.swift in Sources */, 223D77A523B06FFA005BE95D /* AttachmentAdapter.swift in Sources */, - D3D9C5C72645DA9A002FD479 /* EmitronApp.swift in Sources */, + D3D9C5C72645DA9A002FD479 /* App.swift in Sources */, 223D77B523B0C0CA005BE95D /* Domain.swift in Sources */, 223D77B723B0C0D1005BE95D /* Group.swift in Sources */, B6D8EB2722CBA86000DE29AF /* ProgressionsRequest.swift in Sources */, diff --git a/Emitron/Emitron/EmitronApp.swift b/Emitron/Emitron/App.swift similarity index 93% rename from Emitron/Emitron/EmitronApp.swift rename to Emitron/Emitron/App.swift index e3aa7251..fc7f792d 100644 --- a/Emitron/Emitron/EmitronApp.swift +++ b/Emitron/Emitron/App.swift @@ -31,10 +31,8 @@ import SwiftUI import GRDB @main -struct EmitronApp: App { - @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate - - typealias EmitronObjects = ( +struct App { + typealias Objects = ( persistenceStore: PersistenceStore, guardpost: Guardpost, sessionController: SessionController, @@ -43,7 +41,8 @@ struct EmitronApp: App { dataManager: DataManager, messageBus: MessageBus ) - + + @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate private var persistenceStore: PersistenceStore private var guardpost: Guardpost private var dataManager: DataManager @@ -54,9 +53,8 @@ struct EmitronApp: App { private var iconManager: IconManager init() { - // setup objects - let emitronObjects = EmitronApp.emitronObjects() + let emitronObjects = App.objects persistenceStore = emitronObjects.persistenceStore guardpost = emitronObjects.guardpost dataManager = emitronObjects.dataManager @@ -78,7 +76,10 @@ struct EmitronApp: App { // additional setup setupAppReview() } +} +// MARK: - SwiftUI.App +extension App: SwiftUI.App { var body: some Scene { WindowGroup { ZStack { @@ -97,8 +98,11 @@ struct EmitronApp: App { } } } +} - static func emitronObjects() -> EmitronObjects { +// MARK: - internal +extension App { + static var objects: Objects { // Initialise the database // swiftlint:disable:next force_try let databaseURL = try! FileManager.default @@ -117,7 +121,7 @@ struct EmitronApp: App { let messageBus = MessageBus() let dataManager = DataManager(sessionController: sessionController, persistenceStore: persistenceStore, downloadService: downloadService, messageBus: messageBus, settingsManager: settingsManager) - return EmitronObjects( + return Objects( persistenceStore: persistenceStore, guardpost: guardpost, sessionController: sessionController, @@ -127,8 +131,11 @@ struct EmitronApp: App { messageBus: messageBus ) } +} - private mutating func startServices() { +// MARK: - private +private extension App { + mutating func startServices() { // guardpost guardpost = Guardpost(baseURL: "https://accounts.raywenderlich.com", urlScheme: "com.razeware.emitron://", @@ -163,7 +170,7 @@ struct EmitronApp: App { downloadService.startProcessing() } - private func customizeNavigationBar() { + func customizeNavigationBar() { UINavigationBar.appearance().backgroundColor = .backgroundColor UINavigationBar.appearance().largeTitleTextAttributes = [ @@ -176,7 +183,7 @@ struct EmitronApp: App { ] } - private func customizeTableView() { + func customizeTableView() { UITableView.appearance().separatorColor = .clear UITableViewCell.appearance().backgroundColor = .backgroundColor UITableViewCell.appearance().selectionStyle = .none @@ -184,11 +191,11 @@ struct EmitronApp: App { UITableView.appearance().backgroundColor = .backgroundColor } - private func customizeControls() { + func customizeControls() { UISwitch.appearance().onTintColor = .accent } - private func setupAppReview() { + func setupAppReview() { if NSUbiquitousKeyValueStore.default.object(forKey: LookupKey.requestReview) == nil { NSUbiquitousKeyValueStore.default.set(Date().timeIntervalSince1970, forKey: LookupKey.requestReview) } diff --git a/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift b/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift index 469ffd09..661fdc06 100644 --- a/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift @@ -106,12 +106,12 @@ struct FilterGroupView_Previews: PreviewProvider { VStack { FiltersHeaderView( filterGroup: FilterGroup(type: .difficulties, filters: filters), - filters: Filters(settingsManager: EmitronApp.emitronObjects().settingsManager), + filters: Filters(settingsManager: App.objects.settingsManager), isExpanded: true ) FiltersHeaderView( filterGroup: FilterGroup(type: .categories, filters: filters), - filters: Filters(settingsManager: EmitronApp.emitronObjects().settingsManager) + filters: Filters(settingsManager: App.objects.settingsManager) ) } .padding() diff --git a/Emitron/Emitron/UI/Settings/SettingsList.swift b/Emitron/Emitron/UI/Settings/SettingsList.swift index acf09f34..003052ef 100644 --- a/Emitron/Emitron/UI/Settings/SettingsList.swift +++ b/Emitron/Emitron/UI/Settings/SettingsList.swift @@ -53,7 +53,7 @@ extension SettingsList: View { struct SettingsList_Previews: PreviewProvider { static var previews: some View { SettingsList( - settingsManager: .init(initialValue: EmitronApp.emitronObjects().settingsManager), + settingsManager: .init(initialValue: App.objects.settingsManager), canDownload: true ) .background(Color.background) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index d69f311f..a6071585 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -126,9 +126,9 @@ struct SettingsView: View { struct SettingsView_Previews: PreviewProvider { static var previews: some View { - SettingsView(settingsManager: EmitronApp.emitronObjects().settingsManager) + SettingsView(settingsManager: App.objects.settingsManager) .background(Color.background) - .environmentObject(EmitronApp.emitronObjects().sessionController) + .environmentObject(App.objects.sessionController) .inAllColorSchemes } } diff --git a/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift b/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift index c5a1aa4a..06a3e499 100644 --- a/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift @@ -36,6 +36,6 @@ class DownloadProcessorTest: XCTestCase { override func setUp() { super.setUp() - downloadProcessor = DownloadProcessor(settingsManager: EmitronApp.emitronObjects().settingsManager) + downloadProcessor = DownloadProcessor(settingsManager: App.objects.settingsManager) } } diff --git a/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift b/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift index 2fabdc3a..d2956a48 100644 --- a/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift @@ -54,7 +54,7 @@ class DownloadQueueManagerTest: XCTestCase { fatalError("Failed trying to test database") } persistenceStore = PersistenceStore(db: database) - settingsManager = EmitronApp.emitronObjects().settingsManager + settingsManager = App.objects.settingsManager let userModelController = UserMCMock(user: .withDownloads) downloadService = DownloadService(persistenceStore: persistenceStore, userModelController: userModelController, videosServiceProvider: { _ in self.videoService }, settingsManager: settingsManager) diff --git a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift index 782353b2..56a00365 100644 --- a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift @@ -44,7 +44,7 @@ class DownloadServiceTest: XCTestCase { database = try! EmitronDatabase.testDatabase() persistenceStore = PersistenceStore(db: database) userModelController = .init(user: .withDownloads) - settingsManager = EmitronApp.emitronObjects().settingsManager + settingsManager = App.objects.settingsManager downloadService = DownloadService(persistenceStore: persistenceStore, userModelController: userModelController, videosServiceProvider: { _ in self.videoService }, @@ -59,7 +59,7 @@ class DownloadServiceTest: XCTestCase { super.tearDown() videoService.reset() deleteSampleFile(fileManager: FileManager.default) - EmitronApp.emitronObjects().settingsManager.resetAll() + App.objects.settingsManager.resetAll() } func getAllContents() -> [Content] { @@ -462,7 +462,7 @@ class DownloadServiceTest: XCTestCase { downloadService = DownloadService(persistenceStore: persistenceStore, userModelController: userModelController, videosServiceProvider: { _ in self.videoService }, - settingsManager: EmitronApp.emitronObjects().settingsManager) + settingsManager: App.objects.settingsManager) XCTAssert(!fileManager.fileExists(atPath: sampleFile.path)) } @@ -487,7 +487,7 @@ class DownloadServiceTest: XCTestCase { downloadService = DownloadService(persistenceStore: persistenceStore, userModelController: userModelController, videosServiceProvider: { _ in self.videoService }, - settingsManager: EmitronApp.emitronObjects().settingsManager) + settingsManager: App.objects.settingsManager) XCTAssertFalse(fileManager.fileExists(atPath: sampleFile.path)) } From 79120bb2904b791dc8644f1d5d8eb30e8e6ecd2e Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Mon, 31 Jan 2022 01:47:05 -0500 Subject: [PATCH 37/69] =?UTF-8?q?Update=20all=20Razeware=20=C2=A9=20to=202?= =?UTF-8?q?022?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Emitron/Emitron/App.swift | 2 +- Emitron/Emitron/AppDelegate.swift | 2 +- Emitron/Emitron/Combine/PublishedPrePostFacto.swift | 2 +- Emitron/Emitron/Configuration/Configuration.swift | 2 +- Emitron/Emitron/Configuration/secrets.template.xcconfig | 2 +- Emitron/Emitron/Constants.swift | 2 +- Emitron/Emitron/Data Synchronisation/ProgressEngine.swift | 2 +- Emitron/Emitron/Data Synchronisation/SyncAction.swift | 2 +- Emitron/Emitron/Data Synchronisation/SyncEngine.swift | 2 +- .../Data Synchronisation/SyncRequest+ProgressionUpdate.swift | 2 +- .../Emitron/Data Synchronisation/SyncRequest+WatchStat.swift | 2 +- .../Emitron/Data/ContentRepositories/BookmarkRepository.swift | 2 +- .../Emitron/Data/ContentRepositories/CompletedRepository.swift | 2 +- .../Emitron/Data/ContentRepositories/ContentRepository.swift | 2 +- .../Emitron/Data/ContentRepositories/DownloadRepository.swift | 2 +- .../Emitron/Data/ContentRepositories/InProgressRepository.swift | 2 +- .../Emitron/Data/ContentRepositories/LibraryRepository.swift | 2 +- Emitron/Emitron/Data/DataManager.swift | 2 +- Emitron/Emitron/Data/DataState.swift | 2 +- .../Emitron/Data/Other Repositories/CategoryRepository.swift | 2 +- Emitron/Emitron/Data/Other Repositories/DomainRepository.swift | 2 +- Emitron/Emitron/Data/Repository.swift | 2 +- .../BookmarksService+ContentServiceAdapter.swift | 2 +- .../Emitron/Data/Service Adapters/ContentServiceAdapter.swift | 2 +- .../ContentsService+ContentServiceAdapter.swift | 2 +- .../ProgressionsService+ContentServiceAdapter.swift | 2 +- Emitron/Emitron/Data/States/ChildContentsState.swift | 2 +- Emitron/Emitron/Data/States/ContentPersistableState.swift | 2 +- Emitron/Emitron/Data/States/ContentSummaryState.swift | 2 +- Emitron/Emitron/Data/States/DynamicContentState.swift | 2 +- Emitron/Emitron/Data/States/VideoPlaybackState.swift | 2 +- Emitron/Emitron/Data/ViewModels/ChildContentsViewModel.swift | 2 +- Emitron/Emitron/Data/ViewModels/ContentScreen.swift | 2 +- .../Data/ViewModels/DataCacheChildContentsViewModel.swift | 2 +- .../Emitron/Data/ViewModels/DownloadDeletionConfirmation.swift | 2 +- Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift | 2 +- .../ViewModels/PersistenceStoreChildContentsViewModel.swift | 2 +- Emitron/Emitron/Data/ViewModels/TabViewModel.swift | 2 +- Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift | 2 +- .../Displayable/Content+ChildContentListDisplayable.swift | 2 +- Emitron/Emitron/Displayable/ContentDisplayable.swift | 2 +- .../ContentSummaryState+ContentListDisplayable.swift | 2 +- Emitron/Emitron/Displayable/DynamicContentDisplayable.swift | 2 +- Emitron/Emitron/Displayable/Group+GroupDisplayable.swift | 2 +- Emitron/Emitron/Downloads/DownloadAction.swift | 2 +- Emitron/Emitron/Downloads/DownloadProcessor.swift | 2 +- Emitron/Emitron/Downloads/DownloadQueueManager.swift | 2 +- Emitron/Emitron/Downloads/DownloadService.swift | 2 +- Emitron/Emitron/Extensions/Comparable+Clamped.swift | 2 +- Emitron/Emitron/Extensions/ContentDetailModel+Extensions.swift | 2 +- Emitron/Emitron/Extensions/Date+Extensions.swift | 2 +- Emitron/Emitron/Extensions/DateFormatter+Extensions.swift | 2 +- Emitron/Emitron/Extensions/TimeInterval+Extensions.swift | 2 +- Emitron/Emitron/Extensions/UIApplication+DismissKeyboard.swift | 2 +- Emitron/Emitron/Extensions/UIViewController+Extensions.swift | 2 +- Emitron/Emitron/Extensions/View+Extensions.swift | 2 +- Emitron/Emitron/Filters/Filter.swift | 2 +- Emitron/Emitron/Filters/FilterGroup.swift | 2 +- Emitron/Emitron/Filters/Filters.swift | 2 +- Emitron/Emitron/Filters/SortFilter.swift | 2 +- Emitron/Emitron/Guardpost/Guardpost.swift | 2 +- Emitron/Emitron/Guardpost/SSO/SingleSignOnRequest.swift | 2 +- Emitron/Emitron/Guardpost/SSO/SingleSignOnResponse.swift | 2 +- Emitron/Emitron/Guardpost/Utils/Data+Hex.swift | 2 +- Emitron/Emitron/Guardpost/Utils/GuardpostConstants.swift | 2 +- Emitron/Emitron/Guardpost/Utils/RandomString.swift | 2 +- Emitron/Emitron/Guardpost/Utils/String+Base64.swift | 2 +- Emitron/Emitron/Logging/Logger.swift | 2 +- Emitron/Emitron/Models/Attachment.swift | 2 +- Emitron/Emitron/Models/Bookmark.swift | 2 +- Emitron/Emitron/Models/Category.swift | 2 +- Emitron/Emitron/Models/Content.swift | 2 +- Emitron/Emitron/Models/ContentCategory.swift | 2 +- Emitron/Emitron/Models/ContentDifficulty.swift | 2 +- Emitron/Emitron/Models/ContentDomain.swift | 2 +- Emitron/Emitron/Models/ContentSubscriptionPlan.swift | 2 +- Emitron/Emitron/Models/ContentType.swift | 2 +- Emitron/Emitron/Models/DataCache.swift | 2 +- Emitron/Emitron/Models/Domain.swift | 2 +- Emitron/Emitron/Models/Download.swift | 2 +- Emitron/Emitron/Models/FossLicense.swift | 2 +- Emitron/Emitron/Models/Group.swift | 2 +- Emitron/Emitron/Models/Icon.swift | 2 +- Emitron/Emitron/Models/Permission.swift | 2 +- Emitron/Emitron/Models/PlaybackSpeed.swift | 2 +- Emitron/Emitron/Models/Progression.swift | 2 +- Emitron/Emitron/Models/SettingsOption.swift | 2 +- Emitron/Emitron/Models/SyncRequest.swift | 2 +- Emitron/Emitron/Models/User.swift | 2 +- Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift | 2 +- Emitron/Emitron/Networking/Adapters/EntityAdapter.swift | 2 +- .../Networking/Adapters/EntityAdapters/AttachmentAdapter.swift | 2 +- .../Networking/Adapters/EntityAdapters/BookmarkAdapter.swift | 2 +- .../Networking/Adapters/EntityAdapters/CategoryAdapter.swift | 2 +- .../Networking/Adapters/EntityAdapters/ContentAdapter.swift | 2 +- .../Adapters/EntityAdapters/ContentCategoryAdapter.swift | 2 +- .../Adapters/EntityAdapters/ContentDomainAdapter.swift | 2 +- .../Networking/Adapters/EntityAdapters/DomainAdapter.swift | 2 +- .../Networking/Adapters/EntityAdapters/GroupAdapter.swift | 2 +- .../Networking/Adapters/EntityAdapters/PermissionAdapter.swift | 2 +- .../Networking/Adapters/EntityAdapters/ProgressionAdapter.swift | 2 +- Emitron/Emitron/Networking/JSONAPI/JSONAPIDocument.swift | 2 +- Emitron/Emitron/Networking/JSONAPI/JSONAPIError.swift | 2 +- Emitron/Emitron/Networking/JSONAPI/JSONAPIErrorSource.swift | 2 +- Emitron/Emitron/Networking/JSONAPI/JSONAPIRelationship.swift | 2 +- Emitron/Emitron/Networking/JSONAPI/JSONAPIResource.swift | 2 +- Emitron/Emitron/Networking/Network/RWAPI.swift | 2 +- Emitron/Emitron/Networking/Network/RWEnvironment.swift | 2 +- Emitron/Emitron/Networking/Requests/BookmarkRequest.swift | 2 +- Emitron/Emitron/Networking/Requests/CategoriesRequest.swift | 2 +- Emitron/Emitron/Networking/Requests/ContentsRequest.swift | 2 +- Emitron/Emitron/Networking/Requests/DomainsRequest.swift | 2 +- .../Networking/Requests/Models/ContentDifficulty+Request.swift | 2 +- .../Requests/Models/ContentSubscriptionPlan+Request.swift | 2 +- .../Networking/Requests/Models/ContentType+Request.swift | 2 +- Emitron/Emitron/Networking/Requests/Parameters.swift | 2 +- Emitron/Emitron/Networking/Requests/PermissionsRequest.swift | 2 +- Emitron/Emitron/Networking/Requests/ProgressionsRequest.swift | 2 +- Emitron/Emitron/Networking/Requests/Request.swift | 2 +- Emitron/Emitron/Networking/Requests/VideosRequest.swift | 2 +- Emitron/Emitron/Networking/Requests/WatchStatsRequest.swift | 2 +- Emitron/Emitron/Networking/Services/BookmarksService.swift | 2 +- Emitron/Emitron/Networking/Services/CategoriesService.swift | 2 +- Emitron/Emitron/Networking/Services/ContentsService.swift | 2 +- Emitron/Emitron/Networking/Services/DomainsService.swift | 2 +- Emitron/Emitron/Networking/Services/PermissionsService.swift | 2 +- Emitron/Emitron/Networking/Services/ProgressionsService.swift | 2 +- Emitron/Emitron/Networking/Services/Service.swift | 2 +- Emitron/Emitron/Networking/Services/VideosService.swift | 2 +- Emitron/Emitron/Networking/Services/WatchStatsService.swift | 2 +- Emitron/Emitron/Ownable/Content+Ownable.swift | 2 +- Emitron/Emitron/Ownable/Ownable.swift | 2 +- Emitron/Emitron/Persistence/EmitronDatabase.swift | 2 +- Emitron/Emitron/Persistence/Models/Bookmark+Persistence.swift | 2 +- Emitron/Emitron/Persistence/Models/Category+Persistence.swift | 2 +- Emitron/Emitron/Persistence/Models/Content+Persistence.swift | 2 +- .../Persistence/Models/ContentCategory+Persistence.swift | 2 +- .../Emitron/Persistence/Models/ContentDomain+Persistence.swift | 2 +- Emitron/Emitron/Persistence/Models/Domain+Persistence.swift | 2 +- Emitron/Emitron/Persistence/Models/Download+Persistence.swift | 2 +- Emitron/Emitron/Persistence/Models/Group+Persistence.swift | 2 +- .../Emitron/Persistence/Models/Progression+Persistence.swift | 2 +- .../Emitron/Persistence/Models/SyncRequest+Persistence.swift | 2 +- Emitron/Emitron/Persistence/PersistenceStore+Categories.swift | 2 +- Emitron/Emitron/Persistence/PersistenceStore+Domains.swift | 2 +- Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift | 2 +- Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift | 2 +- .../Emitron/Persistence/PersistenceStore+Synchronisation.swift | 2 +- Emitron/Emitron/Persistence/PersistenceStore.swift | 2 +- .../Persistence/States/ChildContentsState+Fetchable.swift | 2 +- .../Persistence/States/ContentSummaryState+Fetchable.swift | 2 +- Emitron/Emitron/Protocols/ContentPaginatable.swift | 2 +- Emitron/Emitron/Protocols/Refreshable.swift | 2 +- Emitron/Emitron/Sessions/SessionController+States.swift | 2 +- Emitron/Emitron/Sessions/SessionController.swift | 2 +- Emitron/Emitron/Sessions/User+Backdoor.swift | 2 +- Emitron/Emitron/Settings/EmitronSettings.swift | 2 +- Emitron/Emitron/Settings/IconManager.swift | 2 +- Emitron/Emitron/Settings/SettingsKey.swift | 2 +- Emitron/Emitron/Settings/SettingsManager.swift | 2 +- Emitron/Emitron/Settings/SettingsSelectable.swift | 2 +- Emitron/Emitron/Styleguide/CGFloat+Dimensions.swift | 2 +- Emitron/Emitron/Styleguide/Color+Extensions.swift | 2 +- Emitron/Emitron/Styleguide/Font+Extensions.swift | 2 +- Emitron/Emitron/Styleguide/Image+Extensions.swift | 2 +- Emitron/Emitron/Styleguide/UIColor+Extensions.swift | 2 +- Emitron/Emitron/Styleguide/UIFont+Extensions.swift | 2 +- Emitron/Emitron/UI/App Root/LoginView.swift | 2 +- Emitron/Emitron/UI/App Root/LogoutView.swift | 2 +- Emitron/Emitron/UI/App Root/MainView.swift | 2 +- Emitron/Emitron/UI/App Root/MessageBarView.swift | 2 +- Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift | 2 +- Emitron/Emitron/UI/App Root/SnackbarView.swift | 2 +- Emitron/Emitron/UI/App Root/TabNavView.swift | 2 +- .../UI/Downloads/DownloadDeletionConfirmation+Alert.swift | 2 +- Emitron/Emitron/UI/Downloads/DownloadsView.swift | 2 +- Emitron/Emitron/UI/Empty States/ErrorView.swift | 2 +- Emitron/Emitron/UI/Empty States/LoadingView.swift | 2 +- Emitron/Emitron/UI/Empty States/NoResultsView.swift | 2 +- Emitron/Emitron/UI/Empty States/OfflineView.swift | 2 +- Emitron/Emitron/UI/Generic/PagerView.swift | 2 +- Emitron/Emitron/UI/Generic/PagingIndicatorView.swift | 2 +- .../Emitron/UI/Library/Filtering/AppliedFilterTagButton.swift | 2 +- Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift | 2 +- Emitron/Emitron/UI/Library/Filtering/FiltersView.swift | 2 +- Emitron/Emitron/UI/Library/Filtering/TitleCheckmarkView.swift | 2 +- Emitron/Emitron/UI/Library/LibraryView.swift | 2 +- Emitron/Emitron/UI/Library/SearchFieldView.swift | 2 +- Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift | 2 +- Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift | 2 +- Emitron/Emitron/UI/PortraitHostingController.swift | 2 +- Emitron/Emitron/UI/SceneDelegate.swift | 2 +- Emitron/Emitron/UI/Settings/Icons/IconChooserView.swift | 2 +- Emitron/Emitron/UI/Settings/Icons/IconView.swift | 2 +- Emitron/Emitron/UI/Settings/Licenses/LicenseDetailView.swift | 2 +- Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift | 2 +- Emitron/Emitron/UI/Settings/SettingsDisclosureRow.swift | 2 +- Emitron/Emitron/UI/Settings/SettingsList.swift | 2 +- Emitron/Emitron/UI/Settings/SettingsSelectionView.swift | 2 +- Emitron/Emitron/UI/Settings/SettingsToggleRow.swift | 2 +- Emitron/Emitron/UI/Settings/SettingsView.swift | 2 +- Emitron/Emitron/UI/Shared/CheckmarkView.swift | 2 +- .../Shared/Content Detail/Child Listing/CompletedIconView.swift | 2 +- .../Content Detail/Child Listing/ContinueButtonView.swift | 2 +- .../UI/Shared/Content Detail/Child Listing/LockedIconView.swift | 2 +- .../UI/Shared/Content Detail/Child Listing/NumberIconView.swift | 2 +- .../UI/Shared/Content Detail/Child Listing/PlayButtonView.swift | 2 +- .../Content Detail/Child Listing/VideoOverlayButtonView.swift | 2 +- .../UI/Shared/Content Detail/ChildContentListingView.swift | 2 +- .../Emitron/UI/Shared/Content Detail/ContentDetailView.swift | 2 +- .../Emitron/UI/Shared/Content Detail/ContentSummaryView.swift | 2 +- Emitron/Emitron/UI/Shared/Content Detail/CourseHeaderView.swift | 2 +- .../UI/Shared/Content Detail/ProContentLockedOverlayView.swift | 2 +- Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift | 2 +- .../UI/Shared/Content Detail/VerticalFadeImageView.swift | 2 +- Emitron/Emitron/UI/Shared/Content List/CardView.swift | 2 +- Emitron/Emitron/UI/Shared/Content List/ContentListView.swift | 2 +- Emitron/Emitron/UI/Shared/Download Icon/ArrowInCircleView.swift | 2 +- .../Emitron/UI/Shared/Download Icon/CircleProgressBarView.swift | 2 +- Emitron/Emitron/UI/Shared/Download Icon/DownloadIcon.swift | 2 +- .../Emitron/UI/Shared/Download Icon/DownloadWarningView.swift | 2 +- .../Emitron/UI/Shared/Download Icon/SpinningCircleView.swift | 2 +- Emitron/Emitron/UI/Shared/MainButtonView.swift | 2 +- Emitron/Emitron/UI/Shared/ProgressBarView.swift | 2 +- Emitron/Emitron/UI/Shared/Tags/CompletedTag.swift | 2 +- Emitron/Emitron/UI/Shared/Tags/ProTag.swift | 2 +- Emitron/Emitron/UI/Shared/Tags/TagView.swift | 2 +- .../Emitron/UI/Video/FullScreenVideoPlayerRepresentable.swift | 2 +- .../Emitron/UI/Video/FullScreenVideoPlayerViewController.swift | 2 +- Emitron/Emitron/Utilities/MessageBus.swift | 2 +- Emitron/emitronScreenshots/EmitronScreenshots.swift | 2 +- Emitron/emitronTests/Combine/PublishedPrePostFactoTest.swift | 2 +- Emitron/emitronTests/Data Synchronisation/SyncEngineTest.swift | 2 +- Emitron/emitronTests/Data/DataCacheTest.swift | 2 +- .../Data/States/ContentPersistableState+Mocks.swift | 2 +- Emitron/emitronTests/Downloads/DownloadProcessorTest.swift | 2 +- Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift | 2 +- Emitron/emitronTests/Downloads/DownloadServiceTest.swift | 2 +- Emitron/emitronTests/Filters/FilterGroupTest.swift | 2 +- Emitron/emitronTests/Model Controllers/Mock/UserMCMock.swift | 2 +- Emitron/emitronTests/Models/AttachmentTest+Mocks.swift | 2 +- Emitron/emitronTests/Models/AttachmentTest.swift | 2 +- Emitron/emitronTests/Models/ContentTest+Mocks.swift | 2 +- Emitron/emitronTests/Models/Mocks/Category+Mocks.swift | 2 +- Emitron/emitronTests/Models/Mocks/Domain+Mocks.swift | 2 +- Emitron/emitronTests/Models/Mocks/Permissions+Mocks.swift | 2 +- Emitron/emitronTests/Models/Mocks/User+Mocks.swift | 2 +- Emitron/emitronTests/Models/UserTest.swift | 2 +- .../Adapters/EntityAdapters/AttachmentAdapterTest.swift | 2 +- .../Adapters/EntityAdapters/BookmarkAdapterTest.swift | 2 +- .../Adapters/EntityAdapters/CategoryAdapterTest.swift | 2 +- .../Networking/Adapters/EntityAdapters/ContentAdapterTest.swift | 2 +- .../Adapters/EntityAdapters/ContentCategoryAdapterTest.swift | 2 +- .../Adapters/EntityAdapters/ContentDomainAdapterTest.swift | 2 +- .../Networking/Adapters/EntityAdapters/DomainAdapterTest.swift | 2 +- .../Networking/Adapters/EntityAdapters/GroupAdapterTest.swift | 2 +- .../Adapters/EntityAdapters/PermissionAdapterTest.swift | 2 +- .../Adapters/EntityAdapters/ProgressionAdapterTest.swift | 2 +- Emitron/emitronTests/Persistence/EmitronDatabaseTest.swift | 2 +- Emitron/emitronTests/Persistence/Models/ContentTest.swift | 2 +- Emitron/emitronTests/Persistence/Models/DownloadTest.swift | 2 +- Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift | 2 +- .../Persistence/PersistenceStore+DownloadsTest.swift | 2 +- .../Persistence/PersistenceStore+SynchronisationTest.swift | 2 +- .../Persistence/PersistenceStore+UserKeychainTest.swift | 2 +- Emitron/emitronTests/Protocols/RefreshableTestCase.swift | 2 +- Emitron/emitronTests/Services/Mock/VideosServiceMock.swift | 2 +- Emitron/emitronTests/Settings/SettingsManagerTest.swift | 2 +- 268 files changed, 268 insertions(+), 268 deletions(-) diff --git a/Emitron/Emitron/App.swift b/Emitron/Emitron/App.swift index fc7f792d..4eb8eb65 100644 --- a/Emitron/Emitron/App.swift +++ b/Emitron/Emitron/App.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/AppDelegate.swift b/Emitron/Emitron/AppDelegate.swift index 2e4eb502..e85016c5 100644 --- a/Emitron/Emitron/AppDelegate.swift +++ b/Emitron/Emitron/AppDelegate.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Combine/PublishedPrePostFacto.swift b/Emitron/Emitron/Combine/PublishedPrePostFacto.swift index 848ccc9a..652c35b3 100644 --- a/Emitron/Emitron/Combine/PublishedPrePostFacto.swift +++ b/Emitron/Emitron/Combine/PublishedPrePostFacto.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Configuration/Configuration.swift b/Emitron/Emitron/Configuration/Configuration.swift index 07b0b41f..b08ed21e 100644 --- a/Emitron/Emitron/Configuration/Configuration.swift +++ b/Emitron/Emitron/Configuration/Configuration.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Configuration/secrets.template.xcconfig b/Emitron/Emitron/Configuration/secrets.template.xcconfig index 7ec7eed2..8e8d48c0 100644 --- a/Emitron/Emitron/Configuration/secrets.template.xcconfig +++ b/Emitron/Emitron/Configuration/secrets.template.xcconfig @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Constants.swift b/Emitron/Emitron/Constants.swift index cd167605..5be0fd46 100644 --- a/Emitron/Emitron/Constants.swift +++ b/Emitron/Emitron/Constants.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift b/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift index 09e0e8ff..78799c0c 100644 --- a/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift +++ b/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data Synchronisation/SyncAction.swift b/Emitron/Emitron/Data Synchronisation/SyncAction.swift index 9b681203..429df89a 100644 --- a/Emitron/Emitron/Data Synchronisation/SyncAction.swift +++ b/Emitron/Emitron/Data Synchronisation/SyncAction.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data Synchronisation/SyncEngine.swift b/Emitron/Emitron/Data Synchronisation/SyncEngine.swift index e012fff0..4a540266 100644 --- a/Emitron/Emitron/Data Synchronisation/SyncEngine.swift +++ b/Emitron/Emitron/Data Synchronisation/SyncEngine.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data Synchronisation/SyncRequest+ProgressionUpdate.swift b/Emitron/Emitron/Data Synchronisation/SyncRequest+ProgressionUpdate.swift index 33f3513b..58708f74 100644 --- a/Emitron/Emitron/Data Synchronisation/SyncRequest+ProgressionUpdate.swift +++ b/Emitron/Emitron/Data Synchronisation/SyncRequest+ProgressionUpdate.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data Synchronisation/SyncRequest+WatchStat.swift b/Emitron/Emitron/Data Synchronisation/SyncRequest+WatchStat.swift index bea8e939..82258986 100644 --- a/Emitron/Emitron/Data Synchronisation/SyncRequest+WatchStat.swift +++ b/Emitron/Emitron/Data Synchronisation/SyncRequest+WatchStat.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ContentRepositories/BookmarkRepository.swift b/Emitron/Emitron/Data/ContentRepositories/BookmarkRepository.swift index df0ee7b2..6ed96fee 100644 --- a/Emitron/Emitron/Data/ContentRepositories/BookmarkRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/BookmarkRepository.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ContentRepositories/CompletedRepository.swift b/Emitron/Emitron/Data/ContentRepositories/CompletedRepository.swift index 02a4cba6..4041e9c0 100644 --- a/Emitron/Emitron/Data/ContentRepositories/CompletedRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/CompletedRepository.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift b/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift index 8c179a8a..2a9f17ac 100644 --- a/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift b/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift index f464eaf0..fe4c3329 100644 --- a/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ContentRepositories/InProgressRepository.swift b/Emitron/Emitron/Data/ContentRepositories/InProgressRepository.swift index 5e6c5407..901bee40 100644 --- a/Emitron/Emitron/Data/ContentRepositories/InProgressRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/InProgressRepository.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift b/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift index da3fea1a..3aab84ac 100644 --- a/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/DataManager.swift b/Emitron/Emitron/Data/DataManager.swift index 542e3e62..a73d67f4 100644 --- a/Emitron/Emitron/Data/DataManager.swift +++ b/Emitron/Emitron/Data/DataManager.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/DataState.swift b/Emitron/Emitron/Data/DataState.swift index 41cf11a7..cca4ac9d 100644 --- a/Emitron/Emitron/Data/DataState.swift +++ b/Emitron/Emitron/Data/DataState.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/Other Repositories/CategoryRepository.swift b/Emitron/Emitron/Data/Other Repositories/CategoryRepository.swift index 995af5e5..88f54f26 100644 --- a/Emitron/Emitron/Data/Other Repositories/CategoryRepository.swift +++ b/Emitron/Emitron/Data/Other Repositories/CategoryRepository.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/Other Repositories/DomainRepository.swift b/Emitron/Emitron/Data/Other Repositories/DomainRepository.swift index 90da59b5..37da5063 100644 --- a/Emitron/Emitron/Data/Other Repositories/DomainRepository.swift +++ b/Emitron/Emitron/Data/Other Repositories/DomainRepository.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/Repository.swift b/Emitron/Emitron/Data/Repository.swift index 247df956..9956b1d0 100644 --- a/Emitron/Emitron/Data/Repository.swift +++ b/Emitron/Emitron/Data/Repository.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/Service Adapters/BookmarksService+ContentServiceAdapter.swift b/Emitron/Emitron/Data/Service Adapters/BookmarksService+ContentServiceAdapter.swift index f2995802..30783df5 100644 --- a/Emitron/Emitron/Data/Service Adapters/BookmarksService+ContentServiceAdapter.swift +++ b/Emitron/Emitron/Data/Service Adapters/BookmarksService+ContentServiceAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/Service Adapters/ContentServiceAdapter.swift b/Emitron/Emitron/Data/Service Adapters/ContentServiceAdapter.swift index c2fc578e..a04a9055 100644 --- a/Emitron/Emitron/Data/Service Adapters/ContentServiceAdapter.swift +++ b/Emitron/Emitron/Data/Service Adapters/ContentServiceAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/Service Adapters/ContentsService+ContentServiceAdapter.swift b/Emitron/Emitron/Data/Service Adapters/ContentsService+ContentServiceAdapter.swift index 6d3495b6..b81838b9 100644 --- a/Emitron/Emitron/Data/Service Adapters/ContentsService+ContentServiceAdapter.swift +++ b/Emitron/Emitron/Data/Service Adapters/ContentsService+ContentServiceAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/Service Adapters/ProgressionsService+ContentServiceAdapter.swift b/Emitron/Emitron/Data/Service Adapters/ProgressionsService+ContentServiceAdapter.swift index 9f2e8d2a..0ae602b3 100644 --- a/Emitron/Emitron/Data/Service Adapters/ProgressionsService+ContentServiceAdapter.swift +++ b/Emitron/Emitron/Data/Service Adapters/ProgressionsService+ContentServiceAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/States/ChildContentsState.swift b/Emitron/Emitron/Data/States/ChildContentsState.swift index bf5b025e..cb8823ab 100644 --- a/Emitron/Emitron/Data/States/ChildContentsState.swift +++ b/Emitron/Emitron/Data/States/ChildContentsState.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/States/ContentPersistableState.swift b/Emitron/Emitron/Data/States/ContentPersistableState.swift index 11318d79..2c90e248 100644 --- a/Emitron/Emitron/Data/States/ContentPersistableState.swift +++ b/Emitron/Emitron/Data/States/ContentPersistableState.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/States/ContentSummaryState.swift b/Emitron/Emitron/Data/States/ContentSummaryState.swift index 1af996f8..306cedc8 100644 --- a/Emitron/Emitron/Data/States/ContentSummaryState.swift +++ b/Emitron/Emitron/Data/States/ContentSummaryState.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/States/DynamicContentState.swift b/Emitron/Emitron/Data/States/DynamicContentState.swift index 170e1c0a..e39b2fa3 100644 --- a/Emitron/Emitron/Data/States/DynamicContentState.swift +++ b/Emitron/Emitron/Data/States/DynamicContentState.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/States/VideoPlaybackState.swift b/Emitron/Emitron/Data/States/VideoPlaybackState.swift index b4114792..1cab6b63 100644 --- a/Emitron/Emitron/Data/States/VideoPlaybackState.swift +++ b/Emitron/Emitron/Data/States/VideoPlaybackState.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ViewModels/ChildContentsViewModel.swift b/Emitron/Emitron/Data/ViewModels/ChildContentsViewModel.swift index fe348281..5e616410 100644 --- a/Emitron/Emitron/Data/ViewModels/ChildContentsViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/ChildContentsViewModel.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ViewModels/ContentScreen.swift b/Emitron/Emitron/Data/ViewModels/ContentScreen.swift index 7f08a78a..09495795 100644 --- a/Emitron/Emitron/Data/ViewModels/ContentScreen.swift +++ b/Emitron/Emitron/Data/ViewModels/ContentScreen.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift b/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift index a401ea84..0a0750bf 100644 --- a/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ViewModels/DownloadDeletionConfirmation.swift b/Emitron/Emitron/Data/ViewModels/DownloadDeletionConfirmation.swift index 8f1823d9..96f2c9e8 100644 --- a/Emitron/Emitron/Data/ViewModels/DownloadDeletionConfirmation.swift +++ b/Emitron/Emitron/Data/ViewModels/DownloadDeletionConfirmation.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift index b4dbb191..ab312fd1 100644 --- a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ViewModels/PersistenceStoreChildContentsViewModel.swift b/Emitron/Emitron/Data/ViewModels/PersistenceStoreChildContentsViewModel.swift index ce40e91f..d84e492a 100644 --- a/Emitron/Emitron/Data/ViewModels/PersistenceStoreChildContentsViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/PersistenceStoreChildContentsViewModel.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ViewModels/TabViewModel.swift b/Emitron/Emitron/Data/ViewModels/TabViewModel.swift index 29310766..f73a23fe 100644 --- a/Emitron/Emitron/Data/ViewModels/TabViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/TabViewModel.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift index 5d29cdca..e6f0208a 100644 --- a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Displayable/Content+ChildContentListDisplayable.swift b/Emitron/Emitron/Displayable/Content+ChildContentListDisplayable.swift index cc79dccb..7beadce9 100644 --- a/Emitron/Emitron/Displayable/Content+ChildContentListDisplayable.swift +++ b/Emitron/Emitron/Displayable/Content+ChildContentListDisplayable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Displayable/ContentDisplayable.swift b/Emitron/Emitron/Displayable/ContentDisplayable.swift index 57444109..3fabce54 100644 --- a/Emitron/Emitron/Displayable/ContentDisplayable.swift +++ b/Emitron/Emitron/Displayable/ContentDisplayable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift b/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift index b0e1fb72..fb4e3015 100644 --- a/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift +++ b/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Displayable/DynamicContentDisplayable.swift b/Emitron/Emitron/Displayable/DynamicContentDisplayable.swift index c23e182f..4d94141c 100644 --- a/Emitron/Emitron/Displayable/DynamicContentDisplayable.swift +++ b/Emitron/Emitron/Displayable/DynamicContentDisplayable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Displayable/Group+GroupDisplayable.swift b/Emitron/Emitron/Displayable/Group+GroupDisplayable.swift index 27fe32e1..a3821504 100644 --- a/Emitron/Emitron/Displayable/Group+GroupDisplayable.swift +++ b/Emitron/Emitron/Displayable/Group+GroupDisplayable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Downloads/DownloadAction.swift b/Emitron/Emitron/Downloads/DownloadAction.swift index 2e660a79..2b195b57 100644 --- a/Emitron/Emitron/Downloads/DownloadAction.swift +++ b/Emitron/Emitron/Downloads/DownloadAction.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Downloads/DownloadProcessor.swift b/Emitron/Emitron/Downloads/DownloadProcessor.swift index fd9f7695..12c8554a 100644 --- a/Emitron/Emitron/Downloads/DownloadProcessor.swift +++ b/Emitron/Emitron/Downloads/DownloadProcessor.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Downloads/DownloadQueueManager.swift b/Emitron/Emitron/Downloads/DownloadQueueManager.swift index 9f3ae5d9..fa06484c 100644 --- a/Emitron/Emitron/Downloads/DownloadQueueManager.swift +++ b/Emitron/Emitron/Downloads/DownloadQueueManager.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Downloads/DownloadService.swift b/Emitron/Emitron/Downloads/DownloadService.swift index a092db83..ce234ba0 100644 --- a/Emitron/Emitron/Downloads/DownloadService.swift +++ b/Emitron/Emitron/Downloads/DownloadService.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Extensions/Comparable+Clamped.swift b/Emitron/Emitron/Extensions/Comparable+Clamped.swift index 09109df4..689c194a 100644 --- a/Emitron/Emitron/Extensions/Comparable+Clamped.swift +++ b/Emitron/Emitron/Extensions/Comparable+Clamped.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Extensions/ContentDetailModel+Extensions.swift b/Emitron/Emitron/Extensions/ContentDetailModel+Extensions.swift index beb510bb..c36af766 100644 --- a/Emitron/Emitron/Extensions/ContentDetailModel+Extensions.swift +++ b/Emitron/Emitron/Extensions/ContentDetailModel+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Extensions/Date+Extensions.swift b/Emitron/Emitron/Extensions/Date+Extensions.swift index 38b567a7..31202a8e 100644 --- a/Emitron/Emitron/Extensions/Date+Extensions.swift +++ b/Emitron/Emitron/Extensions/Date+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Extensions/DateFormatter+Extensions.swift b/Emitron/Emitron/Extensions/DateFormatter+Extensions.swift index 3fcc1e77..e8f10448 100644 --- a/Emitron/Emitron/Extensions/DateFormatter+Extensions.swift +++ b/Emitron/Emitron/Extensions/DateFormatter+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Extensions/TimeInterval+Extensions.swift b/Emitron/Emitron/Extensions/TimeInterval+Extensions.swift index fd81fe6e..f18194b4 100644 --- a/Emitron/Emitron/Extensions/TimeInterval+Extensions.swift +++ b/Emitron/Emitron/Extensions/TimeInterval+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Extensions/UIApplication+DismissKeyboard.swift b/Emitron/Emitron/Extensions/UIApplication+DismissKeyboard.swift index 67d89588..4644e668 100644 --- a/Emitron/Emitron/Extensions/UIApplication+DismissKeyboard.swift +++ b/Emitron/Emitron/Extensions/UIApplication+DismissKeyboard.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Extensions/UIViewController+Extensions.swift b/Emitron/Emitron/Extensions/UIViewController+Extensions.swift index 8ed59861..e8c4bb5d 100644 --- a/Emitron/Emitron/Extensions/UIViewController+Extensions.swift +++ b/Emitron/Emitron/Extensions/UIViewController+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Extensions/View+Extensions.swift b/Emitron/Emitron/Extensions/View+Extensions.swift index 516d5bab..58abbb96 100644 --- a/Emitron/Emitron/Extensions/View+Extensions.swift +++ b/Emitron/Emitron/Extensions/View+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Filters/Filter.swift b/Emitron/Emitron/Filters/Filter.swift index a9d5af87..9f359ab9 100644 --- a/Emitron/Emitron/Filters/Filter.swift +++ b/Emitron/Emitron/Filters/Filter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Filters/FilterGroup.swift b/Emitron/Emitron/Filters/FilterGroup.swift index 0488bdc6..b5ca6757 100644 --- a/Emitron/Emitron/Filters/FilterGroup.swift +++ b/Emitron/Emitron/Filters/FilterGroup.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Filters/Filters.swift b/Emitron/Emitron/Filters/Filters.swift index c206501c..08dc3ea1 100644 --- a/Emitron/Emitron/Filters/Filters.swift +++ b/Emitron/Emitron/Filters/Filters.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Filters/SortFilter.swift b/Emitron/Emitron/Filters/SortFilter.swift index 4e675f1d..6028ac2e 100644 --- a/Emitron/Emitron/Filters/SortFilter.swift +++ b/Emitron/Emitron/Filters/SortFilter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Guardpost/Guardpost.swift b/Emitron/Emitron/Guardpost/Guardpost.swift index 376a9efb..91fb509a 100644 --- a/Emitron/Emitron/Guardpost/Guardpost.swift +++ b/Emitron/Emitron/Guardpost/Guardpost.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Guardpost/SSO/SingleSignOnRequest.swift b/Emitron/Emitron/Guardpost/SSO/SingleSignOnRequest.swift index 22cba192..09fca851 100644 --- a/Emitron/Emitron/Guardpost/SSO/SingleSignOnRequest.swift +++ b/Emitron/Emitron/Guardpost/SSO/SingleSignOnRequest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Guardpost/SSO/SingleSignOnResponse.swift b/Emitron/Emitron/Guardpost/SSO/SingleSignOnResponse.swift index af7bca8f..016dec5e 100644 --- a/Emitron/Emitron/Guardpost/SSO/SingleSignOnResponse.swift +++ b/Emitron/Emitron/Guardpost/SSO/SingleSignOnResponse.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Guardpost/Utils/Data+Hex.swift b/Emitron/Emitron/Guardpost/Utils/Data+Hex.swift index 4ae487d8..7cb876ea 100644 --- a/Emitron/Emitron/Guardpost/Utils/Data+Hex.swift +++ b/Emitron/Emitron/Guardpost/Utils/Data+Hex.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Guardpost/Utils/GuardpostConstants.swift b/Emitron/Emitron/Guardpost/Utils/GuardpostConstants.swift index 3c0778a5..f246ef00 100644 --- a/Emitron/Emitron/Guardpost/Utils/GuardpostConstants.swift +++ b/Emitron/Emitron/Guardpost/Utils/GuardpostConstants.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Guardpost/Utils/RandomString.swift b/Emitron/Emitron/Guardpost/Utils/RandomString.swift index ebc0d68e..e03c8436 100644 --- a/Emitron/Emitron/Guardpost/Utils/RandomString.swift +++ b/Emitron/Emitron/Guardpost/Utils/RandomString.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Guardpost/Utils/String+Base64.swift b/Emitron/Emitron/Guardpost/Utils/String+Base64.swift index f33eabe2..921e1de1 100644 --- a/Emitron/Emitron/Guardpost/Utils/String+Base64.swift +++ b/Emitron/Emitron/Guardpost/Utils/String+Base64.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Logging/Logger.swift b/Emitron/Emitron/Logging/Logger.swift index 7a451345..ba7525ba 100644 --- a/Emitron/Emitron/Logging/Logger.swift +++ b/Emitron/Emitron/Logging/Logger.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/Attachment.swift b/Emitron/Emitron/Models/Attachment.swift index 74d6d546..843f441e 100644 --- a/Emitron/Emitron/Models/Attachment.swift +++ b/Emitron/Emitron/Models/Attachment.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/Bookmark.swift b/Emitron/Emitron/Models/Bookmark.swift index 6aa39813..dc4aa304 100644 --- a/Emitron/Emitron/Models/Bookmark.swift +++ b/Emitron/Emitron/Models/Bookmark.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/Category.swift b/Emitron/Emitron/Models/Category.swift index d58b1e1b..763dc209 100644 --- a/Emitron/Emitron/Models/Category.swift +++ b/Emitron/Emitron/Models/Category.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/Content.swift b/Emitron/Emitron/Models/Content.swift index bd63958c..77824a2d 100644 --- a/Emitron/Emitron/Models/Content.swift +++ b/Emitron/Emitron/Models/Content.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/ContentCategory.swift b/Emitron/Emitron/Models/ContentCategory.swift index c7ed097e..b7d90b3c 100644 --- a/Emitron/Emitron/Models/ContentCategory.swift +++ b/Emitron/Emitron/Models/ContentCategory.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/ContentDifficulty.swift b/Emitron/Emitron/Models/ContentDifficulty.swift index b4aabff3..73910a63 100644 --- a/Emitron/Emitron/Models/ContentDifficulty.swift +++ b/Emitron/Emitron/Models/ContentDifficulty.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/ContentDomain.swift b/Emitron/Emitron/Models/ContentDomain.swift index 07a566fd..dc12155d 100644 --- a/Emitron/Emitron/Models/ContentDomain.swift +++ b/Emitron/Emitron/Models/ContentDomain.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/ContentSubscriptionPlan.swift b/Emitron/Emitron/Models/ContentSubscriptionPlan.swift index 74d565de..32eae6b5 100644 --- a/Emitron/Emitron/Models/ContentSubscriptionPlan.swift +++ b/Emitron/Emitron/Models/ContentSubscriptionPlan.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/ContentType.swift b/Emitron/Emitron/Models/ContentType.swift index 805c2438..c56d313b 100644 --- a/Emitron/Emitron/Models/ContentType.swift +++ b/Emitron/Emitron/Models/ContentType.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/DataCache.swift b/Emitron/Emitron/Models/DataCache.swift index 3605db49..bff51dd1 100644 --- a/Emitron/Emitron/Models/DataCache.swift +++ b/Emitron/Emitron/Models/DataCache.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/Domain.swift b/Emitron/Emitron/Models/Domain.swift index c0533a04..68e61907 100644 --- a/Emitron/Emitron/Models/Domain.swift +++ b/Emitron/Emitron/Models/Domain.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/Download.swift b/Emitron/Emitron/Models/Download.swift index 749c72d6..ad1c943a 100644 --- a/Emitron/Emitron/Models/Download.swift +++ b/Emitron/Emitron/Models/Download.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/FossLicense.swift b/Emitron/Emitron/Models/FossLicense.swift index 8201ee00..50ccfd82 100644 --- a/Emitron/Emitron/Models/FossLicense.swift +++ b/Emitron/Emitron/Models/FossLicense.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/Group.swift b/Emitron/Emitron/Models/Group.swift index 32ae9713..90e9fc2f 100644 --- a/Emitron/Emitron/Models/Group.swift +++ b/Emitron/Emitron/Models/Group.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/Icon.swift b/Emitron/Emitron/Models/Icon.swift index d7a07308..2bc4df95 100644 --- a/Emitron/Emitron/Models/Icon.swift +++ b/Emitron/Emitron/Models/Icon.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/Permission.swift b/Emitron/Emitron/Models/Permission.swift index b46da519..ae60bc25 100644 --- a/Emitron/Emitron/Models/Permission.swift +++ b/Emitron/Emitron/Models/Permission.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/PlaybackSpeed.swift b/Emitron/Emitron/Models/PlaybackSpeed.swift index b220ddcd..6e1bd6f3 100644 --- a/Emitron/Emitron/Models/PlaybackSpeed.swift +++ b/Emitron/Emitron/Models/PlaybackSpeed.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/Progression.swift b/Emitron/Emitron/Models/Progression.swift index aa4f6d41..7d6f8b1f 100644 --- a/Emitron/Emitron/Models/Progression.swift +++ b/Emitron/Emitron/Models/Progression.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/SettingsOption.swift b/Emitron/Emitron/Models/SettingsOption.swift index 4063777a..ba991477 100644 --- a/Emitron/Emitron/Models/SettingsOption.swift +++ b/Emitron/Emitron/Models/SettingsOption.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/SyncRequest.swift b/Emitron/Emitron/Models/SyncRequest.swift index 9ca9e1d9..f9d69732 100644 --- a/Emitron/Emitron/Models/SyncRequest.swift +++ b/Emitron/Emitron/Models/SyncRequest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Models/User.swift b/Emitron/Emitron/Models/User.swift index fc893fab..d38efa8d 100644 --- a/Emitron/Emitron/Models/User.swift +++ b/Emitron/Emitron/Models/User.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift b/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift index 67428f14..dfeac1c1 100644 --- a/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift +++ b/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapter.swift index 6be8ea3b..509a49be 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/AttachmentAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/AttachmentAdapter.swift index b186d351..10615701 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/AttachmentAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/AttachmentAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/BookmarkAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/BookmarkAdapter.swift index e7bbf03c..012e3bb3 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/BookmarkAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/BookmarkAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/CategoryAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/CategoryAdapter.swift index e0740211..0ed3f462 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/CategoryAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/CategoryAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentAdapter.swift index 45b99853..adb75f9d 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentCategoryAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentCategoryAdapter.swift index 190d6a1b..cbeaa570 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentCategoryAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentCategoryAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentDomainAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentDomainAdapter.swift index e5cd9a82..0df2533d 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentDomainAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentDomainAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/DomainAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/DomainAdapter.swift index cace2e9c..607a6c26 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/DomainAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/DomainAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/GroupAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/GroupAdapter.swift index 5360d2d8..6d6acc85 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/GroupAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/GroupAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/PermissionAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/PermissionAdapter.swift index df5f8837..5607138d 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/PermissionAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/PermissionAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ProgressionAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ProgressionAdapter.swift index 0a224793..428448ae 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ProgressionAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ProgressionAdapter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/JSONAPI/JSONAPIDocument.swift b/Emitron/Emitron/Networking/JSONAPI/JSONAPIDocument.swift index f97629f4..b2221d2f 100644 --- a/Emitron/Emitron/Networking/JSONAPI/JSONAPIDocument.swift +++ b/Emitron/Emitron/Networking/JSONAPI/JSONAPIDocument.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/JSONAPI/JSONAPIError.swift b/Emitron/Emitron/Networking/JSONAPI/JSONAPIError.swift index 21b0b113..4564b3c1 100644 --- a/Emitron/Emitron/Networking/JSONAPI/JSONAPIError.swift +++ b/Emitron/Emitron/Networking/JSONAPI/JSONAPIError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/JSONAPI/JSONAPIErrorSource.swift b/Emitron/Emitron/Networking/JSONAPI/JSONAPIErrorSource.swift index 93fc6879..470ae6a2 100644 --- a/Emitron/Emitron/Networking/JSONAPI/JSONAPIErrorSource.swift +++ b/Emitron/Emitron/Networking/JSONAPI/JSONAPIErrorSource.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/JSONAPI/JSONAPIRelationship.swift b/Emitron/Emitron/Networking/JSONAPI/JSONAPIRelationship.swift index 072acedb..20684f6d 100644 --- a/Emitron/Emitron/Networking/JSONAPI/JSONAPIRelationship.swift +++ b/Emitron/Emitron/Networking/JSONAPI/JSONAPIRelationship.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/JSONAPI/JSONAPIResource.swift b/Emitron/Emitron/Networking/JSONAPI/JSONAPIResource.swift index 4d43f4df..ec6797da 100644 --- a/Emitron/Emitron/Networking/JSONAPI/JSONAPIResource.swift +++ b/Emitron/Emitron/Networking/JSONAPI/JSONAPIResource.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Network/RWAPI.swift b/Emitron/Emitron/Networking/Network/RWAPI.swift index 69964936..20cec277 100644 --- a/Emitron/Emitron/Networking/Network/RWAPI.swift +++ b/Emitron/Emitron/Networking/Network/RWAPI.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Network/RWEnvironment.swift b/Emitron/Emitron/Networking/Network/RWEnvironment.swift index b2fc81ef..7337a105 100644 --- a/Emitron/Emitron/Networking/Network/RWEnvironment.swift +++ b/Emitron/Emitron/Networking/Network/RWEnvironment.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/BookmarkRequest.swift b/Emitron/Emitron/Networking/Requests/BookmarkRequest.swift index f1b05f5b..1e1f98d7 100644 --- a/Emitron/Emitron/Networking/Requests/BookmarkRequest.swift +++ b/Emitron/Emitron/Networking/Requests/BookmarkRequest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/CategoriesRequest.swift b/Emitron/Emitron/Networking/Requests/CategoriesRequest.swift index debe6843..d12d7934 100644 --- a/Emitron/Emitron/Networking/Requests/CategoriesRequest.swift +++ b/Emitron/Emitron/Networking/Requests/CategoriesRequest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/ContentsRequest.swift b/Emitron/Emitron/Networking/Requests/ContentsRequest.swift index 551acc12..706511ac 100644 --- a/Emitron/Emitron/Networking/Requests/ContentsRequest.swift +++ b/Emitron/Emitron/Networking/Requests/ContentsRequest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/DomainsRequest.swift b/Emitron/Emitron/Networking/Requests/DomainsRequest.swift index a94e944f..72fd2bdb 100644 --- a/Emitron/Emitron/Networking/Requests/DomainsRequest.swift +++ b/Emitron/Emitron/Networking/Requests/DomainsRequest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/Models/ContentDifficulty+Request.swift b/Emitron/Emitron/Networking/Requests/Models/ContentDifficulty+Request.swift index 39487312..2ae8a0aa 100644 --- a/Emitron/Emitron/Networking/Requests/Models/ContentDifficulty+Request.swift +++ b/Emitron/Emitron/Networking/Requests/Models/ContentDifficulty+Request.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/Models/ContentSubscriptionPlan+Request.swift b/Emitron/Emitron/Networking/Requests/Models/ContentSubscriptionPlan+Request.swift index c7fd0a53..762873bb 100644 --- a/Emitron/Emitron/Networking/Requests/Models/ContentSubscriptionPlan+Request.swift +++ b/Emitron/Emitron/Networking/Requests/Models/ContentSubscriptionPlan+Request.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/Models/ContentType+Request.swift b/Emitron/Emitron/Networking/Requests/Models/ContentType+Request.swift index e51f867f..0211c21e 100644 --- a/Emitron/Emitron/Networking/Requests/Models/ContentType+Request.swift +++ b/Emitron/Emitron/Networking/Requests/Models/ContentType+Request.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/Parameters.swift b/Emitron/Emitron/Networking/Requests/Parameters.swift index 9021b95f..06dae7e4 100644 --- a/Emitron/Emitron/Networking/Requests/Parameters.swift +++ b/Emitron/Emitron/Networking/Requests/Parameters.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/PermissionsRequest.swift b/Emitron/Emitron/Networking/Requests/PermissionsRequest.swift index b11069b5..47197b25 100644 --- a/Emitron/Emitron/Networking/Requests/PermissionsRequest.swift +++ b/Emitron/Emitron/Networking/Requests/PermissionsRequest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/ProgressionsRequest.swift b/Emitron/Emitron/Networking/Requests/ProgressionsRequest.swift index fe05307a..30b8d786 100644 --- a/Emitron/Emitron/Networking/Requests/ProgressionsRequest.swift +++ b/Emitron/Emitron/Networking/Requests/ProgressionsRequest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/Request.swift b/Emitron/Emitron/Networking/Requests/Request.swift index 350ce208..26788dc4 100644 --- a/Emitron/Emitron/Networking/Requests/Request.swift +++ b/Emitron/Emitron/Networking/Requests/Request.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/VideosRequest.swift b/Emitron/Emitron/Networking/Requests/VideosRequest.swift index 3d7455c5..14567868 100644 --- a/Emitron/Emitron/Networking/Requests/VideosRequest.swift +++ b/Emitron/Emitron/Networking/Requests/VideosRequest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Requests/WatchStatsRequest.swift b/Emitron/Emitron/Networking/Requests/WatchStatsRequest.swift index 585aa935..3885ab56 100644 --- a/Emitron/Emitron/Networking/Requests/WatchStatsRequest.swift +++ b/Emitron/Emitron/Networking/Requests/WatchStatsRequest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Services/BookmarksService.swift b/Emitron/Emitron/Networking/Services/BookmarksService.swift index 40af9fb8..bf651108 100644 --- a/Emitron/Emitron/Networking/Services/BookmarksService.swift +++ b/Emitron/Emitron/Networking/Services/BookmarksService.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Services/CategoriesService.swift b/Emitron/Emitron/Networking/Services/CategoriesService.swift index a876f37f..d16e957a 100644 --- a/Emitron/Emitron/Networking/Services/CategoriesService.swift +++ b/Emitron/Emitron/Networking/Services/CategoriesService.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Services/ContentsService.swift b/Emitron/Emitron/Networking/Services/ContentsService.swift index 5a58a92b..466d259b 100644 --- a/Emitron/Emitron/Networking/Services/ContentsService.swift +++ b/Emitron/Emitron/Networking/Services/ContentsService.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Services/DomainsService.swift b/Emitron/Emitron/Networking/Services/DomainsService.swift index 843a391f..30bea516 100644 --- a/Emitron/Emitron/Networking/Services/DomainsService.swift +++ b/Emitron/Emitron/Networking/Services/DomainsService.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Services/PermissionsService.swift b/Emitron/Emitron/Networking/Services/PermissionsService.swift index 67398475..61f16657 100644 --- a/Emitron/Emitron/Networking/Services/PermissionsService.swift +++ b/Emitron/Emitron/Networking/Services/PermissionsService.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Services/ProgressionsService.swift b/Emitron/Emitron/Networking/Services/ProgressionsService.swift index 2a7a752b..9c98fc35 100644 --- a/Emitron/Emitron/Networking/Services/ProgressionsService.swift +++ b/Emitron/Emitron/Networking/Services/ProgressionsService.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Services/Service.swift b/Emitron/Emitron/Networking/Services/Service.swift index 3c21003a..f8f723e5 100644 --- a/Emitron/Emitron/Networking/Services/Service.swift +++ b/Emitron/Emitron/Networking/Services/Service.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Services/VideosService.swift b/Emitron/Emitron/Networking/Services/VideosService.swift index 37278619..84184d35 100644 --- a/Emitron/Emitron/Networking/Services/VideosService.swift +++ b/Emitron/Emitron/Networking/Services/VideosService.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Networking/Services/WatchStatsService.swift b/Emitron/Emitron/Networking/Services/WatchStatsService.swift index 04107d0b..3ec024e9 100644 --- a/Emitron/Emitron/Networking/Services/WatchStatsService.swift +++ b/Emitron/Emitron/Networking/Services/WatchStatsService.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Ownable/Content+Ownable.swift b/Emitron/Emitron/Ownable/Content+Ownable.swift index dd85e8e3..4e9e33e2 100644 --- a/Emitron/Emitron/Ownable/Content+Ownable.swift +++ b/Emitron/Emitron/Ownable/Content+Ownable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Ownable/Ownable.swift b/Emitron/Emitron/Ownable/Ownable.swift index ac7fc158..322ed8bd 100644 --- a/Emitron/Emitron/Ownable/Ownable.swift +++ b/Emitron/Emitron/Ownable/Ownable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/EmitronDatabase.swift b/Emitron/Emitron/Persistence/EmitronDatabase.swift index f9dc1302..d10cac52 100644 --- a/Emitron/Emitron/Persistence/EmitronDatabase.swift +++ b/Emitron/Emitron/Persistence/EmitronDatabase.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/Models/Bookmark+Persistence.swift b/Emitron/Emitron/Persistence/Models/Bookmark+Persistence.swift index dbb8da1c..f35886ed 100644 --- a/Emitron/Emitron/Persistence/Models/Bookmark+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/Bookmark+Persistence.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/Models/Category+Persistence.swift b/Emitron/Emitron/Persistence/Models/Category+Persistence.swift index 4375b124..f1e1de64 100644 --- a/Emitron/Emitron/Persistence/Models/Category+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/Category+Persistence.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/Models/Content+Persistence.swift b/Emitron/Emitron/Persistence/Models/Content+Persistence.swift index 41680270..d4a4afea 100644 --- a/Emitron/Emitron/Persistence/Models/Content+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/Content+Persistence.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/Models/ContentCategory+Persistence.swift b/Emitron/Emitron/Persistence/Models/ContentCategory+Persistence.swift index 384bb6cb..d780f09b 100644 --- a/Emitron/Emitron/Persistence/Models/ContentCategory+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/ContentCategory+Persistence.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/Models/ContentDomain+Persistence.swift b/Emitron/Emitron/Persistence/Models/ContentDomain+Persistence.swift index 0aa15ea1..7a456bfc 100644 --- a/Emitron/Emitron/Persistence/Models/ContentDomain+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/ContentDomain+Persistence.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/Models/Domain+Persistence.swift b/Emitron/Emitron/Persistence/Models/Domain+Persistence.swift index 9ba615b6..8e2c1eb9 100644 --- a/Emitron/Emitron/Persistence/Models/Domain+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/Domain+Persistence.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/Models/Download+Persistence.swift b/Emitron/Emitron/Persistence/Models/Download+Persistence.swift index 6d9286e2..433b7da5 100644 --- a/Emitron/Emitron/Persistence/Models/Download+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/Download+Persistence.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/Models/Group+Persistence.swift b/Emitron/Emitron/Persistence/Models/Group+Persistence.swift index 951f02bd..9d4d3d2d 100644 --- a/Emitron/Emitron/Persistence/Models/Group+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/Group+Persistence.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/Models/Progression+Persistence.swift b/Emitron/Emitron/Persistence/Models/Progression+Persistence.swift index 92aee795..f52fd6f2 100644 --- a/Emitron/Emitron/Persistence/Models/Progression+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/Progression+Persistence.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/Models/SyncRequest+Persistence.swift b/Emitron/Emitron/Persistence/Models/SyncRequest+Persistence.swift index 4c44d0bb..0a3091fa 100644 --- a/Emitron/Emitron/Persistence/Models/SyncRequest+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/SyncRequest+Persistence.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Categories.swift b/Emitron/Emitron/Persistence/PersistenceStore+Categories.swift index 2ea0c8c8..a43cd538 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Categories.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Categories.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Domains.swift b/Emitron/Emitron/Persistence/PersistenceStore+Domains.swift index 5c40b2d8..e6ecc637 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Domains.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Domains.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift index 7cc2587e..8d0b97bb 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift b/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift index 37f1f01a..88857185 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift b/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift index 0880a130..e575df72 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/PersistenceStore.swift b/Emitron/Emitron/Persistence/PersistenceStore.swift index c95b0136..286321a7 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/States/ChildContentsState+Fetchable.swift b/Emitron/Emitron/Persistence/States/ChildContentsState+Fetchable.swift index 03095c12..22340ca7 100644 --- a/Emitron/Emitron/Persistence/States/ChildContentsState+Fetchable.swift +++ b/Emitron/Emitron/Persistence/States/ChildContentsState+Fetchable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Persistence/States/ContentSummaryState+Fetchable.swift b/Emitron/Emitron/Persistence/States/ContentSummaryState+Fetchable.swift index edc4eed2..bba2e307 100644 --- a/Emitron/Emitron/Persistence/States/ContentSummaryState+Fetchable.swift +++ b/Emitron/Emitron/Persistence/States/ContentSummaryState+Fetchable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Protocols/ContentPaginatable.swift b/Emitron/Emitron/Protocols/ContentPaginatable.swift index fa8d673f..0e2b519c 100644 --- a/Emitron/Emitron/Protocols/ContentPaginatable.swift +++ b/Emitron/Emitron/Protocols/ContentPaginatable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Protocols/Refreshable.swift b/Emitron/Emitron/Protocols/Refreshable.swift index a5f9240a..a4858120 100644 --- a/Emitron/Emitron/Protocols/Refreshable.swift +++ b/Emitron/Emitron/Protocols/Refreshable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Sessions/SessionController+States.swift b/Emitron/Emitron/Sessions/SessionController+States.swift index 18c0349c..33d12a83 100644 --- a/Emitron/Emitron/Sessions/SessionController+States.swift +++ b/Emitron/Emitron/Sessions/SessionController+States.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Sessions/SessionController.swift b/Emitron/Emitron/Sessions/SessionController.swift index 3c63878c..ccfff6a3 100644 --- a/Emitron/Emitron/Sessions/SessionController.swift +++ b/Emitron/Emitron/Sessions/SessionController.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Sessions/User+Backdoor.swift b/Emitron/Emitron/Sessions/User+Backdoor.swift index 81f5d1e4..4c3e3754 100644 --- a/Emitron/Emitron/Sessions/User+Backdoor.swift +++ b/Emitron/Emitron/Sessions/User+Backdoor.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Settings/EmitronSettings.swift b/Emitron/Emitron/Settings/EmitronSettings.swift index da3ca41a..bf9b0db0 100644 --- a/Emitron/Emitron/Settings/EmitronSettings.swift +++ b/Emitron/Emitron/Settings/EmitronSettings.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Settings/IconManager.swift b/Emitron/Emitron/Settings/IconManager.swift index 1ca9fc15..c56083d7 100644 --- a/Emitron/Emitron/Settings/IconManager.swift +++ b/Emitron/Emitron/Settings/IconManager.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Settings/SettingsKey.swift b/Emitron/Emitron/Settings/SettingsKey.swift index 687041aa..54682d6f 100644 --- a/Emitron/Emitron/Settings/SettingsKey.swift +++ b/Emitron/Emitron/Settings/SettingsKey.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Settings/SettingsManager.swift b/Emitron/Emitron/Settings/SettingsManager.swift index eccaedd2..4ad6fb9b 100644 --- a/Emitron/Emitron/Settings/SettingsManager.swift +++ b/Emitron/Emitron/Settings/SettingsManager.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Settings/SettingsSelectable.swift b/Emitron/Emitron/Settings/SettingsSelectable.swift index 53b2ae08..01b66c7f 100644 --- a/Emitron/Emitron/Settings/SettingsSelectable.swift +++ b/Emitron/Emitron/Settings/SettingsSelectable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Styleguide/CGFloat+Dimensions.swift b/Emitron/Emitron/Styleguide/CGFloat+Dimensions.swift index 6c7447a2..62790ec4 100644 --- a/Emitron/Emitron/Styleguide/CGFloat+Dimensions.swift +++ b/Emitron/Emitron/Styleguide/CGFloat+Dimensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Styleguide/Color+Extensions.swift b/Emitron/Emitron/Styleguide/Color+Extensions.swift index 96f15594..dab852a2 100644 --- a/Emitron/Emitron/Styleguide/Color+Extensions.swift +++ b/Emitron/Emitron/Styleguide/Color+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Styleguide/Font+Extensions.swift b/Emitron/Emitron/Styleguide/Font+Extensions.swift index 92bf2a47..c37d543a 100644 --- a/Emitron/Emitron/Styleguide/Font+Extensions.swift +++ b/Emitron/Emitron/Styleguide/Font+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Styleguide/Image+Extensions.swift b/Emitron/Emitron/Styleguide/Image+Extensions.swift index bf88178c..38e39a33 100644 --- a/Emitron/Emitron/Styleguide/Image+Extensions.swift +++ b/Emitron/Emitron/Styleguide/Image+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Styleguide/UIColor+Extensions.swift b/Emitron/Emitron/Styleguide/UIColor+Extensions.swift index 76ccf88c..cdd2fc41 100644 --- a/Emitron/Emitron/Styleguide/UIColor+Extensions.swift +++ b/Emitron/Emitron/Styleguide/UIColor+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Styleguide/UIFont+Extensions.swift b/Emitron/Emitron/Styleguide/UIFont+Extensions.swift index a44a730a..85d4b6b2 100644 --- a/Emitron/Emitron/Styleguide/UIFont+Extensions.swift +++ b/Emitron/Emitron/Styleguide/UIFont+Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/App Root/LoginView.swift b/Emitron/Emitron/UI/App Root/LoginView.swift index 6b2ec082..485ca20b 100644 --- a/Emitron/Emitron/UI/App Root/LoginView.swift +++ b/Emitron/Emitron/UI/App Root/LoginView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/App Root/LogoutView.swift b/Emitron/Emitron/UI/App Root/LogoutView.swift index be3995b3..03d3a6ee 100644 --- a/Emitron/Emitron/UI/App Root/LogoutView.swift +++ b/Emitron/Emitron/UI/App Root/LogoutView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/App Root/MainView.swift b/Emitron/Emitron/UI/App Root/MainView.swift index 2ce3f711..f01b71bd 100644 --- a/Emitron/Emitron/UI/App Root/MainView.swift +++ b/Emitron/Emitron/UI/App Root/MainView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/App Root/MessageBarView.swift b/Emitron/Emitron/UI/App Root/MessageBarView.swift index 87aef6dc..ae48b6b8 100644 --- a/Emitron/Emitron/UI/App Root/MessageBarView.swift +++ b/Emitron/Emitron/UI/App Root/MessageBarView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift b/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift index b6ec889e..962082b1 100644 --- a/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift +++ b/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/App Root/SnackbarView.swift b/Emitron/Emitron/UI/App Root/SnackbarView.swift index 2a3da926..201abb84 100644 --- a/Emitron/Emitron/UI/App Root/SnackbarView.swift +++ b/Emitron/Emitron/UI/App Root/SnackbarView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/App Root/TabNavView.swift b/Emitron/Emitron/UI/App Root/TabNavView.swift index 488034f7..5f7ebda6 100644 --- a/Emitron/Emitron/UI/App Root/TabNavView.swift +++ b/Emitron/Emitron/UI/App Root/TabNavView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Downloads/DownloadDeletionConfirmation+Alert.swift b/Emitron/Emitron/UI/Downloads/DownloadDeletionConfirmation+Alert.swift index efc16e5e..27db4cf6 100644 --- a/Emitron/Emitron/UI/Downloads/DownloadDeletionConfirmation+Alert.swift +++ b/Emitron/Emitron/UI/Downloads/DownloadDeletionConfirmation+Alert.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Downloads/DownloadsView.swift b/Emitron/Emitron/UI/Downloads/DownloadsView.swift index 4785ded4..d6cb0854 100644 --- a/Emitron/Emitron/UI/Downloads/DownloadsView.swift +++ b/Emitron/Emitron/UI/Downloads/DownloadsView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Empty States/ErrorView.swift b/Emitron/Emitron/UI/Empty States/ErrorView.swift index cc0d7e73..26377218 100644 --- a/Emitron/Emitron/UI/Empty States/ErrorView.swift +++ b/Emitron/Emitron/UI/Empty States/ErrorView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Empty States/LoadingView.swift b/Emitron/Emitron/UI/Empty States/LoadingView.swift index f4bb5e1f..d40adf59 100644 --- a/Emitron/Emitron/UI/Empty States/LoadingView.swift +++ b/Emitron/Emitron/UI/Empty States/LoadingView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Empty States/NoResultsView.swift b/Emitron/Emitron/UI/Empty States/NoResultsView.swift index ac9054c2..12b72e8d 100644 --- a/Emitron/Emitron/UI/Empty States/NoResultsView.swift +++ b/Emitron/Emitron/UI/Empty States/NoResultsView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Empty States/OfflineView.swift b/Emitron/Emitron/UI/Empty States/OfflineView.swift index 1ef42186..782bf17e 100644 --- a/Emitron/Emitron/UI/Empty States/OfflineView.swift +++ b/Emitron/Emitron/UI/Empty States/OfflineView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Generic/PagerView.swift b/Emitron/Emitron/UI/Generic/PagerView.swift index 5b80e7e9..c77bc60f 100644 --- a/Emitron/Emitron/UI/Generic/PagerView.swift +++ b/Emitron/Emitron/UI/Generic/PagerView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Generic/PagingIndicatorView.swift b/Emitron/Emitron/UI/Generic/PagingIndicatorView.swift index d43cf8ca..2a4f4b22 100644 --- a/Emitron/Emitron/UI/Generic/PagingIndicatorView.swift +++ b/Emitron/Emitron/UI/Generic/PagingIndicatorView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Library/Filtering/AppliedFilterTagButton.swift b/Emitron/Emitron/UI/Library/Filtering/AppliedFilterTagButton.swift index a4513681..e508fc1b 100644 --- a/Emitron/Emitron/UI/Library/Filtering/AppliedFilterTagButton.swift +++ b/Emitron/Emitron/UI/Library/Filtering/AppliedFilterTagButton.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift b/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift index 661fdc06..fd18a117 100644 --- a/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift index 2948c503..ddfde2fe 100644 --- a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Library/Filtering/TitleCheckmarkView.swift b/Emitron/Emitron/UI/Library/Filtering/TitleCheckmarkView.swift index 9022b5ce..14ee9687 100644 --- a/Emitron/Emitron/UI/Library/Filtering/TitleCheckmarkView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/TitleCheckmarkView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Library/LibraryView.swift b/Emitron/Emitron/UI/Library/LibraryView.swift index a6bd14ef..f14d3e9c 100644 --- a/Emitron/Emitron/UI/Library/LibraryView.swift +++ b/Emitron/Emitron/UI/Library/LibraryView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Library/SearchFieldView.swift b/Emitron/Emitron/UI/Library/SearchFieldView.swift index 50b330b4..87331900 100644 --- a/Emitron/Emitron/UI/Library/SearchFieldView.swift +++ b/Emitron/Emitron/UI/Library/SearchFieldView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift index 6e9d637c..f496cc4b 100644 --- a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift +++ b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift index 236d2e8d..2230da4f 100644 --- a/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift +++ b/Emitron/Emitron/UI/My Tutorials/ToggleControlView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/PortraitHostingController.swift b/Emitron/Emitron/UI/PortraitHostingController.swift index 73bb34a8..bd77299d 100644 --- a/Emitron/Emitron/UI/PortraitHostingController.swift +++ b/Emitron/Emitron/UI/PortraitHostingController.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/SceneDelegate.swift b/Emitron/Emitron/UI/SceneDelegate.swift index 7d77d807..fc8e9503 100644 --- a/Emitron/Emitron/UI/SceneDelegate.swift +++ b/Emitron/Emitron/UI/SceneDelegate.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Settings/Icons/IconChooserView.swift b/Emitron/Emitron/UI/Settings/Icons/IconChooserView.swift index ca7f7c38..a5cb9f04 100644 --- a/Emitron/Emitron/UI/Settings/Icons/IconChooserView.swift +++ b/Emitron/Emitron/UI/Settings/Icons/IconChooserView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Settings/Icons/IconView.swift b/Emitron/Emitron/UI/Settings/Icons/IconView.swift index bcaaf684..8de626d5 100644 --- a/Emitron/Emitron/UI/Settings/Icons/IconView.swift +++ b/Emitron/Emitron/UI/Settings/Icons/IconView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Settings/Licenses/LicenseDetailView.swift b/Emitron/Emitron/UI/Settings/Licenses/LicenseDetailView.swift index 6914bb30..600e1467 100644 --- a/Emitron/Emitron/UI/Settings/Licenses/LicenseDetailView.swift +++ b/Emitron/Emitron/UI/Settings/Licenses/LicenseDetailView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift b/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift index b957c16b..77be907e 100644 --- a/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift +++ b/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Settings/SettingsDisclosureRow.swift b/Emitron/Emitron/UI/Settings/SettingsDisclosureRow.swift index 9e6378b0..c5e2e7e9 100644 --- a/Emitron/Emitron/UI/Settings/SettingsDisclosureRow.swift +++ b/Emitron/Emitron/UI/Settings/SettingsDisclosureRow.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Settings/SettingsList.swift b/Emitron/Emitron/UI/Settings/SettingsList.swift index 003052ef..016d75bf 100644 --- a/Emitron/Emitron/UI/Settings/SettingsList.swift +++ b/Emitron/Emitron/UI/Settings/SettingsList.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift b/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift index 4a099a40..66a5e454 100644 --- a/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Settings/SettingsToggleRow.swift b/Emitron/Emitron/UI/Settings/SettingsToggleRow.swift index 0334b3b4..bfba7cf6 100644 --- a/Emitron/Emitron/UI/Settings/SettingsToggleRow.swift +++ b/Emitron/Emitron/UI/Settings/SettingsToggleRow.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index a6071585..ad307113 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/CheckmarkView.swift b/Emitron/Emitron/UI/Shared/CheckmarkView.swift index bcaf4ca0..470e25eb 100644 --- a/Emitron/Emitron/UI/Shared/CheckmarkView.swift +++ b/Emitron/Emitron/UI/Shared/CheckmarkView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/CompletedIconView.swift b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/CompletedIconView.swift index 471cdbb2..c68bb4dd 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/CompletedIconView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/CompletedIconView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/ContinueButtonView.swift b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/ContinueButtonView.swift index f007a9dd..e051343f 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/ContinueButtonView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/ContinueButtonView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/LockedIconView.swift b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/LockedIconView.swift index 95137348..89b219de 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/LockedIconView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/LockedIconView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/NumberIconView.swift b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/NumberIconView.swift index 37539710..449c7d8a 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/NumberIconView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/NumberIconView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/PlayButtonView.swift b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/PlayButtonView.swift index b4e68f9b..34486f5d 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/PlayButtonView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/PlayButtonView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/VideoOverlayButtonView.swift b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/VideoOverlayButtonView.swift index ebaeb3c3..ba115e97 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/VideoOverlayButtonView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/VideoOverlayButtonView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift index 71b4494b..93d25cea 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index cbb724a7..f937e0ff 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift index ac2864a3..dd276073 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/CourseHeaderView.swift b/Emitron/Emitron/UI/Shared/Content Detail/CourseHeaderView.swift index d67a4add..00381413 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/CourseHeaderView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/CourseHeaderView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift index 58de2236..c977311d 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift index 8074c140..e87eea27 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content Detail/VerticalFadeImageView.swift b/Emitron/Emitron/UI/Shared/Content Detail/VerticalFadeImageView.swift index 918132d8..a082a293 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/VerticalFadeImageView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/VerticalFadeImageView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content List/CardView.swift b/Emitron/Emitron/UI/Shared/Content List/CardView.swift index aae638fb..251d72f6 100644 --- a/Emitron/Emitron/UI/Shared/Content List/CardView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/CardView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift index b356ac94..6a8dd2fa 100644 --- a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Download Icon/ArrowInCircleView.swift b/Emitron/Emitron/UI/Shared/Download Icon/ArrowInCircleView.swift index f45e3568..30757dc1 100644 --- a/Emitron/Emitron/UI/Shared/Download Icon/ArrowInCircleView.swift +++ b/Emitron/Emitron/UI/Shared/Download Icon/ArrowInCircleView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Download Icon/CircleProgressBarView.swift b/Emitron/Emitron/UI/Shared/Download Icon/CircleProgressBarView.swift index d3178f4b..e3e72830 100644 --- a/Emitron/Emitron/UI/Shared/Download Icon/CircleProgressBarView.swift +++ b/Emitron/Emitron/UI/Shared/Download Icon/CircleProgressBarView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Download Icon/DownloadIcon.swift b/Emitron/Emitron/UI/Shared/Download Icon/DownloadIcon.swift index f5c1e285..b6c5de36 100644 --- a/Emitron/Emitron/UI/Shared/Download Icon/DownloadIcon.swift +++ b/Emitron/Emitron/UI/Shared/Download Icon/DownloadIcon.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Download Icon/DownloadWarningView.swift b/Emitron/Emitron/UI/Shared/Download Icon/DownloadWarningView.swift index e7a10e9f..a185d22d 100644 --- a/Emitron/Emitron/UI/Shared/Download Icon/DownloadWarningView.swift +++ b/Emitron/Emitron/UI/Shared/Download Icon/DownloadWarningView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Download Icon/SpinningCircleView.swift b/Emitron/Emitron/UI/Shared/Download Icon/SpinningCircleView.swift index 3bf72d62..8fb5b986 100644 --- a/Emitron/Emitron/UI/Shared/Download Icon/SpinningCircleView.swift +++ b/Emitron/Emitron/UI/Shared/Download Icon/SpinningCircleView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/MainButtonView.swift b/Emitron/Emitron/UI/Shared/MainButtonView.swift index 1f32d4fe..11051441 100644 --- a/Emitron/Emitron/UI/Shared/MainButtonView.swift +++ b/Emitron/Emitron/UI/Shared/MainButtonView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/ProgressBarView.swift b/Emitron/Emitron/UI/Shared/ProgressBarView.swift index 1bb74b47..8f34d758 100644 --- a/Emitron/Emitron/UI/Shared/ProgressBarView.swift +++ b/Emitron/Emitron/UI/Shared/ProgressBarView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Tags/CompletedTag.swift b/Emitron/Emitron/UI/Shared/Tags/CompletedTag.swift index 35e61e2a..aa3dbd8a 100644 --- a/Emitron/Emitron/UI/Shared/Tags/CompletedTag.swift +++ b/Emitron/Emitron/UI/Shared/Tags/CompletedTag.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Tags/ProTag.swift b/Emitron/Emitron/UI/Shared/Tags/ProTag.swift index 6929d601..1e6aa3bc 100644 --- a/Emitron/Emitron/UI/Shared/Tags/ProTag.swift +++ b/Emitron/Emitron/UI/Shared/Tags/ProTag.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Shared/Tags/TagView.swift b/Emitron/Emitron/UI/Shared/Tags/TagView.swift index 7dcd8b46..bf4ea3b1 100644 --- a/Emitron/Emitron/UI/Shared/Tags/TagView.swift +++ b/Emitron/Emitron/UI/Shared/Tags/TagView.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Video/FullScreenVideoPlayerRepresentable.swift b/Emitron/Emitron/UI/Video/FullScreenVideoPlayerRepresentable.swift index b96c45ac..6fe47038 100644 --- a/Emitron/Emitron/UI/Video/FullScreenVideoPlayerRepresentable.swift +++ b/Emitron/Emitron/UI/Video/FullScreenVideoPlayerRepresentable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift b/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift index a1c065be..db0fc06d 100644 --- a/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift +++ b/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/Emitron/Utilities/MessageBus.swift b/Emitron/Emitron/Utilities/MessageBus.swift index d64c6de6..db49e2f0 100644 --- a/Emitron/Emitron/Utilities/MessageBus.swift +++ b/Emitron/Emitron/Utilities/MessageBus.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronScreenshots/EmitronScreenshots.swift b/Emitron/emitronScreenshots/EmitronScreenshots.swift index daadb6a1..b95f2040 100644 --- a/Emitron/emitronScreenshots/EmitronScreenshots.swift +++ b/Emitron/emitronScreenshots/EmitronScreenshots.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Combine/PublishedPrePostFactoTest.swift b/Emitron/emitronTests/Combine/PublishedPrePostFactoTest.swift index f9dbe798..5bfea05b 100644 --- a/Emitron/emitronTests/Combine/PublishedPrePostFactoTest.swift +++ b/Emitron/emitronTests/Combine/PublishedPrePostFactoTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Data Synchronisation/SyncEngineTest.swift b/Emitron/emitronTests/Data Synchronisation/SyncEngineTest.swift index efcf540c..e8c9be34 100644 --- a/Emitron/emitronTests/Data Synchronisation/SyncEngineTest.swift +++ b/Emitron/emitronTests/Data Synchronisation/SyncEngineTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Data/DataCacheTest.swift b/Emitron/emitronTests/Data/DataCacheTest.swift index ab4d47da..69cade16 100644 --- a/Emitron/emitronTests/Data/DataCacheTest.swift +++ b/Emitron/emitronTests/Data/DataCacheTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Data/States/ContentPersistableState+Mocks.swift b/Emitron/emitronTests/Data/States/ContentPersistableState+Mocks.swift index f42b07da..ab6a0cf4 100644 --- a/Emitron/emitronTests/Data/States/ContentPersistableState+Mocks.swift +++ b/Emitron/emitronTests/Data/States/ContentPersistableState+Mocks.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift b/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift index 06a3e499..0b34743a 100644 --- a/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift b/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift index d2956a48..a56ea69f 100644 --- a/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift index 56a00365..25cd457d 100644 --- a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Filters/FilterGroupTest.swift b/Emitron/emitronTests/Filters/FilterGroupTest.swift index 215430bf..1530e75d 100644 --- a/Emitron/emitronTests/Filters/FilterGroupTest.swift +++ b/Emitron/emitronTests/Filters/FilterGroupTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Model Controllers/Mock/UserMCMock.swift b/Emitron/emitronTests/Model Controllers/Mock/UserMCMock.swift index 08a2f4ab..2b599f81 100644 --- a/Emitron/emitronTests/Model Controllers/Mock/UserMCMock.swift +++ b/Emitron/emitronTests/Model Controllers/Mock/UserMCMock.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Models/AttachmentTest+Mocks.swift b/Emitron/emitronTests/Models/AttachmentTest+Mocks.swift index e2fd2eef..b1004f80 100644 --- a/Emitron/emitronTests/Models/AttachmentTest+Mocks.swift +++ b/Emitron/emitronTests/Models/AttachmentTest+Mocks.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Models/AttachmentTest.swift b/Emitron/emitronTests/Models/AttachmentTest.swift index a004401d..b9a5d442 100644 --- a/Emitron/emitronTests/Models/AttachmentTest.swift +++ b/Emitron/emitronTests/Models/AttachmentTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Models/ContentTest+Mocks.swift b/Emitron/emitronTests/Models/ContentTest+Mocks.swift index 68a54d78..f122148d 100644 --- a/Emitron/emitronTests/Models/ContentTest+Mocks.swift +++ b/Emitron/emitronTests/Models/ContentTest+Mocks.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Models/Mocks/Category+Mocks.swift b/Emitron/emitronTests/Models/Mocks/Category+Mocks.swift index 78d115a3..b3235d94 100644 --- a/Emitron/emitronTests/Models/Mocks/Category+Mocks.swift +++ b/Emitron/emitronTests/Models/Mocks/Category+Mocks.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Models/Mocks/Domain+Mocks.swift b/Emitron/emitronTests/Models/Mocks/Domain+Mocks.swift index 4da1b723..8e12b0c5 100644 --- a/Emitron/emitronTests/Models/Mocks/Domain+Mocks.swift +++ b/Emitron/emitronTests/Models/Mocks/Domain+Mocks.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Models/Mocks/Permissions+Mocks.swift b/Emitron/emitronTests/Models/Mocks/Permissions+Mocks.swift index 66823112..c46070d1 100644 --- a/Emitron/emitronTests/Models/Mocks/Permissions+Mocks.swift +++ b/Emitron/emitronTests/Models/Mocks/Permissions+Mocks.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Models/Mocks/User+Mocks.swift b/Emitron/emitronTests/Models/Mocks/User+Mocks.swift index 0274f827..4bbec291 100644 --- a/Emitron/emitronTests/Models/Mocks/User+Mocks.swift +++ b/Emitron/emitronTests/Models/Mocks/User+Mocks.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Models/UserTest.swift b/Emitron/emitronTests/Models/UserTest.swift index 22b3f73d..3ae5e836 100644 --- a/Emitron/emitronTests/Models/UserTest.swift +++ b/Emitron/emitronTests/Models/UserTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/AttachmentAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/AttachmentAdapterTest.swift index 1af76b09..7bdf5414 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/AttachmentAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/AttachmentAdapterTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/BookmarkAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/BookmarkAdapterTest.swift index d177b06a..cc30e835 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/BookmarkAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/BookmarkAdapterTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/CategoryAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/CategoryAdapterTest.swift index c3e759d8..6e73bc42 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/CategoryAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/CategoryAdapterTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentAdapterTest.swift index c5b76e4b..d67a3f4c 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentAdapterTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentCategoryAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentCategoryAdapterTest.swift index 30e61696..c1b2dc17 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentCategoryAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentCategoryAdapterTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentDomainAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentDomainAdapterTest.swift index 8ec120ce..24b46f60 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentDomainAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentDomainAdapterTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/DomainAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/DomainAdapterTest.swift index b2a5a47c..6555c90a 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/DomainAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/DomainAdapterTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/GroupAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/GroupAdapterTest.swift index 29c7d79b..e7e3882a 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/GroupAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/GroupAdapterTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/PermissionAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/PermissionAdapterTest.swift index 3965dd2e..347e9a43 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/PermissionAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/PermissionAdapterTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ProgressionAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ProgressionAdapterTest.swift index 405e2f74..41f801b2 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ProgressionAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ProgressionAdapterTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Persistence/EmitronDatabaseTest.swift b/Emitron/emitronTests/Persistence/EmitronDatabaseTest.swift index e4494ba0..a8be3a87 100644 --- a/Emitron/emitronTests/Persistence/EmitronDatabaseTest.swift +++ b/Emitron/emitronTests/Persistence/EmitronDatabaseTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Persistence/Models/ContentTest.swift b/Emitron/emitronTests/Persistence/Models/ContentTest.swift index 8010c60d..252d7716 100644 --- a/Emitron/emitronTests/Persistence/Models/ContentTest.swift +++ b/Emitron/emitronTests/Persistence/Models/ContentTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Persistence/Models/DownloadTest.swift b/Emitron/emitronTests/Persistence/Models/DownloadTest.swift index d3cf0507..8cbefa17 100644 --- a/Emitron/emitronTests/Persistence/Models/DownloadTest.swift +++ b/Emitron/emitronTests/Persistence/Models/DownloadTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift b/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift index 3fa69e2e..9e7fe084 100644 --- a/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift +++ b/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift b/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift index 4fb7d24f..300c2dbf 100644 --- a/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift +++ b/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Persistence/PersistenceStore+SynchronisationTest.swift b/Emitron/emitronTests/Persistence/PersistenceStore+SynchronisationTest.swift index 93e747f2..91a353b2 100644 --- a/Emitron/emitronTests/Persistence/PersistenceStore+SynchronisationTest.swift +++ b/Emitron/emitronTests/Persistence/PersistenceStore+SynchronisationTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Persistence/PersistenceStore+UserKeychainTest.swift b/Emitron/emitronTests/Persistence/PersistenceStore+UserKeychainTest.swift index d350673e..486f0dd4 100644 --- a/Emitron/emitronTests/Persistence/PersistenceStore+UserKeychainTest.swift +++ b/Emitron/emitronTests/Persistence/PersistenceStore+UserKeychainTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Protocols/RefreshableTestCase.swift b/Emitron/emitronTests/Protocols/RefreshableTestCase.swift index 76e3d8af..f1673226 100644 --- a/Emitron/emitronTests/Protocols/RefreshableTestCase.swift +++ b/Emitron/emitronTests/Protocols/RefreshableTestCase.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Services/Mock/VideosServiceMock.swift b/Emitron/emitronTests/Services/Mock/VideosServiceMock.swift index f5631953..ae8f9828 100644 --- a/Emitron/emitronTests/Services/Mock/VideosServiceMock.swift +++ b/Emitron/emitronTests/Services/Mock/VideosServiceMock.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Emitron/emitronTests/Settings/SettingsManagerTest.swift b/Emitron/emitronTests/Settings/SettingsManagerTest.swift index ce59a4af..b3933964 100644 --- a/Emitron/emitronTests/Settings/SettingsManagerTest.swift +++ b/Emitron/emitronTests/Settings/SettingsManagerTest.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Razeware LLC +// Copyright (c) 2022 Razeware LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal From 3adb356f213137f013da0ebf07c9e6db93898112 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Mon, 31 Jan 2022 01:25:21 -0500 Subject: [PATCH 38/69] Various cleanups --- Emitron/Emitron/Guardpost/Guardpost.swift | 50 ++++++++++--------- Emitron/Emitron/Models/SettingsOption.swift | 8 ++- .../Networking/Adapters/DataCacheUpdate.swift | 6 +-- .../EntityAdapters/AttachmentAdapter.swift | 15 +++--- .../Networking/JSONAPI/JSONAPIResource.swift | 21 ++++---- .../PersistenceStore+Keychain.swift | 19 +++---- Emitron/Emitron/Sessions/User+Backdoor.swift | 22 ++++---- Emitron/Emitron/UI/App Root/LogoutView.swift | 13 ++--- .../UI/App Root/PermissionsLoadingView.swift | 17 ++++--- .../UI/Settings/SettingsDisclosureRow.swift | 2 +- .../Content Detail/TextListItemView.swift | 5 +- .../Downloads/DownloadProcessorTest.swift | 3 +- .../PersistenceStore+DownloadsTest.swift | 13 +++-- .../PersistenceStore+UserKeychainTest.swift | 1 - 14 files changed, 102 insertions(+), 93 deletions(-) diff --git a/Emitron/Emitron/Guardpost/Guardpost.swift b/Emitron/Emitron/Guardpost/Guardpost.swift index 91fb509a..5dd5fb4c 100644 --- a/Emitron/Emitron/Guardpost/Guardpost.swift +++ b/Emitron/Emitron/Guardpost/Guardpost.swift @@ -29,30 +29,6 @@ import AuthenticationServices import Combine -public enum LoginError: Error { - case unableToCreateLoginURL - case errorResponseFromGuardpost(Error?) - case unableToDecodeGuardpostResponse - case invalidSignature - case unableToCreateValidUser - - public var localizedDescription: String { - let prefix = "GuardpostLoginError::" - switch self { - case .unableToCreateLoginURL: - return "\(prefix)UnableToCreateLoginURL" - case .errorResponseFromGuardpost(let error): - return "\(prefix)GuardpostLoginError:: [Error: \(error?.localizedDescription ?? "UNKNOWN")]" - case .unableToDecodeGuardpostResponse: - return "\(prefix)UnableToDecodeGuardpostResponse" - case .invalidSignature: - return "\(prefix)InvalidSignature" - case .unableToCreateValidUser: - return "\(prefix)UnableToCreateValidUser" - } - } -} - public class Guardpost: ObservableObject { // MARK: - Properties private let baseURL: String @@ -160,3 +136,29 @@ public class Guardpost: ObservableObject { } } } + +public extension Guardpost { + enum LoginError: Error { + case unableToCreateLoginURL + case errorResponseFromGuardpost(Error?) + case unableToDecodeGuardpostResponse + case invalidSignature + case unableToCreateValidUser + + public var localizedDescription: String { + let prefix = "GuardpostLoginError::" + switch self { + case .unableToCreateLoginURL: + return "\(prefix)UnableToCreateLoginURL" + case .errorResponseFromGuardpost(let error): + return "\(prefix)[Error: \(error?.localizedDescription ?? "UNKNOWN")]" + case .unableToDecodeGuardpostResponse: + return "\(prefix)UnableToDecodeGuardpostResponse" + case .invalidSignature: + return "\(prefix)InvalidSignature" + case .unableToCreateValidUser: + return "\(prefix)UnableToCreateValidUser" + } + } + } +} diff --git a/Emitron/Emitron/Models/SettingsOption.swift b/Emitron/Emitron/Models/SettingsOption.swift index ba991477..eb97536f 100644 --- a/Emitron/Emitron/Models/SettingsOption.swift +++ b/Emitron/Emitron/Models/SettingsOption.swift @@ -62,10 +62,8 @@ enum SettingsOption: Int, Identifiable, CaseIterable { // MARK: - Option Selection extension SettingsOption { static func getOptions(for canDownload: Bool) -> [SettingsOption] { - if canDownload { - return SettingsOption.allCases - } else { - return [.playbackSpeed, .closedCaptionOn] - } + canDownload + ? SettingsOption.allCases + : [.playbackSpeed, .closedCaptionOn] } } diff --git a/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift b/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift index dfeac1c1..c4e3f1a6 100644 --- a/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift +++ b/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift @@ -44,7 +44,7 @@ struct DataCacheUpdate { static func loadFrom(document: JSONAPIDocument) throws -> DataCacheUpdate { let data = try DataCacheUpdate(resources: document.data) - let included = try DataCacheUpdate(resources: document.included, relationships: document.data.map { (entity: $0.entityId, $0.relationships) }) + let included = try DataCacheUpdate(resources: document.included, relationships: document.data.map { (entity: $0.entityID, $0.relationships) }) return data.merged(with: included) } @@ -120,7 +120,7 @@ struct DataCacheUpdate { return entityRelationships(from: entityRelationship.jsonRelationships, fromEntity: entityId) } relationshipsToReturn += resources.flatMap { resource -> [EntityRelationship] in - guard let resourceEntityId = resource.entityId else { return [] } + guard let resourceEntityId = resource.entityID else { return [] } return entityRelationships(from: resource.relationships, fromEntity: resourceEntityId) } return relationshipsToReturn @@ -129,7 +129,7 @@ struct DataCacheUpdate { private static func entityRelationships(from jsonRelationships: [JSONAPIRelationship], fromEntity: EntityIdentity) -> [EntityRelationship] { jsonRelationships.flatMap { relationship in relationship.data.compactMap { resource in - guard let toEntity = resource.entityId else { return nil } + guard let toEntity = resource.entityID else { return nil } return EntityRelationship(name: relationship.type, from: fromEntity, to: toEntity) diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/AttachmentAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/AttachmentAdapter.swift index 10615701..de91d5f5 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/AttachmentAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/AttachmentAdapter.swift @@ -32,16 +32,17 @@ struct AttachmentAdapter: EntityAdapter { static func process(resource: JSONAPIResource, relationships: [EntityRelationship] = []) throws -> Attachment { guard resource.entityType == .attachment else { throw EntityAdapterError.invalidResourceTypeForAdapter } - guard let urlString = resource.attributes["url"] as? String, - let url = URL(string: urlString), - let kindString = resource.attributes["kind"] as? String, - let kind = Attachment.Kind(from: kindString) + guard + let url = (resource.attributes["url"] as? String).flatMap(URL.init), + let kind = (resource.attributes["kind"] as? String).flatMap(Attachment.Kind.init) else { throw EntityAdapterError.invalidOrMissingAttributes } - return Attachment(id: resource.id, - kind: kind, - url: url) + return Attachment( + id: resource.id, + kind: kind, + url: url + ) } } diff --git a/Emitron/Emitron/Networking/JSONAPI/JSONAPIResource.swift b/Emitron/Emitron/Networking/JSONAPI/JSONAPIResource.swift index ec6797da..bb05ef3d 100644 --- a/Emitron/Emitron/Networking/JSONAPI/JSONAPIResource.swift +++ b/Emitron/Emitron/Networking/JSONAPI/JSONAPIResource.swift @@ -43,20 +43,21 @@ public class JSONAPIResource { EntityType(from: type) } - var entityId: EntityIdentity? { + var entityID: EntityIdentity? { guard let entityType = entityType else { return nil } return EntityIdentity(id: id, type: entityType) } - public subscript(key: String) -> Any? { + subscript(key: String) -> Any? { attributes[key] } // MARK: - Initializers - convenience init(_ json: JSON, - parent: JSONAPIDocument?) { - + convenience init( + _ json: JSON, + parent: JSONAPIDocument? + ) { self.init() if let doc = parent { @@ -65,12 +66,14 @@ public class JSONAPIResource { id = json["id"].intValue type = json["type"].stringValue - + for relationship in json["relationships"].dictionaryValue { relationships.append( - JSONAPIRelationship(relationship.value, - type: relationship.key, - parent: nil) + JSONAPIRelationship( + relationship.value, + type: relationship.key, + parent: nil + ) ) } diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift b/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift index 88857185..16a0becd 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift @@ -32,27 +32,25 @@ import KeychainSwift // MARK: Keychain // User + Auth Token (refresh daily) -private let SSOUserKey = "com.razeware.emitron.sso_user" +private let ssoUserKey = "com.razeware.emitron.sso_user" extension PersistenceStore { - @discardableResult - func persistUserToKeychain(user: User, encoder: JSONEncoder = JSONEncoder()) -> Bool { + func persistUserToKeychain(user: User, encoder: JSONEncoder = .init()) -> Bool { guard let encoded = try? encoder.encode(user) else { return false } - let keychain = KeychainSwift() - return keychain.set(encoded, - forKey: SSOUserKey, + return KeychainSwift().set(encoded, + forKey: ssoUserKey, withAccess: .accessibleAfterFirstUnlock) } - func userFromKeychain(_ decoder: JSONDecoder = JSONDecoder()) -> User? { - let keychain = KeychainSwift() - guard let encoded = keychain.getData(SSOUserKey) else { + func userFromKeychain(_ decoder: JSONDecoder = .init()) -> User? { + guard let encoded = KeychainSwift().getData(ssoUserKey) else { return nil } + do { return try decoder.decode(User.self, from: encoded) } catch { @@ -65,7 +63,6 @@ extension PersistenceStore { @discardableResult func removeUserFromKeychain() -> Bool { - let keychain = KeychainSwift() - return keychain.delete(SSOUserKey) + KeychainSwift().delete(ssoUserKey) } } diff --git a/Emitron/Emitron/Sessions/User+Backdoor.swift b/Emitron/Emitron/Sessions/User+Backdoor.swift index 4c3e3754..35ae1444 100644 --- a/Emitron/Emitron/Sessions/User+Backdoor.swift +++ b/Emitron/Emitron/Sessions/User+Backdoor.swift @@ -31,20 +31,20 @@ import class Foundation.UserDefaults extension User { static var backdoor: User? { guard let backdoorToken = UserDefaults.standard.string(forKey: "userBackdoorToken") else { return nil } - - let userDict = [ - "external_id": "BACKDOOR_USER", - "email": "emitron@razeware.com", - "username": "backdoor", - "avatar_url": "https://example.com/", - "name": "BACKDOOR+USER", - "token": backdoorToken - ] - + // Reset all the settings when logging in as a backdoor user. // I'd like to move this to the test target, but Xcode won't compile (missing module CSQLite). SettingsKey.allCases.forEach(UserDefaults.standard.removeObject) - return User(dictionary: userDict) + return User( + dictionary: [ + "external_id": "BACKDOOR_USER", + "email": "emitron@razeware.com", + "username": "backdoor", + "avatar_url": "https://example.com/", + "name": "BACKDOOR+USER", + "token": backdoorToken + ] + ) } } diff --git a/Emitron/Emitron/UI/App Root/LogoutView.swift b/Emitron/Emitron/UI/App Root/LogoutView.swift index 03d3a6ee..f7f5c51d 100644 --- a/Emitron/Emitron/UI/App Root/LogoutView.swift +++ b/Emitron/Emitron/UI/App Root/LogoutView.swift @@ -33,7 +33,6 @@ struct LogoutView: View { var body: some View { VStack { - Image("logo") .padding([.top], 88) @@ -54,14 +53,16 @@ struct LogoutView: View { Spacer() - MainButtonView(title: "Sign Out", type: .destructive(withArrow: true)) { - sessionController.logout() - } + MainButtonView( + title: "Sign Out", + type: .destructive(withArrow: true), + callback: sessionController.logout + ) .padding([.leading, .trailing], 18) - .padding([.bottom], 38) + .padding(.bottom, 38) } .background(Color.background) - .edgesIgnoringSafeArea([.all]) + .edgesIgnoringSafeArea(.all) } } diff --git a/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift b/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift index 962082b1..c9290aad 100644 --- a/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift +++ b/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift @@ -37,13 +37,16 @@ struct PermissionsLoadingView: View { .onTapGesture(count: 5) { showLogoutAlert.toggle() } - .alert(isPresented: $showLogoutAlert) { - Alert( - title: Text("Force Logout?"), - primaryButton: .destructive(Text("Logout"), action: sessionController.logout), - secondaryButton: .cancel() - ) - } + .alert(isPresented: $showLogoutAlert) { + Alert( + title: Text("Force Logout?"), + primaryButton: .destructive( + Text("Logout"), + action: sessionController.logout + ), + secondaryButton: .cancel() + ) + } } } diff --git a/Emitron/Emitron/UI/Settings/SettingsDisclosureRow.swift b/Emitron/Emitron/UI/Settings/SettingsDisclosureRow.swift index c5e2e7e9..1b82d7d0 100644 --- a/Emitron/Emitron/UI/Settings/SettingsDisclosureRow.swift +++ b/Emitron/Emitron/UI/Settings/SettingsDisclosureRow.swift @@ -38,7 +38,7 @@ struct SettingsDisclosureRow: View { Text(title) .font(.uiBodyAppleDefault) .foregroundColor(.titleText) - .padding([.vertical], SettingsLayout.rowSpacing) + .padding(.vertical, SettingsLayout.rowSpacing) Spacer() diff --git a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift index e87eea27..edfe1606 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift @@ -46,10 +46,11 @@ struct TextListItemView: View { var content: ChildContentListDisplayable var canStreamPro: Bool { - sessionController.user?.canStreamPro ?? false + sessionController.user?.canStreamPro == true } + var canDownload: Bool { - sessionController.user?.canDownload ?? false + sessionController.user?.canDownload == true } var body: some View { diff --git a/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift b/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift index 0b34743a..544b557e 100644 --- a/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadProcessorTest.swift @@ -30,8 +30,7 @@ import XCTest import CoreData @testable import Emitron -class DownloadProcessorTest: XCTestCase { - +final class DownloadProcessorTest: XCTestCase { private var downloadProcessor: DownloadProcessor! override func setUp() { diff --git a/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift b/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift index 300c2dbf..1d0cc87b 100644 --- a/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift +++ b/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift @@ -329,8 +329,10 @@ class PersistenceStore_DownloadsTest: XCTestCase { PersistenceStore.CollectionDownloadSummary( totalChildren: episodes.count, childrenRequested: episodes.count, - childrenCompleted: episodes.count), - collectionDownloadSummary) + childrenCompleted: episodes.count + ), + collectionDownloadSummary + ) } func testCollectionDownloadSummaryThrowsForNonCollection() throws { @@ -440,7 +442,7 @@ class PersistenceStore_DownloadsTest: XCTestCase { let recorder = persistenceStore.downloads(in: .inProgress).record() let downloads = getAllDownloads().sorted { $0.requestedAt < $1.requestedAt } - let episodes = getAllContents().filter({ $0.contentType == .episode }) + let episodes = getAllContents().filter { $0.contentType == .episode } try downloads.forEach { download in try persistenceStore.transitionDownload(withId: download.id, to: .inProgress) } @@ -453,7 +455,10 @@ class PersistenceStore_DownloadsTest: XCTestCase { let inProgressQueue = try wait(for: recorder.next(episodes.count + 1), timeout: 10) XCTAssertEqual(0, inProgressQueue.filter { $0?.content.contentType == .collection }.count) - XCTAssertEqual(episodes.map(\.id).sorted(), inProgressQueue.compactMap { $0?.content.id }.sorted()) + XCTAssertEqual( + episodes.map(\.id).sorted(), + inProgressQueue.compactMap { $0?.content.id }.sorted() + ) } func testDownloadQueueDoesNotContainCollections() throws { diff --git a/Emitron/emitronTests/Persistence/PersistenceStore+UserKeychainTest.swift b/Emitron/emitronTests/Persistence/PersistenceStore+UserKeychainTest.swift index 486f0dd4..a4abdc34 100644 --- a/Emitron/emitronTests/Persistence/PersistenceStore+UserKeychainTest.swift +++ b/Emitron/emitronTests/Persistence/PersistenceStore+UserKeychainTest.swift @@ -30,7 +30,6 @@ import XCTest @testable import Emitron class PersistenceStore_UserKeychainTest: XCTestCase { - var persistenceStore: PersistenceStore! private let userDictionary = [ From 8ff475b0c7f69595b9da44b49201e9c2ed8ff5e6 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Mon, 31 Jan 2022 02:10:09 -0500 Subject: [PATCH 39/69] Change `Id`s to `ID`s Swift convention. https://github.com/raywenderlich/swift-style-guide/issues/101#issuecomment-393476464 --- .../Data Synchronisation/ProgressEngine.swift | 22 +-- .../Data Synchronisation/SyncAction.swift | 12 +- .../Data Synchronisation/SyncEngine.swift | 62 ++++----- .../ContentRepository.swift | 22 +-- .../DownloadRepository.swift | 4 +- Emitron/Emitron/Data/Repository.swift | 60 ++++---- ...okmarksService+ContentServiceAdapter.swift | 2 +- .../ContentServiceAdapter.swift | 2 +- ...ontentsService+ContentServiceAdapter.swift | 2 +- ...essionsService+ContentServiceAdapter.swift | 2 +- .../Data/States/ContentPersistableState.swift | 2 +- .../ViewModels/ChildContentsViewModel.swift | 16 +-- .../DataCacheChildContentsViewModel.swift | 6 +- .../DownloadDeletionConfirmation.swift | 2 +- .../ViewModels/DynamicContentViewModel.swift | 30 ++-- ...rsistenceStoreChildContentsViewModel.swift | 2 +- .../ViewModels/VideoPlaybackViewModel.swift | 18 +-- .../Displayable/ContentDisplayable.swift | 2 +- ...tSummaryState+ContentListDisplayable.swift | 4 +- .../Emitron/Downloads/DownloadAction.swift | 6 +- .../Emitron/Downloads/DownloadProcessor.swift | 58 ++++---- .../Emitron/Downloads/DownloadService.swift | 82 +++++------ Emitron/Emitron/Models/Bookmark.swift | 4 +- Emitron/Emitron/Models/Content.swift | 6 +- Emitron/Emitron/Models/ContentCategory.swift | 4 +- Emitron/Emitron/Models/ContentDomain.swift | 4 +- Emitron/Emitron/Models/DataCache.swift | 128 +++++++++--------- Emitron/Emitron/Models/Download.swift | 6 +- Emitron/Emitron/Models/Group.swift | 2 +- Emitron/Emitron/Models/Progression.swift | 6 +- Emitron/Emitron/Models/SyncRequest.swift | 4 +- Emitron/Emitron/Models/User.swift | 8 +- .../Networking/Adapters/DataCacheUpdate.swift | 24 ++-- .../EntityAdapters/BookmarkAdapter.swift | 4 +- .../EntityAdapters/ContentAdapter.swift | 4 +- .../ContentCategoryAdapter.swift | 4 +- .../EntityAdapters/ContentDomainAdapter.swift | 4 +- .../EntityAdapters/GroupAdapter.swift | 4 +- .../EntityAdapters/ProgressionAdapter.swift | 4 +- .../Networking/Requests/Parameters.swift | 10 +- .../Requests/ProgressionsRequest.swift | 4 +- .../Requests/WatchStatsRequest.swift | 4 +- .../Models/Content+Persistence.swift | 2 +- .../Models/Download+Persistence.swift | 2 +- .../Models/Group+Persistence.swift | 2 +- .../Models/SyncRequest+Persistence.swift | 4 +- .../PersistenceStore+Categories.swift | 4 +- .../PersistenceStore+Domains.swift | 4 +- .../PersistenceStore+Downloads.swift | 32 ++--- .../PersistenceStore+Synchronisation.swift | 42 +++--- .../DownloadDeletionConfirmation+Alert.swift | 2 +- .../Shared/Content List/ContentListView.swift | 2 +- Emitron/emitronTests/Data/DataCacheTest.swift | 4 +- .../ContentPersistableState+Mocks.swift | 24 ++-- .../Downloads/DownloadQueueManagerTest.swift | 22 +-- .../Downloads/DownloadServiceTest.swift | 70 +++++----- Emitron/emitronTests/Models/UserTest.swift | 2 +- .../EntityAdapters/BookmarkAdapterTest.swift | 2 +- .../EntityAdapters/ContentAdapterTest.swift | 6 +- .../ContentCategoryAdapterTest.swift | 8 +- .../ContentDomainAdapterTest.swift | 8 +- .../EntityAdapters/GroupAdapterTest.swift | 4 +- .../ProgressionAdapterTest.swift | 2 +- .../Persistence/Models/PersistenceMocks.swift | 4 +- .../PersistenceStore+DownloadsTest.swift | 96 ++++++------- 65 files changed, 502 insertions(+), 502 deletions(-) diff --git a/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift b/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift index 78799c0c..461e01b8 100644 --- a/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift +++ b/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift @@ -95,14 +95,14 @@ final class ProgressEngine { } } - func updateProgress(for contentId: Int, progress: Int) -> Future { - let progression = updateCacheWithProgress(for: contentId, progress: progress) + func updateProgress(for contentID: Int, progress: Int) -> Future { + let progression = updateCacheWithProgress(for: contentID, progress: progress) switch mode { case .offline: do { - try syncAction?.updateProgress(for: contentId, progress: progress) - try syncAction?.recordWatchStats(for: contentId, secondsWatched: .videoPlaybackProgressTrackingInterval) + try syncAction?.updateProgress(for: contentID, progress: progress) + try syncAction?.recordWatchStats(for: contentID, secondsWatched: .videoPlaybackProgressTrackingInterval) return Future { promise in promise(.success(progression)) @@ -117,7 +117,7 @@ final class ProgressEngine { return Future { promise in // Don't bother trying if the playback token is empty. guard let playbackToken = self.playbackToken else { return } - self.contentsService.reportPlaybackUsage(for: contentId, progress: progress, playbackToken: playbackToken) { [weak self] response in + self.contentsService.reportPlaybackUsage(for: contentID, progress: progress, playbackToken: playbackToken) { [weak self] response in guard let self = self else { return promise(.failure(.notImplemented)) } switch response { case .failure(let error): @@ -131,7 +131,7 @@ final class ProgressEngine { // Update the cache and return the updated progression self.repository.apply(update: cacheUpdate) // Do we need to update the parent? - if let parentContent = self.repository.parentContent(for: contentId), + if let parentContent = self.repository.parentContent(for: contentID), let childProgressUpdate = self.repository.childProgress(for: parentContent.id), var existingProgression = self.repository.progression(for: parentContent.id) { existingProgression.progress = childProgressUpdate.completed @@ -155,11 +155,11 @@ final class ProgressEngine { } } - @discardableResult private func updateCacheWithProgress(for contentId: Int, progress: Int, target: Int? = nil) -> Progression { - let content = repository.content(for: contentId) + @discardableResult private func updateCacheWithProgress(for contentID: Int, progress: Int, target: Int? = nil) -> Progression { + let content = repository.content(for: contentID) let progression: Progression - if var existingProgression = repository.progression(for: contentId) { + if var existingProgression = repository.progression(for: contentID) { existingProgression.progress = progress progression = existingProgression } else { @@ -169,7 +169,7 @@ final class ProgressEngine { progress: progress, createdAt: Date(), updatedAt: Date(), - contentId: contentId + contentID: contentID ) } @@ -178,7 +178,7 @@ final class ProgressEngine { // See whether we need to update parent content if progression.finished, - let parentContent = repository.parentContent(for: contentId), + let parentContent = repository.parentContent(for: contentID), let childProgress = repository.childProgress(for: parentContent.id) { updateCacheWithProgress(for: parentContent.id, progress: childProgress.completed, target: childProgress.total) } diff --git a/Emitron/Emitron/Data Synchronisation/SyncAction.swift b/Emitron/Emitron/Data Synchronisation/SyncAction.swift index 429df89a..4bec9927 100644 --- a/Emitron/Emitron/Data Synchronisation/SyncAction.swift +++ b/Emitron/Emitron/Data Synchronisation/SyncAction.swift @@ -27,12 +27,12 @@ // THE SOFTWARE. protocol SyncAction: AnyObject { - func createBookmark(for contentId: Int) throws - func deleteBookmark(for contentId: Int) throws + func createBookmark(for contentID: Int) throws + func deleteBookmark(for contentID: Int) throws - func markContentAsComplete(contentId: Int) throws - func removeProgress(for contentId: Int) throws - func updateProgress(for contentId: Int, progress: Int) throws + func markContentAsComplete(contentID: Int) throws + func removeProgress(for contentID: Int) throws + func updateProgress(for contentID: Int, progress: Int) throws - func recordWatchStats(for contentId: Int, secondsWatched: Int) throws + func recordWatchStats(for contentID: Int, secondsWatched: Int) throws } diff --git a/Emitron/Emitron/Data Synchronisation/SyncEngine.swift b/Emitron/Emitron/Data Synchronisation/SyncEngine.swift index 4a540266..6756fddc 100644 --- a/Emitron/Emitron/Data Synchronisation/SyncEngine.swift +++ b/Emitron/Emitron/Data Synchronisation/SyncEngine.swift @@ -150,7 +150,7 @@ extension SyncEngine { syncRequests.forEach { syncRequest in guard syncRequest.type == .createBookmark else { return } - bookmarksService.makeBookmark(for: syncRequest.contentId) { [weak self] result in + bookmarksService.makeBookmark(for: syncRequest.contentID) { [weak self] result in guard let self = self else { return } switch result { @@ -178,10 +178,10 @@ extension SyncEngine { syncRequests.forEach { syncRequest in guard syncRequest.type == .deleteBookmark, - let bookmarkId = syncRequest.associatedRecordId + let bookmarkID = syncRequest.associatedRecordID else { return } - bookmarksService.destroyBookmark(for: bookmarkId) { [weak self] result in + bookmarksService.destroyBookmark(for: bookmarkID) { [weak self] result in guard let self = self else { return } switch result { @@ -195,7 +195,7 @@ extension SyncEngine { } case .success: // Update the cache - let cacheUpdate = DataCacheUpdate(bookmarkDeletionContentIds: [syncRequest.contentId]) + let cacheUpdate = DataCacheUpdate(bookmarkDeletionContentIDs: [syncRequest.contentID]) self.repository.apply(update: cacheUpdate) // Remove the sync request—we're done self.persistenceStore.complete(syncRequests: [syncRequest]) @@ -275,10 +275,10 @@ extension SyncEngine { syncRequests.forEach { syncRequest in guard syncRequest.type == .deleteProgression, - let progressionId = syncRequest.associatedRecordId + let progressionID = syncRequest.associatedRecordID else { return } - progressionsService.delete(with: progressionId) { [weak self] result in + progressionsService.delete(with: progressionID) { [weak self] result in guard let self = self else { return } switch result { @@ -293,7 +293,7 @@ extension SyncEngine { } case .success: // Update the cache - let cacheUpdate = DataCacheUpdate(progressionDeletionContentIds: [syncRequest.contentId]) + let cacheUpdate = DataCacheUpdate(progressionDeletionContentIDs: [syncRequest.contentID]) self.repository.apply(update: cacheUpdate) // Remove the sync request—we're done self.persistenceStore.complete(syncRequests: [syncRequest]) @@ -304,12 +304,12 @@ extension SyncEngine { } extension SyncEngine: SyncAction { - func createBookmark(for contentId: Int) throws { + func createBookmark(for contentID: Int) throws { // 1. Create / update sync request - try persistenceStore.createBookmarkSyncRequest(for: contentId) + try persistenceStore.createBookmarkSyncRequest(for: contentID) // 2. Create cache update and pass to repository - let bookmark = Bookmark(id: -1, createdAt: Date(), contentId: contentId) + let bookmark = Bookmark(id: -1, createdAt: Date(), contentID: contentID) let cacheUpdate = DataCacheUpdate(bookmarks: [bookmark]) repository.apply(update: cacheUpdate) @@ -317,32 +317,32 @@ extension SyncEngine: SyncAction { // TODO: Persist if the content has been persisted } - func deleteBookmark(for contentId: Int) throws { - guard let bookmark = repository.bookmark(for: contentId) else { return } + func deleteBookmark(for contentID: Int) throws { + guard let bookmark = repository.bookmark(for: contentID) else { return } // 1. Create / update sync request - try persistenceStore.deleteBookmarkSyncRequest(for: contentId, bookmarkId: bookmark.id) + try persistenceStore.deleteBookmarkSyncRequest(for: contentID, bookmarkID: bookmark.id) // 2. Create cache update and pass to repository - let cacheUpdate = DataCacheUpdate(bookmarkDeletionContentIds: [bookmark.contentId]) + let cacheUpdate = DataCacheUpdate(bookmarkDeletionContentIDs: [bookmark.contentID]) repository.apply(update: cacheUpdate) // 3. Delete bookmark if it is persisted // TODO: Delete bookmark if it is persisted } - func markContentAsComplete(contentId: Int) throws { + func markContentAsComplete(contentID: Int) throws { // 1. Create / update sync request - try persistenceStore.markContentAsCompleteSyncRequest(for: contentId) + try persistenceStore.markContentAsCompleteSyncRequest(for: contentID) // 2. Create cache update and pass to repository let cacheUpdate: DataCacheUpdate let progression: Progression - if var existingProgression = repository.progression(for: contentId) { + if var existingProgression = repository.progression(for: contentID) { existingProgression.progress = existingProgression.target progression = existingProgression } else { - guard let content = repository.content(for: contentId) else { return } + guard let content = repository.content(for: contentID) else { return } progression = Progression.completed(for: content) } cacheUpdate = DataCacheUpdate(progressions: [progression]) @@ -352,7 +352,7 @@ extension SyncEngine: SyncAction { // TODO: Update if persisted // 4. Check whether we need to update a parent - if let parentContent = repository.parentContent(for: contentId), + if let parentContent = repository.parentContent(for: contentID), let childProgressUpdate = repository.childProgress(for: parentContent.id), var existingProgression = repository.progression(for: parentContent.id) { existingProgression.progress = childProgressUpdate.completed @@ -361,18 +361,18 @@ extension SyncEngine: SyncAction { } } - func updateProgress(for contentId: Int, progress: Int) throws { + func updateProgress(for contentID: Int, progress: Int) throws { // 1. Create / update sync request - try persistenceStore.updateProgressSyncRequest(for: contentId, progress: progress) + try persistenceStore.updateProgressSyncRequest(for: contentID, progress: progress) // 2. Create cache update and pass to repository let cacheUpdate: DataCacheUpdate let progression: Progression - if var existingProgression = repository.progression(for: contentId) { + if var existingProgression = repository.progression(for: contentID) { existingProgression.progress = progress progression = existingProgression } else { - guard let content = repository.content(for: contentId) else { return } + guard let content = repository.content(for: contentID) else { return } progression = Progression.withProgress(for: content, progress: progress) } cacheUpdate = DataCacheUpdate(progressions: [progression]) @@ -382,7 +382,7 @@ extension SyncEngine: SyncAction { // TODO: Update if persisted // 4. Check whether we need to update a parent - if let parentContent = repository.parentContent(for: contentId), + if let parentContent = repository.parentContent(for: contentID), let childProgressUpdate = repository.childProgress(for: parentContent.id), var existingProgression = repository.progression(for: parentContent.id) { existingProgression.progress = childProgressUpdate.completed @@ -391,21 +391,21 @@ extension SyncEngine: SyncAction { } } - func removeProgress(for contentId: Int) throws { - guard let progression = repository.progression(for: contentId) else { return } + func removeProgress(for contentID: Int) throws { + guard let progression = repository.progression(for: contentID) else { return } // 1. Create / update sync request - try persistenceStore.removeProgressSyncRequest(for: contentId, progressionId: progression.id) + try persistenceStore.removeProgressSyncRequest(for: contentID, progressionID: progression.id) // 2. Create cache update and pass to repository - let cacheUpdate = DataCacheUpdate(progressionDeletionContentIds: [contentId]) + let cacheUpdate = DataCacheUpdate(progressionDeletionContentIDs: [contentID]) repository.apply(update: cacheUpdate) // 3. Remove if persisted // TODO: Remove if persisted // 4. Check whether we need to update a parent - if let parentContent = repository.parentContent(for: contentId), + if let parentContent = repository.parentContent(for: contentID), let childProgressUpdate = repository.childProgress(for: parentContent.id), var existingProgression = repository.progression(for: parentContent.id) { existingProgression.progress = childProgressUpdate.completed @@ -414,8 +414,8 @@ extension SyncEngine: SyncAction { } } - func recordWatchStats(for contentId: Int, secondsWatched: Int) throws { + func recordWatchStats(for contentID: Int, secondsWatched: Int) throws { // 1. Create / update sync request - try persistenceStore.watchStatsSyncRequest(for: contentId, secondsWatched: secondsWatched) + try persistenceStore.watchStatsSyncRequest(for: contentID, secondsWatched: secondsWatched) } } diff --git a/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift b/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift index 2a9f17ac..1debdef6 100644 --- a/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift @@ -53,7 +53,7 @@ class ContentRepository: ObservableObject, ContentPaginatable { // Let's see if we actually need it to be @Published... var state: DataState = .initial - private var contentIds: [Int] = [] + private var contentIDs: [Int] = [] private var contentSubscription: AnyCancellable? // Provide a value for this in a subclass to subscribe to invalidation notifications var invalidationPublisher: AnyPublisher? { nil } @@ -94,7 +94,7 @@ class ContentRepository: ObservableObject, ContentPaginatable { return } - guard contentIds.isEmpty || contentIds.count <= totalContentNum else { + guard contentIDs.isEmpty || contentIDs.count <= totalContentNum else { return } @@ -115,8 +115,8 @@ class ContentRepository: ObservableObject, ContentPaginatable { Failure .fetch(from: String(describing: type(of: self)), reason: error.localizedDescription) .log() - case .success(let (newContentIds, cacheUpdate, totalResultCount)): - self.contentIds += newContentIds + case .success(let (newContentIDs, cacheUpdate, totalResultCount)): + self.contentIDs += newContentIDs self.contentSubscription?.cancel() self.repository.apply(update: cacheUpdate) self.totalContentNum = totalResultCount @@ -150,8 +150,8 @@ class ContentRepository: ObservableObject, ContentPaginatable { Failure .fetch(from: String(describing: type(of: self)), reason: error.localizedDescription) .log() - case .success(let (newContentIds, cacheUpdate, totalResultCount)): - self.contentIds = newContentIds + case .success(let (newContentIDs, cacheUpdate, totalResultCount)): + self.contentIDs = newContentIDs self.contentSubscription?.cancel() self.repository.apply(update: cacheUpdate) self.totalContentNum = totalResultCount @@ -163,7 +163,7 @@ class ContentRepository: ObservableObject, ContentPaginatable { private func configureContentSubscription() { contentSubscription = repository - .contentSummaryState(for: contentIds) + .contentSummaryState(for: contentIDs) .removeDuplicates() .sink(receiveCompletion: { [weak self] error in guard let self = self else { return } @@ -194,9 +194,9 @@ class ContentRepository: ObservableObject, ContentPaginatable { } } - func dynamicContentViewModel(for contentId: Int) -> DynamicContentViewModel { + func dynamicContentViewModel(for contentID: Int) -> DynamicContentViewModel { DynamicContentViewModel( - contentId: contentId, + contentID: contentID, repository: repository, downloadAction: downloadAction, syncAction: syncAction, @@ -206,10 +206,10 @@ class ContentRepository: ObservableObject, ContentPaginatable { ) } - func childContentsViewModel(for contentId: Int) -> ChildContentsViewModel { + func childContentsViewModel(for contentID: Int) -> ChildContentsViewModel { // Default to using the cached version DataCacheChildContentsViewModel( - parentContentId: contentId, + parentContentID: contentID, downloadAction: downloadAction, syncAction: syncAction, repository: repository, diff --git a/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift b/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift index fe4c3329..c0343f02 100644 --- a/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift @@ -62,10 +62,10 @@ final class DownloadRepository: ContentRepository { configureSubscription() } - override func childContentsViewModel(for contentId: Int) -> ChildContentsViewModel { + override func childContentsViewModel(for contentID: Int) -> ChildContentsViewModel { // For downloaded content, we need to tell it to use the DB, not the service PersistenceStoreChildContentsViewModel( - parentContentId: contentId, + parentContentID: contentID, downloadAction: downloadService, syncAction: syncAction, repository: repository, diff --git a/Emitron/Emitron/Data/Repository.swift b/Emitron/Emitron/Data/Repository.swift index 9956b1d0..3fa22aac 100644 --- a/Emitron/Emitron/Data/Repository.swift +++ b/Emitron/Emitron/Data/Repository.swift @@ -45,9 +45,9 @@ extension Repository { } extension Repository { - func contentSummaryState(for contentIds: [Int]) -> AnyPublisher<[ContentSummaryState], Error> { + func contentSummaryState(for contentIDs: [Int]) -> AnyPublisher<[ContentSummaryState], Error> { dataCache - .contentSummaryState(for: contentIds) + .contentSummaryState(for: contentIDs) .map { cachedContentSummaryStates in cachedContentSummaryStates.map { cached in self.contentSummaryState(cached: cached) @@ -56,23 +56,23 @@ extension Repository { .eraseToAnyPublisher() } - func contentSummaryState(for contentId: Int) -> AnyPublisher { + func contentSummaryState(for contentID: Int) -> AnyPublisher { dataCache - .contentSummaryState(for: contentId) + .contentSummaryState(for: contentID) .map { cachedContentSummaryState in self.contentSummaryState(cached: cachedContentSummaryState) } .eraseToAnyPublisher() } - func childContentsState(for contentId: Int) -> AnyPublisher { + func childContentsState(for contentID: Int) -> AnyPublisher { dataCache - .childContentsState(for: contentId) + .childContentsState(for: contentID) } - func contentDynamicState(for contentId: Int) -> AnyPublisher { - let fromCache = dataCache.contentDynamicState(for: contentId) - let download = persistenceStore.download(for: contentId) + func contentDynamicState(for contentID: Int) -> AnyPublisher { + let fromCache = dataCache.contentDynamicState(for: contentID) + let download = persistenceStore.download(for: contentID) return fromCache .combineLatest(download) @@ -85,16 +85,16 @@ extension Repository { .eraseToAnyPublisher() } - func contentPersistableState(for contentId: Int) throws -> ContentPersistableState? { - try dataCache.cachedContentPersistableState(for: contentId) + func contentPersistableState(for contentID: Int) throws -> ContentPersistableState? { + try dataCache.cachedContentPersistableState(for: contentID) } /// Return an array of states that provide the playlist of what should be played for this item of content - /// - Parameter contentId: The id of the `Content` the user has requested to be played back - func playlist(for contentId: Int) throws -> [VideoPlaybackState] { - let fromCache = try dataCache.videoPlaylist(for: contentId) + /// - Parameter contentID: The id of the `Content` the user has requested to be played back + func playlist(for contentID: Int) throws -> [VideoPlaybackState] { + let fromCache = try dataCache.videoPlaylist(for: contentID) return try fromCache.map { cachedState in - let download = try persistenceStore.download(forContentId: cachedState.content.id) + let download = try persistenceStore.download(forContentID: cachedState.content.id) return VideoPlaybackState( content: cachedState.content, progression: cachedState.progression, @@ -109,23 +109,23 @@ extension Repository { } /// Attempt to find a progression from the cache for a given item of content - /// - Parameter contentId: The `id` of the content item - func progression(for contentId: Int) -> Progression? { - dataCache.progression(for: contentId) + /// - Parameter contentID: The `id` of the content item + func progression(for contentID: Int) -> Progression? { + dataCache.progression(for: contentID) } /// Attempt to locate a bookmark from the cache for a given item of content - /// - Parameter contentId: The `id` of the content item - func bookmark(for contentId: Int) -> Bookmark? { - dataCache.bookmark(for: contentId) + /// - Parameter contentID: The `id` of the content item + func bookmark(for contentID: Int) -> Bookmark? { + dataCache.bookmark(for: contentID) } - func parentContent(for contentId: Int) -> Content? { - dataCache.parentContent(for: contentId) + func parentContent(for contentID: Int) -> Content? { + dataCache.parentContent(for: contentID) } - func childProgress(for contentId: Int) -> (total: Int, completed: Int)? { - dataCache.childProgress(for: contentId) + func childProgress(for contentID: Int) -> (total: Int, completed: Int)? { + dataCache.childProgress(for: contentID) } func domainList() throws -> [Domain] { @@ -144,9 +144,9 @@ extension Repository { try persistenceStore.sync(categories: categories) } - func loadDownloadedChildContentsIntoCache(for contentId: Int) throws { - guard let content = try persistenceStore.downloadedContent(with: contentId), - let childContents = try persistenceStore.childContentsForDownloadedContent(with: contentId) else { + func loadDownloadedChildContentsIntoCache(for contentID: Int) throws { + guard let content = try persistenceStore.downloadedContent(with: contentID), + let childContents = try persistenceStore.childContentsForDownloadedContent(with: contentID) else { throw PersistenceStoreError.notFound } let cacheUpdate = DataCacheUpdate(contents: childContents.contents + [content], groups: childContents.groups) @@ -164,7 +164,7 @@ extension Repository { private func domains(from contentDomains: [ContentDomain]) -> [Domain] { do { - return try persistenceStore.domains( with: contentDomains.map(\.domainId) ) + return try persistenceStore.domains( with: contentDomains.map(\.domainID) ) } catch { Failure .loadFromPersistentStore(from: String(describing: type(of: self)), reason: "There was a problem getting domains: \(error)") @@ -175,7 +175,7 @@ extension Repository { private func categories(from contentCategories: [ContentCategory]) -> [Category] { do { - return try persistenceStore.categories( with: contentCategories.map(\.categoryId) ) + return try persistenceStore.categories( with: contentCategories.map(\.categoryID) ) } catch { Failure .loadFromPersistentStore(from: String(describing: type(of: self)), reason: "There was a problem getting categories: \(error)") diff --git a/Emitron/Emitron/Data/Service Adapters/BookmarksService+ContentServiceAdapter.swift b/Emitron/Emitron/Data/Service Adapters/BookmarksService+ContentServiceAdapter.swift index 30783df5..f3a10545 100644 --- a/Emitron/Emitron/Data/Service Adapters/BookmarksService+ContentServiceAdapter.swift +++ b/Emitron/Emitron/Data/Service Adapters/BookmarksService+ContentServiceAdapter.swift @@ -31,7 +31,7 @@ extension BookmarksService: ContentServiceAdapter { bookmarks(parameters: parameters) { result in completion(result.map { response in ( - contentIds: response.bookmarks.map(\.contentId), + contentIDs: response.bookmarks.map(\.contentID), cacheUpdate: response.cacheUpdate, totalResultCount: response.totalNumber ) diff --git a/Emitron/Emitron/Data/Service Adapters/ContentServiceAdapter.swift b/Emitron/Emitron/Data/Service Adapters/ContentServiceAdapter.swift index a04a9055..46c59c25 100644 --- a/Emitron/Emitron/Data/Service Adapters/ContentServiceAdapter.swift +++ b/Emitron/Emitron/Data/Service Adapters/ContentServiceAdapter.swift @@ -26,7 +26,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -typealias ContentServiceAdapterResponse = Result<(contentIds: [Int], cacheUpdate: DataCacheUpdate, totalResultCount: Int), RWAPIError> +typealias ContentServiceAdapterResponse = Result<(contentIDs: [Int], cacheUpdate: DataCacheUpdate, totalResultCount: Int), RWAPIError> protocol ContentServiceAdapter { func findContent(parameters: [Parameter], completion: @escaping(_ response: ContentServiceAdapterResponse) -> Void) diff --git a/Emitron/Emitron/Data/Service Adapters/ContentsService+ContentServiceAdapter.swift b/Emitron/Emitron/Data/Service Adapters/ContentsService+ContentServiceAdapter.swift index b81838b9..98f93f76 100644 --- a/Emitron/Emitron/Data/Service Adapters/ContentsService+ContentServiceAdapter.swift +++ b/Emitron/Emitron/Data/Service Adapters/ContentsService+ContentServiceAdapter.swift @@ -31,7 +31,7 @@ extension ContentsService: ContentServiceAdapter { allContents(parameters: parameters) { result in completion(result.map { response in ( - contentIds: response.contents.map(\.id), + contentIDs: response.contents.map(\.id), cacheUpdate: response.cacheUpdate, totalResultCount: response.totalNumber ) diff --git a/Emitron/Emitron/Data/Service Adapters/ProgressionsService+ContentServiceAdapter.swift b/Emitron/Emitron/Data/Service Adapters/ProgressionsService+ContentServiceAdapter.swift index 0ae602b3..993a90c8 100644 --- a/Emitron/Emitron/Data/Service Adapters/ProgressionsService+ContentServiceAdapter.swift +++ b/Emitron/Emitron/Data/Service Adapters/ProgressionsService+ContentServiceAdapter.swift @@ -31,7 +31,7 @@ extension ProgressionsService: ContentServiceAdapter { progressions(parameters: parameters) { result in completion(result.map { response in ( - contentIds: response.progressions.map(\.contentId), + contentIDs: response.progressions.map(\.contentID), cacheUpdate: response.cacheUpdate, totalResultCount: response.totalNumber ) diff --git a/Emitron/Emitron/Data/States/ContentPersistableState.swift b/Emitron/Emitron/Data/States/ContentPersistableState.swift index 2c90e248..8e6ff286 100644 --- a/Emitron/Emitron/Data/States/ContentPersistableState.swift +++ b/Emitron/Emitron/Data/States/ContentPersistableState.swift @@ -37,4 +37,4 @@ struct ContentPersistableState: Equatable { let childContents: [Content] } -typealias ContentLookup = (_ contentId: Int) -> ContentPersistableState? +typealias ContentLookup = (_ contentID: Int) -> ContentPersistableState? diff --git a/Emitron/Emitron/Data/ViewModels/ChildContentsViewModel.swift b/Emitron/Emitron/Data/ViewModels/ChildContentsViewModel.swift index 5e616410..62738bd7 100644 --- a/Emitron/Emitron/Data/ViewModels/ChildContentsViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/ChildContentsViewModel.swift @@ -29,7 +29,7 @@ import Combine class ChildContentsViewModel: ObservableObject { - let parentContentId: Int + let parentContentID: Int let downloadAction: DownloadAction weak var syncAction: SyncAction? let repository: Repository @@ -43,14 +43,14 @@ class ChildContentsViewModel: ObservableObject { var subscriptions = Set() - init(parentContentId: Int, + init(parentContentID: Int, downloadAction: DownloadAction, syncAction: SyncAction?, repository: Repository, messageBus: MessageBus, settingsManager: SettingsManager, sessionController: SessionController) { - self.parentContentId = parentContentId + self.parentContentID = parentContentID self.downloadAction = downloadAction self.syncAction = syncAction self.repository = repository @@ -75,13 +75,13 @@ class ChildContentsViewModel: ObservableObject { configureSubscriptions() } - func contents(for groupId: Int) -> [ChildContentListDisplayable] { - contents.filter({ $0.groupId == groupId }) + func contents(for groupID: Int) -> [ChildContentListDisplayable] { + contents.filter({ $0.groupID == groupID }) } func configureSubscriptions() { repository - .childContentsState(for: parentContentId) + .childContentsState(for: parentContentID) .sink(receiveCompletion: { [weak self] completion in guard let self = self else { return } if case .failure(let error) = completion, (error as? DataCacheError) == DataCacheError.cacheMiss { @@ -106,9 +106,9 @@ class ChildContentsViewModel: ObservableObject { preconditionFailure("Override in a subclass please.") } - func dynamicContentViewModel(for contentId: Int) -> DynamicContentViewModel { + func dynamicContentViewModel(for contentID: Int) -> DynamicContentViewModel { DynamicContentViewModel( - contentId: contentId, + contentID: contentID, repository: repository, downloadAction: downloadAction, syncAction: syncAction, diff --git a/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift b/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift index 0a0750bf..8c907eed 100644 --- a/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift @@ -29,7 +29,7 @@ final class DataCacheChildContentsViewModel: ChildContentsViewModel { private let service: ContentsService - init(parentContentId: Int, + init(parentContentID: Int, downloadAction: DownloadAction, syncAction: SyncAction?, repository: Repository, @@ -38,7 +38,7 @@ final class DataCacheChildContentsViewModel: ChildContentsViewModel { settingsManager: SettingsManager, sessionController: SessionController) { self.service = service - super.init(parentContentId: parentContentId, + super.init(parentContentID: parentContentID, downloadAction: downloadAction, syncAction: syncAction, repository: repository, @@ -49,7 +49,7 @@ final class DataCacheChildContentsViewModel: ChildContentsViewModel { override func loadContentDetailsIntoCache() { state = .loading - service.contentDetails(for: parentContentId) { result in + service.contentDetails(for: parentContentID) { result in switch result { case .failure(let error): self.state = .failed diff --git a/Emitron/Emitron/Data/ViewModels/DownloadDeletionConfirmation.swift b/Emitron/Emitron/Data/ViewModels/DownloadDeletionConfirmation.swift index 96f2c9e8..7cf4c019 100644 --- a/Emitron/Emitron/Data/ViewModels/DownloadDeletionConfirmation.swift +++ b/Emitron/Emitron/Data/ViewModels/DownloadDeletionConfirmation.swift @@ -27,7 +27,7 @@ // THE SOFTWARE. struct DownloadDeletionConfirmation { - let contentId: Int + let contentID: Int let title: String let message: String let confirm: () -> Void diff --git a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift index ab312fd1..cb64c378 100644 --- a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift @@ -31,7 +31,7 @@ import Combine import class Foundation.RunLoop final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable { - private let contentId: Int + private let contentID: Int private let repository: Repository private let downloadAction: DownloadAction private weak var syncAction: SyncAction? @@ -49,8 +49,8 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable private var subscriptions = Set() private var downloadActionSubscriptions = Set() - init(contentId: Int, repository: Repository, downloadAction: DownloadAction, syncAction: SyncAction?, messageBus: MessageBus, settingsManager: SettingsManager, sessionController: SessionController) { - self.contentId = contentId + init(contentID: Int, repository: Repository, downloadAction: DownloadAction, syncAction: SyncAction?, messageBus: MessageBus, settingsManager: SettingsManager, sessionController: SessionController) { + self.contentID = contentID self.repository = repository self.downloadAction = downloadAction self.syncAction = syncAction @@ -77,9 +77,9 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable switch downloadProgress { case .downloadable: - downloadAction.requestDownload(contentId: contentId) { contentId -> (ContentPersistableState?) in + downloadAction.requestDownload(contentID: contentID) { contentID -> (ContentPersistableState?) in do { - return try self.repository.contentPersistableState(for: contentId) + return try self.repository.contentPersistableState(for: contentID) } catch { Failure .repositoryLoad(from: String(describing: type(of: self)), reason: "Unable to locate persistable state in cache: \(error)") @@ -103,7 +103,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable .store(in: &downloadActionSubscriptions) case .enqueued, .inProgress: - downloadAction.cancelDownload(contentId: contentId) + downloadAction.cancelDownload(contentID: contentID) .receive(on: RunLoop.main) .sink(receiveCompletion: { [weak self] completion in if case .failure(let error) = completion { @@ -116,13 +116,13 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable case .downloaded: return DownloadDeletionConfirmation( - contentId: contentId, + contentID: contentID, title: "Confirm Delete", message: "Are you sure you want to delete this download?" ) { [weak self] in guard let self = self else { return } - self.downloadAction.deleteDownload(contentId: self.contentId) + self.downloadAction.deleteDownload(contentID: self.contentID) .receive(on: RunLoop.main) .sink(receiveCompletion: { [weak self] completion in if case .failure(let error) = completion { @@ -135,7 +135,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable } case .notDownloadable: - downloadAction.cancelDownload(contentId: contentId) + downloadAction.cancelDownload(contentID: contentID) .receive(on: RunLoop.main) .sink(receiveCompletion: { [weak self] completion in if case .failure(let error) = completion { @@ -157,7 +157,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable if bookmarked { do { - try syncAction.deleteBookmark(for: contentId) + try syncAction.deleteBookmark(for: contentID) } catch { messageBus.post(message: Message(level: .error, message: .bookmarkDeletedError)) Failure @@ -166,7 +166,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable } } else { do { - try syncAction.createBookmark(for: contentId) + try syncAction.createBookmark(for: contentID) UINotificationFeedbackGenerator().notificationOccurred(.success) } catch { messageBus.post(message: Message(level: .error, message: .bookmarkCreatedError)) @@ -183,7 +183,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable if case .completed = viewProgress { do { - try syncAction.removeProgress(for: contentId) + try syncAction.removeProgress(for: contentID) messageBus.post(message: Message(level: .success, message: .progressRemoved)) } catch { messageBus.post(message: Message(level: .error, message: .progressRemovedError)) @@ -193,7 +193,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable } } else { do { - try syncAction.markContentAsComplete(contentId: contentId) + try syncAction.markContentAsComplete(contentID: contentID) messageBus.post(message: Message(level: .success, message: .progressMarkedAsComplete)) } catch { messageBus.post(message: Message(level: .error, message: .progressMarkedAsCompleteError)) @@ -208,7 +208,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable let videosService = VideosService(client: apiClient) let contentsService = ContentsService(client: apiClient) return VideoPlaybackViewModel( - contentId: contentId, + contentID: contentID, repository: repository, videosService: videosService, contentsService: contentsService, @@ -225,7 +225,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable private extension DynamicContentViewModel { func configureSubscriptions() { repository - .contentDynamicState(for: contentId) + .contentDynamicState(for: contentID) .removeDuplicates() .sink(receiveCompletion: { [weak self] completion in self?.state = .failed diff --git a/Emitron/Emitron/Data/ViewModels/PersistenceStoreChildContentsViewModel.swift b/Emitron/Emitron/Data/ViewModels/PersistenceStoreChildContentsViewModel.swift index d84e492a..85fecde9 100644 --- a/Emitron/Emitron/Data/ViewModels/PersistenceStoreChildContentsViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/PersistenceStoreChildContentsViewModel.swift @@ -32,7 +32,7 @@ final class PersistenceStoreChildContentsViewModel: ChildContentsViewModel { override func loadContentDetailsIntoCache() { do { - try repository.loadDownloadedChildContentsIntoCache(for: parentContentId) + try repository.loadDownloadedChildContentsIntoCache(for: parentContentID) DispatchQueue.main.async(execute: reload) } catch { state = .failed diff --git a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift index e6f0208a..714c0c20 100644 --- a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift @@ -80,7 +80,7 @@ final class VideoPlaybackViewModel { // Allow control of appearance and dismissal of the video view var shouldShow = false - private let initialContentId: Int + private let initialContentID: Int private let repository: Repository private let videosService: VideosService private let contentsService: ContentsService @@ -92,11 +92,11 @@ final class VideoPlaybackViewModel { private var contentList = [VideoPlaybackState]() // A cache of playback items, and a way of finding the content model for the currently playing item private var playerItems = [Int: AVPlayerItem]() - private var currentlyPlayingContentId: Int? { + private var currentlyPlayingContentID: Int? { guard let currentItem = player.currentItem, - let contentId = playerItems.first(where: { $1 == currentItem })?.key + let contentID = playerItems.first(where: { $1 == currentItem })?.key else { return nil } - return contentId + return contentID } // Managing the Player queue. We enqueue stuff at the last possible moment. private var nextContentToEnqueueIndex = 0 @@ -113,7 +113,7 @@ final class VideoPlaybackViewModel { private var shouldBePlaying = false let settingsManager: SettingsManager - init(contentId: Int, + init(contentID: Int, repository: Repository, videosService: VideosService, contentsService: ContentsService, @@ -122,7 +122,7 @@ final class VideoPlaybackViewModel { messageBus: MessageBus, settingsManager: SettingsManager, dismissClosure: @escaping () -> Void = { }) { - initialContentId = contentId + initialContentID = contentID self.repository = repository self.videosService = videosService self.contentsService = contentsService @@ -185,7 +185,7 @@ final class VideoPlaybackViewModel { do { state = .loading progressEngine.start() - contentList = try repository.playlist(for: initialContentId) + contentList = try repository.playlist(for: initialContentID) nextContentToEnqueueIndex = 0 if let progression = nextContentToEnqueue.progression, !progression.finished { @@ -295,9 +295,9 @@ private extension VideoPlaybackViewModel { } func handleTimeUpdate(time: CMTime) { - guard let currentlyPlayingContentId = currentlyPlayingContentId else { return } + guard let currentlyPlayingContentID = currentlyPlayingContentID else { return } // Update progress - progressEngine.updateProgress(for: currentlyPlayingContentId, progress: Int(time.seconds)) + progressEngine.updateProgress(for: currentlyPlayingContentID, progress: Int(time.seconds)) .sink(receiveCompletion: { [weak self] completion in guard let self = self else { return } diff --git a/Emitron/Emitron/Displayable/ContentDisplayable.swift b/Emitron/Emitron/Displayable/ContentDisplayable.swift index 3fabce54..d15c20d6 100644 --- a/Emitron/Emitron/Displayable/ContentDisplayable.swift +++ b/Emitron/Emitron/Displayable/ContentDisplayable.swift @@ -149,7 +149,7 @@ protocol ChildContentListDisplayable: Ownable { var descriptionPlainText: String { get } var ordinal: Int? { get } var duration: Int { get } - var groupId: Int? { get } + var groupID: Int? { get } var videoIdentifier: Int? { get } } diff --git a/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift b/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift index fb4e3015..18fe61cd 100644 --- a/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift +++ b/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift @@ -82,8 +82,8 @@ extension ContentSummaryState: ContentListDisplayable { content.contributors } - var groupId: Int? { - content.groupId + var groupID: Int? { + content.groupID } var videoIdentifier: Int? { diff --git a/Emitron/Emitron/Downloads/DownloadAction.swift b/Emitron/Emitron/Downloads/DownloadAction.swift index 2b195b57..cd88742d 100644 --- a/Emitron/Emitron/Downloads/DownloadAction.swift +++ b/Emitron/Emitron/Downloads/DownloadAction.swift @@ -57,7 +57,7 @@ enum DownloadActionError: Error { } protocol DownloadAction { - func requestDownload(contentId: Int, contentLookup: @escaping ContentLookup) -> AnyPublisher - func cancelDownload(contentId: Int) -> AnyPublisher - func deleteDownload(contentId: Int) -> AnyPublisher + func requestDownload(contentID: Int, contentLookup: @escaping ContentLookup) -> AnyPublisher + func cancelDownload(contentID: Int) -> AnyPublisher + func deleteDownload(contentID: Int) -> AnyPublisher } diff --git a/Emitron/Emitron/Downloads/DownloadProcessor.swift b/Emitron/Emitron/Downloads/DownloadProcessor.swift index 12c8554a..260a6eed 100644 --- a/Emitron/Emitron/Downloads/DownloadProcessor.swift +++ b/Emitron/Emitron/Downloads/DownloadProcessor.swift @@ -37,16 +37,16 @@ protocol DownloadProcessorModel { } protocol DownloadProcessorDelegate: AnyObject { - func downloadProcessor(_ processor: DownloadProcessor, downloadModelForDownloadWithId downloadId: UUID) -> DownloadProcessorModel? - func downloadProcessor(_ processor: DownloadProcessor, didStartDownloadWithId downloadId: UUID) - func downloadProcessor(_ processor: DownloadProcessor, downloadWithId downloadId: UUID, didUpdateProgress progress: Double) - func downloadProcessor(_ processor: DownloadProcessor, didFinishDownloadWithId downloadId: UUID) - func downloadProcessor(_ processor: DownloadProcessor, didCancelDownloadWithId downloadId: UUID) - func downloadProcessor(_ processor: DownloadProcessor, downloadWithId downloadId: UUID, didFailWithError error: Error) + func downloadProcessor(_ processor: DownloadProcessor, downloadModelForDownloadWithID downloadID: UUID) -> DownloadProcessorModel? + func downloadProcessor(_ processor: DownloadProcessor, didStartDownloadWithID downloadID: UUID) + func downloadProcessor(_ processor: DownloadProcessor, downloadWithID downloadID: UUID, didUpdateProgress progress: Double) + func downloadProcessor(_ processor: DownloadProcessor, didFinishDownloadWithID downloadID: UUID) + func downloadProcessor(_ processor: DownloadProcessor, didCancelDownloadWithID downloadID: UUID) + func downloadProcessor(_ processor: DownloadProcessor, downloadWithID downloadID: UUID, didFailWithError error: Error) } private extension URLSessionDownloadTask { - var downloadId: UUID? { + var downloadID: UUID? { get { guard let taskDescription = taskDescription else { return .none } return UUID(uuidString: taskDescription) @@ -58,7 +58,7 @@ private extension URLSessionDownloadTask { } private extension AVAssetDownloadTask { - var downloadId: UUID? { + var downloadID: UUID? { get { guard let taskDescription = taskDescription else { return .none } return UUID(uuidString: taskDescription) @@ -113,16 +113,16 @@ extension DownloadProcessor { } guard let downloadTask = session.makeAssetDownloadTask(asset: hlsAsset, assetTitle: "\(download.id))", assetArtworkData: nil, options: options) else { return } - downloadTask.downloadId = download.id + downloadTask.downloadID = download.id downloadTask.resume() currentDownloads.append(downloadTask) - delegate.downloadProcessor(self, didStartDownloadWithId: download.id) + delegate.downloadProcessor(self, didStartDownloadWithID: download.id) } func cancelDownload(_ download: DownloadProcessorModel) throws { - guard let downloadTask = currentDownloads.first(where: { $0.downloadId == download.id }) else { throw DownloadProcessorError.unknownDownload } + guard let downloadTask = currentDownloads.first(where: { $0.downloadID == download.id }) else { throw DownloadProcessorError.unknownDownload } downloadTask.cancel() } @@ -167,7 +167,7 @@ extension DownloadProcessor: AVAssetDownloadDelegate { func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didLoad timeRange: CMTimeRange, totalTimeRangesLoaded loadedTimeRanges: [NSValue], timeRangeExpectedToLoad: CMTimeRange) { - guard let downloadId = assetDownloadTask.downloadId else { return } + guard let downloadID = assetDownloadTask.downloadID else { return } var percentComplete = 0.0 for value in loadedTimeRanges { @@ -175,21 +175,21 @@ extension DownloadProcessor: AVAssetDownloadDelegate { percentComplete += CMTimeGetSeconds(loadedTimeRange.duration) / CMTimeGetSeconds(timeRangeExpectedToLoad.duration) } - if let lastReportedProgress = throttleList[downloadId], + if let lastReportedProgress = throttleList[downloadID], abs(percentComplete - lastReportedProgress) < 0.02 { // Less than a 2% change—it's a no-op return } - throttleList[downloadId] = percentComplete - delegate.downloadProcessor(self, downloadWithId: downloadId, didUpdateProgress: percentComplete) + throttleList[downloadID] = percentComplete + delegate.downloadProcessor(self, downloadWithID: downloadID, didUpdateProgress: percentComplete) } func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) { - guard let downloadId = assetDownloadTask.downloadId, + guard let downloadID = assetDownloadTask.downloadID, let delegate = delegate else { return } - let download = delegate.downloadProcessor(self, downloadModelForDownloadWithId: downloadId) + let download = delegate.downloadProcessor(self, downloadModelForDownloadWithID: downloadID) guard let localURL = download?.localURL else { return } let fileManager = FileManager.default @@ -199,7 +199,7 @@ extension DownloadProcessor: AVAssetDownloadDelegate { } try fileManager.moveItem(at: location, to: localURL) } catch { - delegate.downloadProcessor(self, downloadWithId: downloadId, didFailWithError: error) + delegate.downloadProcessor(self, downloadWithID: downloadID, didFailWithError: error) } } } @@ -215,41 +215,41 @@ extension DownloadProcessor: URLSessionDownloadDelegate { // Used to update the progress stats of a download task func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { - guard let downloadId = downloadTask.downloadId else { return } + guard let downloadID = downloadTask.downloadID else { return } let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite) // We want to call the progress update every 2% - if let lastReportedProgress = throttleList[downloadId], + if let lastReportedProgress = throttleList[downloadID], abs(progress - lastReportedProgress) < 0.02 { // Less than a 2% change—it's a no-op return } // Update the throttle list and make the delegate call - throttleList[downloadId] = progress - delegate.downloadProcessor(self, downloadWithId: downloadId, didUpdateProgress: progress) + throttleList[downloadID] = progress + delegate.downloadProcessor(self, downloadWithID: downloadID, didUpdateProgress: progress) } // Download completed—move the file to the appropriate place and update the DB func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { - guard let downloadId = downloadTask.downloadId, + guard let downloadID = downloadTask.downloadID, let delegate = delegate else { return } - let download = delegate.downloadProcessor(self, downloadModelForDownloadWithId: downloadId) + let download = delegate.downloadProcessor(self, downloadModelForDownloadWithID: downloadID) guard let localURL = download?.localURL else { return } let fileManager = FileManager.default do { try fileManager.moveItem(at: location, to: localURL) } catch { - delegate.downloadProcessor(self, downloadWithId: downloadId, didFailWithError: error) + delegate.downloadProcessor(self, downloadWithID: downloadID, didFailWithError: error) } } // Use this to handle and client-side download errors func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - guard let downloadTask = task as? AVAssetDownloadTask, let downloadId = downloadTask.downloadId else { return } + guard let downloadTask = task as? AVAssetDownloadTask, let downloadID = downloadTask.downloadID else { return } if let error = error as NSError? { let cancellationReason = (error.userInfo[NSURLErrorBackgroundTaskCancelledReasonKey] as? NSNumber)?.intValue @@ -261,18 +261,18 @@ extension DownloadProcessor: URLSessionDownloadDelegate { // User-requested cancellation currentDownloads.removeAll { $0 == downloadTask } - delegate.downloadProcessor(self, didCancelDownloadWithId: downloadId) + delegate.downloadProcessor(self, didCancelDownloadWithID: downloadID) } else { // Unknown error currentDownloads.removeAll { $0 == downloadTask } - delegate.downloadProcessor(self, downloadWithId: downloadId, didFailWithError: error) + delegate.downloadProcessor(self, downloadWithID: downloadID, didFailWithError: error) } } else { // Success! currentDownloads.removeAll { $0 == downloadTask } - delegate.downloadProcessor(self, didFinishDownloadWithId: downloadId) + delegate.downloadProcessor(self, didFinishDownloadWithID: downloadID) } } } diff --git a/Emitron/Emitron/Downloads/DownloadService.swift b/Emitron/Emitron/Downloads/DownloadService.swift index ce234ba0..db26f2cf 100644 --- a/Emitron/Emitron/Downloads/DownloadService.swift +++ b/Emitron/Emitron/Downloads/DownloadService.swift @@ -140,7 +140,7 @@ final class DownloadService: ObservableObject { // MARK: - DownloadAction Methods extension DownloadService: DownloadAction { - func requestDownload(contentId: Int, contentLookup: @escaping ContentLookup) -> AnyPublisher { + func requestDownload(contentID: Int, contentLookup: @escaping ContentLookup) -> AnyPublisher { guard videosService != nil else { Failure .fetch(from: String(describing: type(of: self)), reason: "User not allowed to request downloads") @@ -151,7 +151,7 @@ extension DownloadService: DownloadAction { .eraseToAnyPublisher() } - guard let contentPersistableState = contentLookup(contentId) else { + guard let contentPersistableState = contentLookup(contentID) else { Failure .loadFromPersistentStore(from: String(describing: type(of: self)), reason: "Unable to locate content to persist") .log() @@ -186,18 +186,18 @@ extension DownloadService: DownloadAction { .eraseToAnyPublisher() } - func cancelDownload(contentId: Int) -> AnyPublisher { - var contentIds = [Int]() + func cancelDownload(contentID: Int) -> AnyPublisher { + var contentIDs = [Int]() // 0. If there are some children, then let's find their content ids too - if let children = try? persistenceStore.childContentsForDownloadedContent(with: contentId) { - contentIds = children.contents.map(\.id) + if let children = try? persistenceStore.childContentsForDownloadedContent(with: contentID) { + contentIDs = children.contents.map(\.id) } - contentIds += [contentId] + contentIDs += [contentID] // 1. Find the downloads. - let downloads = contentIds.compactMap { - try? persistenceStore.download(forContentId: $0) + let downloads = contentIDs.compactMap { + try? persistenceStore.download(forContentID: $0) } // 2. Is it already downloading? let currentlyDownloading = downloads.filter { $0.isDownloading } @@ -218,29 +218,29 @@ extension DownloadService: DownloadAction { // Don't have it in the processor, so we just need to // delete the download model self.persistenceStore - .deleteDownloads(withIds: notYetDownloading.map(\.id)) + .deleteDownloads(withIDs: notYetDownloading.map(\.id)) } .mapError { error in Failure - .deleteFromPersistentStore(from: String(describing: type(of: self)), reason: "There was a problem cancelling the download (contentId: \(contentId)): \(error)") + .deleteFromPersistentStore(from: String(describing: type(of: self)), reason: "There was a problem cancelling the download (contentID: \(contentID)): \(error)") .log() return DownloadActionError.unableToCancelDownload } .eraseToAnyPublisher() } - func deleteDownload(contentId: Int) -> AnyPublisher { - var contentIds = [Int]() + func deleteDownload(contentID: Int) -> AnyPublisher { + var contentIDs = [Int]() // 0. If there are some children, the let's find their content ids too - if let children = try? persistenceStore.childContentsForDownloadedContent(with: contentId) { - contentIds = children.contents.map(\.id) + if let children = try? persistenceStore.childContentsForDownloadedContent(with: contentID) { + contentIDs = children.contents.map(\.id) } - contentIds += [contentId] + contentIDs += [contentID] // 1. Find the downloads - let downloads = contentIds.compactMap { - try? persistenceStore.download(forContentId: $0) + let downloads = contentIDs.compactMap { + try? persistenceStore.download(forContentID: $0) } return Future { promise in @@ -257,11 +257,11 @@ extension DownloadService: DownloadAction { .flatMap { // 3. Delete the persisted record self.persistenceStore - .deleteDownloads(withIds: downloads.map(\.id)) + .deleteDownloads(withIDs: downloads.map(\.id)) } .mapError { error in Failure - .deleteFromPersistentStore(from: String(describing: type(of: self)), reason: "There was a problem deleting the download (contentId: \(contentId)): \(error)") + .deleteFromPersistentStore(from: String(describing: type(of: self)), reason: "There was a problem deleting the download (contentID: \(contentID)): \(error)") .log() return DownloadActionError.unableToDeleteDownload } @@ -291,19 +291,19 @@ extension DownloadService { return } // Find the video ID - guard let videoId = downloadQueueItem.content.videoIdentifier, - videoId != 0 else { + guard let videoID = downloadQueueItem.content.videoIdentifier, + videoID != 0 else { Failure .downloadService( from: "requestDownloadURL", - reason: "Unable to locate videoId for download: \(downloadQueueItem.download)" + reason: "Unable to locate videoID for download: \(downloadQueueItem.download)" ) .log() return } // Use the video service to request the URLs - videosService.getVideoStreamDownload(for: videoId) { [weak self] result in + videosService.getVideoStreamDownload(for: videoID) { [weak self] result in // Ensure we're still around guard let self = self else { return } var download = downloadQueueItem.download @@ -350,16 +350,16 @@ extension DownloadService { return } // Find the video ID - guard let videoId = downloadQueueItem.content.videoIdentifier else { + guard let videoID = downloadQueueItem.content.videoIdentifier else { Failure .downloadService(from: "enqueue", - reason: "Unable to locate videoId for download: \(downloadQueueItem.download)") + reason: "Unable to locate videoID for download: \(downloadQueueItem.download)") .log() return } // Generate filename - let filename = "\(videoId).m3u8" + let filename = "\(videoID).m3u8" // Save local URL and filename var download = downloadQueueItem.download @@ -459,9 +459,9 @@ extension DownloadService { // MARK: - DownloadProcesserDelegate Methods extension DownloadService: DownloadProcessorDelegate { - func downloadProcessor(_ processor: DownloadProcessor, downloadModelForDownloadWithId downloadId: UUID) -> DownloadProcessorModel? { + func downloadProcessor(_ processor: DownloadProcessor, downloadModelForDownloadWithID downloadID: UUID) -> DownloadProcessorModel? { do { - return try persistenceStore.download(withId: downloadId) + return try persistenceStore.download(withID: downloadID) } catch { Failure .loadFromPersistentStore(from: String(describing: type(of: self)), reason: "Error finding download: \(error)") @@ -470,13 +470,13 @@ extension DownloadService: DownloadProcessorDelegate { } } - func downloadProcessor(_ processor: DownloadProcessor, didStartDownloadWithId downloadId: UUID) { - transitionDownload(withID: downloadId, to: .inProgress) + func downloadProcessor(_ processor: DownloadProcessor, didStartDownloadWithID downloadID: UUID) { + transitionDownload(withID: downloadID, to: .inProgress) } - func downloadProcessor(_ processor: DownloadProcessor, downloadWithId downloadId: UUID, didUpdateProgress progress: Double) { + func downloadProcessor(_ processor: DownloadProcessor, downloadWithID downloadID: UUID, didUpdateProgress progress: Double) { do { - try persistenceStore.updateDownload(withId: downloadId, withProgress: progress) + try persistenceStore.updateDownload(withID: downloadID, withProgress: progress) } catch { Failure .saveToPersistentStore(from: String(describing: type(of: self)), reason: "Unable to update progress on download: \(error)") @@ -484,15 +484,15 @@ extension DownloadService: DownloadProcessorDelegate { } } - func downloadProcessor(_ processor: DownloadProcessor, didFinishDownloadWithId downloadId: UUID) { - transitionDownload(withID: downloadId, to: .complete) + func downloadProcessor(_ processor: DownloadProcessor, didFinishDownloadWithID downloadID: UUID) { + transitionDownload(withID: downloadID, to: .complete) } - func downloadProcessor(_ processor: DownloadProcessor, didCancelDownloadWithId downloadId: UUID) { + func downloadProcessor(_ processor: DownloadProcessor, didCancelDownloadWithID downloadID: UUID) { do { - if try !persistenceStore.deleteDownload(withId: downloadId) { + if try !persistenceStore.deleteDownload(withID: downloadID) { Failure - .deleteFromPersistentStore(from: String(describing: type(of: self)), reason: "Unable to delete download: \(downloadId)") + .deleteFromPersistentStore(from: String(describing: type(of: self)), reason: "Unable to delete download: \(downloadID)") .log() } } catch { @@ -502,8 +502,8 @@ extension DownloadService: DownloadProcessorDelegate { } } - func downloadProcessor(_ processor: DownloadProcessor, downloadWithId downloadId: UUID, didFailWithError error: Error) { - transitionDownload(withID: downloadId, to: .error) + func downloadProcessor(_ processor: DownloadProcessor, downloadWithID downloadID: UUID, didFailWithError error: Error) { + transitionDownload(withID: downloadID, to: .error) Failure .saveToPersistentStore(from: String(describing: type(of: self)), reason: "DownloadDidFailWithError: \(error)") .log() @@ -511,7 +511,7 @@ extension DownloadService: DownloadProcessorDelegate { private func transitionDownload(withID id: UUID, to state: Download.State) { do { - try persistenceStore.transitionDownload(withId: id, to: state) + try persistenceStore.transitionDownload(withID: id, to: state) } catch { Failure .saveToPersistentStore(from: String(describing: type(of: self)), reason: "Unable to transition download: \(error)") diff --git a/Emitron/Emitron/Models/Bookmark.swift b/Emitron/Emitron/Models/Bookmark.swift index dc4aa304..537236a7 100644 --- a/Emitron/Emitron/Models/Bookmark.swift +++ b/Emitron/Emitron/Models/Bookmark.swift @@ -31,13 +31,13 @@ import struct Foundation.Date struct Bookmark: Codable { var id: Int var createdAt: Date - var contentId: Int + var contentID: Int } extension Bookmark: Equatable { static func == (lhs: Bookmark, rhs: Bookmark) -> Bool { lhs.id == rhs.id && lhs.createdAt.equalEnough(to: rhs.createdAt) && - lhs.contentId == rhs.contentId + lhs.contentID == rhs.contentID } } diff --git a/Emitron/Emitron/Models/Content.swift b/Emitron/Emitron/Models/Content.swift index 77824a2d..537af440 100644 --- a/Emitron/Emitron/Models/Content.swift +++ b/Emitron/Emitron/Models/Content.swift @@ -44,7 +44,7 @@ struct Content: Codable { var cardArtworkURL: URL? var technologyTriple: String var contributors: String - var groupId: Int? + var groupID: Int? var ordinal: Int? } @@ -66,7 +66,7 @@ extension Content: Equatable { lhs.cardArtworkURL == rhs.cardArtworkURL && lhs.technologyTriple == rhs.technologyTriple && lhs.contributors == rhs.contributors && - lhs.groupId == rhs.groupId + lhs.groupID == rhs.groupID } } @@ -87,7 +87,7 @@ extension Content { cardArtworkURL: other.cardArtworkURL, technologyTriple: other.technologyTriple, contributors: other.contributors, - groupId: other.groupId ?? groupId, + groupID: other.groupID ?? groupID, ordinal: other.ordinal) } } diff --git a/Emitron/Emitron/Models/ContentCategory.swift b/Emitron/Emitron/Models/ContentCategory.swift index b7d90b3c..51879fc2 100644 --- a/Emitron/Emitron/Models/ContentCategory.swift +++ b/Emitron/Emitron/Models/ContentCategory.swift @@ -28,6 +28,6 @@ struct ContentCategory: Equatable, Codable { var id: Int64? - var contentId: Int - var categoryId: Int + var contentID: Int + var categoryID: Int } diff --git a/Emitron/Emitron/Models/ContentDomain.swift b/Emitron/Emitron/Models/ContentDomain.swift index dc12155d..1b389c08 100644 --- a/Emitron/Emitron/Models/ContentDomain.swift +++ b/Emitron/Emitron/Models/ContentDomain.swift @@ -28,6 +28,6 @@ struct ContentDomain: Equatable, Codable { var id: Int64? - var contentId: Int - var domainId: Int + var contentID: Int + var domainID: Int } diff --git a/Emitron/Emitron/Models/DataCache.swift b/Emitron/Emitron/Models/DataCache.swift index bff51dd1..75d606a7 100644 --- a/Emitron/Emitron/Models/DataCache.swift +++ b/Emitron/Emitron/Models/DataCache.swift @@ -64,19 +64,19 @@ final class DataCache: ObservableObject { extension DataCache { func update(from cacheUpdate: DataCacheUpdate) { - cacheUpdate.bookmarks.forEach { bookmarks[$0.contentId] = $0 } + cacheUpdate.bookmarks.forEach { bookmarks[$0.contentID] = $0 } // Have to do a special update for content—since some API endpoints won't include group info cacheUpdate.contents.forEach { contents[$0.id] = contents[$0.id]?.update(from: $0) ?? $0 } - cacheUpdate.progressions.forEach { progressions[$0.contentId] = $0 } + cacheUpdate.progressions.forEach { progressions[$0.contentID] = $0 } cacheUpdate.groups.forEach { groupIndexedGroups[$0.id] = $0 } // swiftlint:disable generic_type_name - func mergeWithCacheUpdate( - _ dictionary: inout [ Int: [contentId] ], - _ getContentId: (DataCacheUpdate) -> [contentId] + func mergeWithCacheUpdate( + _ dictionary: inout [ Int: [contentID] ], + _ getContentID: (DataCacheUpdate) -> [contentID] ) { dictionary.merge( - .init(grouping: getContentId(cacheUpdate), by: \.contentId), + .init(grouping: getContentID(cacheUpdate), by: \.contentID), uniquingKeysWith: { $1 } ) } @@ -85,15 +85,15 @@ extension DataCache { mergeWithCacheUpdate(&contentDomains, \.contentDomains) mergeWithCacheUpdate(&contentIndexedGroups, \.groups) - cacheUpdate.bookmarkDeletionContentIds.forEach { bookmarks.removeValue(forKey: $0) } - cacheUpdate.progressionDeletionContentIds.forEach { progressions.removeValue(forKey: $0) } + cacheUpdate.bookmarkDeletionContentIDs.forEach { bookmarks.removeValue(forKey: $0) } + cacheUpdate.progressionDeletionContentIDs.forEach { progressions.removeValue(forKey: $0) } // Send cache invalidations - if !cacheUpdate.bookmarks.isEmpty || !cacheUpdate.bookmarkDeletionContentIds.isEmpty { + if !cacheUpdate.bookmarks.isEmpty || !cacheUpdate.bookmarkDeletionContentIDs.isEmpty { cacheWasInvalidated.send(.bookmarks) } - if !cacheUpdate.progressions.isEmpty || !cacheUpdate.progressionDeletionContentIds.isEmpty { + if !cacheUpdate.progressions.isEmpty || !cacheUpdate.progressionDeletionContentIDs.isEmpty { cacheWasInvalidated.send(.progressions) } @@ -101,47 +101,47 @@ extension DataCache { } } -/// A type with a `contentId` property. -private protocol contentId { - var contentId: Int { get } +/// A type with a `contentID` property. +private protocol contentID { + var contentID: Int { get } } -extension ContentCategory: contentId { } -extension ContentDomain: contentId { } -extension Group: contentId { } +extension ContentCategory: contentID { } +extension ContentDomain: contentID { } +extension Group: contentID { } extension DataCache { - func contentSummaryState(for contentIds: [Int]) -> AnyPublisher<[CachedContentSummaryState], Error> { + func contentSummaryState(for contentIDs: [Int]) -> AnyPublisher<[CachedContentSummaryState], Error> { objectDidChange .tryMap { _ in - try contentIds.map { contentId in - try self.cachedContentSummaryState(for: contentId) + try contentIDs.map { contentID in + try self.cachedContentSummaryState(for: contentID) } } .removeDuplicates() .eraseToAnyPublisher() } - func contentSummaryState(for contentId: Int) -> AnyPublisher { + func contentSummaryState(for contentID: Int) -> AnyPublisher { objectDidChange .tryMap { _ in - try self.cachedContentSummaryState(for: contentId) + try self.cachedContentSummaryState(for: contentID) } .removeDuplicates() .eraseToAnyPublisher() } - func childContentsState(for contentId: Int) -> AnyPublisher { + func childContentsState(for contentID: Int) -> AnyPublisher { objectDidChange.tryMap { _ in - try self.cachedChildContentsState(for: contentId) + try self.cachedChildContentsState(for: contentID) } .removeDuplicates() .eraseToAnyPublisher() } - func contentDynamicState(for contentId: Int) -> AnyPublisher { + func contentDynamicState(for contentID: Int) -> AnyPublisher { objectDidChange.tryMap { _ in - self.cachedDynamicContentState(for: contentId) + self.cachedDynamicContentState(for: contentID) } .removeDuplicates() .eraseToAnyPublisher() @@ -151,22 +151,22 @@ extension DataCache { contents[id] } - func progression(for contentId: Int) -> Progression? { - progressions[contentId] + func progression(for contentID: Int) -> Progression? { + progressions[contentID] } - func bookmark(for contentId: Int) -> Bookmark? { - bookmarks[contentId] + func bookmark(for contentID: Int) -> Bookmark? { + bookmarks[contentID] } - func parentContent(for contentId: Int) -> Content? { - guard let content = content(with: contentId) else { return nil } + func parentContent(for contentID: Int) -> Content? { + guard let content = content(with: contentID) else { return nil } return try? parentContent(for: content) } - func childProgress(for contentId: Int) -> (total: Int, completed: Int)? { - guard let content = content(with: contentId) else { return nil } + func childProgress(for contentID: Int) -> (total: Int, completed: Int)? { + guard let content = content(with: contentID) else { return nil } guard let childContents = try? childContents(for: content) else { return nil } @@ -179,14 +179,14 @@ extension DataCache { } extension DataCache { - private func cachedContentSummaryState(for contentId: Int) throws -> CachedContentSummaryState { - guard let content = contents[contentId], - let contentDomains = contentDomains[contentId] + private func cachedContentSummaryState(for contentID: Int) throws -> CachedContentSummaryState { + guard let content = contents[contentID], + let contentDomains = contentDomains[contentID] else { throw DataCacheError.cacheMiss } - let contentCategories = self.contentCategories[contentId] ?? [] + let contentCategories = self.contentCategories[contentID] ?? [] return try CachedContentSummaryState( content: content, @@ -196,8 +196,8 @@ extension DataCache { ) } - private func cachedChildContentsState(for contentId: Int) throws -> CachedChildContentsState { - guard let content = contents[contentId] else { + private func cachedChildContentsState(for contentID: Int) throws -> CachedChildContentsState { + guard let content = contents[contentID] else { throw DataCacheError.cacheMiss } @@ -205,11 +205,11 @@ extension DataCache { return CachedChildContentsState(contents: [], groups: []) } - let groups = contentIndexedGroups[contentId] ?? [] - let groupIds = groups.map(\.id) + let groups = contentIndexedGroups[contentID] ?? [] + let groupIDs = groups.map(\.id) let childContents = contents.values.filter { content in - guard let groupId = content.groupId else { return false } - return groupIds.contains(groupId) + guard let groupID = content.groupID else { return false } + return groupIDs.contains(groupID) } if childContents.isEmpty { @@ -222,13 +222,13 @@ extension DataCache { ) } - func cachedContentPersistableState(for contentId: Int) throws -> ContentPersistableState { - guard let content = contents[contentId] else { + func cachedContentPersistableState(for contentID: Int) throws -> ContentPersistableState { + guard let content = contents[contentID] else { throw DataCacheError.cacheMiss } - let contentDomains = self.contentDomains[contentId] ?? [] - let contentCategories = self.contentCategories[contentId] ?? [] + let contentDomains = self.contentDomains[contentID] ?? [] + let contentCategories = self.contentCategories[contentID] ?? [] if content.contentType != .episode { if contentDomains.isEmpty { @@ -236,13 +236,13 @@ extension DataCache { } } - let bookmark = self.bookmarks[contentId] - let progression = self.progressions[contentId] - let groups = self.contentIndexedGroups[contentId] ?? [] - let groupIds = groups.map(\.id) + let bookmark = self.bookmarks[contentID] + let progression = self.progressions[contentID] + let groups = self.contentIndexedGroups[contentID] ?? [] + let groupIDs = groups.map(\.id) let childContents = self.contents.values.filter { content in - guard let groupId = content.groupId else { return false } - return groupIds.contains(groupId) + guard let groupID = content.groupID else { return false } + return groupIDs.contains(groupID) } return try ContentPersistableState( @@ -257,8 +257,8 @@ extension DataCache { ) } - func videoPlaylist(for contentId: Int) throws -> [CachedVideoPlaybackState] { - guard let content = contents[contentId] else { + func videoPlaylist(for contentID: Int) throws -> [CachedVideoPlaybackState] { + guard let content = contents[contentID] else { throw DataCacheError.cacheMiss } @@ -294,19 +294,19 @@ extension DataCache { ) } - private func cachedDynamicContentState(for contentId: Int) -> CachedDynamicContentState { + private func cachedDynamicContentState(for contentID: Int) -> CachedDynamicContentState { CachedDynamicContentState( - progression: progressions[contentId], - bookmark: bookmarks[contentId] + progression: progressions[contentID], + bookmark: bookmarks[contentID] ) } private func parentContent(for content: Content) throws -> Content? { - guard let groupId = content.groupId else { return nil } - guard let group = groupIndexedGroups[groupId] + guard let groupID = content.groupID else { return nil } + guard let group = groupIndexedGroups[groupID] else { throw DataCacheError.cacheMiss } - return contents[group.contentId] + return contents[group.contentID] } private func childContents(for content: Content) throws -> [Content] { @@ -314,10 +314,10 @@ extension DataCache { throw DataCacheError.cacheMiss } - let groupIds = groups.map(\.id) + let groupIDs = groups.map(\.id) return contents.values.filter { - guard let groupId = $0.groupId else { return false } - return groupIds.contains(groupId) + guard let groupID = $0.groupID else { return false } + return groupIDs.contains(groupID) } .sorted { guard let lhsOrdinal = $0.ordinal, let rhsOrdinal = $1.ordinal else { return true } diff --git a/Emitron/Emitron/Models/Download.swift b/Emitron/Emitron/Models/Download.swift index ad1c943a..766a86d7 100644 --- a/Emitron/Emitron/Models/Download.swift +++ b/Emitron/Emitron/Models/Download.swift @@ -49,7 +49,7 @@ struct Download: Codable { var remoteURL: URL? var progress: Double = 0 var state: State - var contentId: Int + var contentID: Int var ordinal: Int = 0 // We copy this from the Content, and it is used to sort the queue var localURL: URL? { @@ -82,7 +82,7 @@ extension Download: Equatable { lhs.remoteURL == rhs.remoteURL && lhs.progress == rhs.progress && lhs.state == rhs.state && - lhs.contentId == rhs.contentId && + lhs.contentID == rhs.contentID && lhs.ordinal == rhs.ordinal && lhs.requestedAt.equalEnough(to: rhs.requestedAt) && ((lhs.lastValidatedAt == nil && rhs.lastValidatedAt == nil) || lhs.lastValidatedAt!.equalEnough(to: rhs.lastValidatedAt!)) @@ -99,7 +99,7 @@ extension Download { remoteURL: nil, progress: 0, state: .pending, - contentId: content.id, + contentID: content.id, ordinal: content.ordinal ?? 0) } } diff --git a/Emitron/Emitron/Models/Group.swift b/Emitron/Emitron/Models/Group.swift index 90e9fc2f..50ffebdc 100644 --- a/Emitron/Emitron/Models/Group.swift +++ b/Emitron/Emitron/Models/Group.swift @@ -31,5 +31,5 @@ struct Group: Equatable, Codable { var name: String var description: String? var ordinal: Int - var contentId: Int + var contentID: Int } diff --git a/Emitron/Emitron/Models/Progression.swift b/Emitron/Emitron/Models/Progression.swift index 7d6f8b1f..08b84d13 100644 --- a/Emitron/Emitron/Models/Progression.swift +++ b/Emitron/Emitron/Models/Progression.swift @@ -34,7 +34,7 @@ struct Progression: Codable { var progress: Int var createdAt: Date var updatedAt: Date - var contentId: Int + var contentID: Int } extension Progression: Equatable { @@ -44,7 +44,7 @@ extension Progression: Equatable { lhs.progress == rhs.progress && lhs.createdAt.equalEnough(to: rhs.createdAt) && lhs.updatedAt.equalEnough(to: rhs.updatedAt) && - lhs.contentId == rhs.contentId + lhs.contentID == rhs.contentID } } @@ -81,7 +81,7 @@ extension Progression { progress: progress, createdAt: Date(), updatedAt: Date(), - contentId: content.id + contentID: content.id ) } } diff --git a/Emitron/Emitron/Models/SyncRequest.swift b/Emitron/Emitron/Models/SyncRequest.swift index f9d69732..95b6903c 100644 --- a/Emitron/Emitron/Models/SyncRequest.swift +++ b/Emitron/Emitron/Models/SyncRequest.swift @@ -50,8 +50,8 @@ struct SyncRequest: Equatable, Codable { } var id: Int64? - var contentId: Int - var associatedRecordId: Int? + var contentID: Int + var associatedRecordID: Int? var category: Category var type: Synchronisation var date: Date diff --git a/Emitron/Emitron/Models/User.swift b/Emitron/Emitron/Models/User.swift index d38efa8d..25ab3e09 100644 --- a/Emitron/Emitron/Models/User.swift +++ b/Emitron/Emitron/Models/User.swift @@ -31,7 +31,7 @@ import struct Foundation.URL public struct User: Equatable, Codable { // MARK: - Properties - public let externalId: String + public let externalID: String public let email: String public let username: String public let avatarURL: URL @@ -64,7 +64,7 @@ public struct User: Equatable, Codable { // MARK: - Initializers init?(dictionary: [String: String]) { guard - let externalId = dictionary["external_id"], + let externalID = dictionary["external_id"], let email = dictionary["email"], let username = dictionary["username"], let avatarURLString = dictionary["avatar_url"], @@ -74,7 +74,7 @@ public struct User: Equatable, Codable { else { return nil } - self.externalId = externalId + self.externalID = externalID self.email = email self.username = username self.avatarURL = avatarURL @@ -84,7 +84,7 @@ public struct User: Equatable, Codable { } private init(user: User, permissions: [Permission]) { - externalId = user.externalId + externalID = user.externalID email = user.email username = user.username avatarURL = user.avatarURL diff --git a/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift b/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift index c4e3f1a6..90ea6ff5 100644 --- a/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift +++ b/Emitron/Emitron/Networking/Adapters/DataCacheUpdate.swift @@ -39,8 +39,8 @@ struct DataCacheUpdate { let contentDomains: [ContentDomain] let relationships: [EntityRelationship] - let bookmarkDeletionContentIds: [Int] - let progressionDeletionContentIds: [Int] + let bookmarkDeletionContentIDs: [Int] + let progressionDeletionContentIDs: [Int] static func loadFrom(document: JSONAPIDocument) throws -> DataCacheUpdate { let data = try DataCacheUpdate(resources: document.data) @@ -58,8 +58,8 @@ struct DataCacheUpdate { contentCategories: [ContentCategory] = [], contentDomains: [ContentDomain] = [], relationships: [EntityRelationship] = [], - bookmarkDeletionContentIds: [Int] = [], - progressionDeletionContentIds: [Int] = [] + bookmarkDeletionContentIDs: [Int] = [], + progressionDeletionContentIDs: [Int] = [] ) { self.contents = contents self.bookmarks = bookmarks @@ -70,8 +70,8 @@ struct DataCacheUpdate { self.contentCategories = contentCategories self.contentDomains = contentDomains self.relationships = relationships - self.bookmarkDeletionContentIds = bookmarkDeletionContentIds - self.progressionDeletionContentIds = progressionDeletionContentIds + self.bookmarkDeletionContentIDs = bookmarkDeletionContentIDs + self.progressionDeletionContentIDs = progressionDeletionContentIDs } init(resources: [JSONAPIResource], relationships jsonEntityRelationships: [JSONEntityRelationships] = []) throws { @@ -98,8 +98,8 @@ struct DataCacheUpdate { contentDomains = try ContentDomainAdapter.process(relationships: relationships) self.relationships = relationships - bookmarkDeletionContentIds = [] - progressionDeletionContentIds = [] + bookmarkDeletionContentIDs = [] + progressionDeletionContentIDs = [] } func merged(with other: DataCacheUpdate) -> DataCacheUpdate { @@ -116,12 +116,12 @@ struct DataCacheUpdate { private static func relationships(from resources: [JSONAPIResource], with additionalRelationships: [JSONEntityRelationships]) -> [EntityRelationship] { var relationshipsToReturn = additionalRelationships.flatMap { entityRelationship -> [EntityRelationship] in - guard let entityId = entityRelationship.entity else { return [] } - return entityRelationships(from: entityRelationship.jsonRelationships, fromEntity: entityId) + guard let entityID = entityRelationship.entity else { return [] } + return entityRelationships(from: entityRelationship.jsonRelationships, fromEntity: entityID) } relationshipsToReturn += resources.flatMap { resource -> [EntityRelationship] in - guard let resourceEntityId = resource.entityID else { return [] } - return entityRelationships(from: resource.relationships, fromEntity: resourceEntityId) + guard let resourceEntityID = resource.entityID else { return [] } + return entityRelationships(from: resource.relationships, fromEntity: resourceEntityID) } return relationshipsToReturn } diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/BookmarkAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/BookmarkAdapter.swift index 012e3bb3..2cca9901 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/BookmarkAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/BookmarkAdapter.swift @@ -39,13 +39,13 @@ struct BookmarkAdapter: EntityAdapter { } guard let content = resource.relationships.first(where: { $0.type == "content" }), - let contentId = content.data.first?.id + let contentID = content.data.first?.id else { throw EntityAdapterError.invalidOrMissingRelationships } return Bookmark(id: resource.id, createdAt: createdAt, - contentId: contentId) + contentID: contentID) } } diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentAdapter.swift index adb75f9d..ec3cb881 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentAdapter.swift @@ -67,7 +67,7 @@ struct ContentAdapter: EntityAdapter { && relationship.to.id == resource.id && relationship.to.type == .content } - let groupId = group?.from.id + let groupID = group?.from.id return Content(id: resource.id, uri: uri, @@ -84,7 +84,7 @@ struct ContentAdapter: EntityAdapter { cardArtworkURL: cardArtworkURL, technologyTriple: technologyTriple, contributors: contributors, - groupId: groupId, + groupID: groupID, ordinal: resource.attributes["ordinal"] as? Int) } } diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentCategoryAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentCategoryAdapter.swift index cbeaa570..ea838182 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentCategoryAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentCategoryAdapter.swift @@ -34,8 +34,8 @@ struct ContentCategoryAdapter: EntityAdapter { } .map { ContentCategory(id: nil, - contentId: $0.from.id, - categoryId: $0.to.id) + contentID: $0.from.id, + categoryID: $0.to.id) } } } diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentDomainAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentDomainAdapter.swift index 0df2533d..58025dc7 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentDomainAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ContentDomainAdapter.swift @@ -34,8 +34,8 @@ struct ContentDomainAdapter: EntityAdapter { } .map { ContentDomain(id: nil, - contentId: $0.from.id, - domainId: $0.to.id) + contentID: $0.from.id, + domainID: $0.to.id) } } } diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/GroupAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/GroupAdapter.swift index 6d6acc85..9fa71b34 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/GroupAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/GroupAdapter.swift @@ -43,12 +43,12 @@ struct GroupAdapter: EntityAdapter { }) else { throw EntityAdapterError.invalidOrMissingRelationships } - let contentId = parentContent.from.id + let contentID = parentContent.from.id return Group(id: resource.id, name: name, description: resource.attributes["description"] as? String, ordinal: ordinal, - contentId: contentId) + contentID: contentID) } } diff --git a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ProgressionAdapter.swift b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ProgressionAdapter.swift index 428448ae..d1ede038 100644 --- a/Emitron/Emitron/Networking/Adapters/EntityAdapters/ProgressionAdapter.swift +++ b/Emitron/Emitron/Networking/Adapters/EntityAdapters/ProgressionAdapter.swift @@ -47,7 +47,7 @@ struct ProgressionAdapter: EntityAdapter { } guard let content = resource.relationships.first(where: { $0.type == "content" }), - let contentId = content.data.first?.id + let contentID = content.data.first?.id else { throw EntityAdapterError.invalidOrMissingRelationships } @@ -57,6 +57,6 @@ struct ProgressionAdapter: EntityAdapter { progress: progress, createdAt: createdAt, updatedAt: updatedAt, - contentId: contentId) + contentID: contentID) } } diff --git a/Emitron/Emitron/Networking/Requests/Parameters.swift b/Emitron/Emitron/Networking/Requests/Parameters.swift index 06dae7e4..f4419ccf 100644 --- a/Emitron/Emitron/Networking/Requests/Parameters.swift +++ b/Emitron/Emitron/Networking/Requests/Parameters.swift @@ -74,7 +74,7 @@ enum ParameterFilterValue { case domainTypes(types: [(id: Int, name: String, sortOrdinal: Int)]) // An array of numerical IDs of the domains you are interested in. case categoryTypes(types: [(id: Int, name: String, sortOrdinal: Int)]) // An array of numberical IDs of the categories you are interested in. case difficulties([ContentDifficulty]) // An array populated with ContentDifficulty options - case contentIds(ids: [Int]) + case contentIDs(ids: [Int]) case queryString(string: String) case completionStatus(status: CompletionStatus) case subscriptionPlans(plans: [ContentSubscriptionPlan]) @@ -89,7 +89,7 @@ enum ParameterFilterValue { return "category_ids" case .difficulties: return "difficulties" - case .contentIds: + case .contentIDs: return "content_ids" case .queryString: return "q" @@ -119,7 +119,7 @@ enum ParameterFilterValue { return types.map { (displayName: $0.name, requestValue: "\($0.id)", ordinal: $0.sortOrdinal) } case .difficulties(difficulties: let difficulties): return difficulties.map { (displayName: $0.displayString, requestValue: $0.requestValue, ordinal: $0.sortOrdinal) } - case .contentIds(ids: let ids): + case .contentIDs(ids: let ids): return ids.map { (displayName: "\($0)", requestValue: "\($0)", ordinal: 0) } case .queryString(string: let str): return [(displayName: str, requestValue: str, ordinal: 0)] @@ -134,7 +134,7 @@ enum ParameterFilterValue { switch self { case .queryString: return true - case .contentIds, + case .contentIDs, .contentTypes, .domainTypes, .difficulties, @@ -151,7 +151,7 @@ enum ParameterFilterValue { return str case .completionStatus(let status): return status.rawValue - case .contentIds, + case .contentIDs, .contentTypes, .domainTypes, .difficulties, diff --git a/Emitron/Emitron/Networking/Requests/ProgressionsRequest.swift b/Emitron/Emitron/Networking/Requests/ProgressionsRequest.swift index 30b8d786..1256fe54 100644 --- a/Emitron/Emitron/Networking/Requests/ProgressionsRequest.swift +++ b/Emitron/Emitron/Networking/Requests/ProgressionsRequest.swift @@ -66,7 +66,7 @@ enum ProgressionUpdateData { } protocol ProgressionUpdate { - var contentId: Int { get } + var contentID: Int { get } var data: ProgressionUpdateData { get } var updatedAt: Date { get } } @@ -83,7 +83,7 @@ struct UpdateProgressionsRequest: Request { [ "type": "progressions", "attributes": [ - "content_id": update.contentId, + "content_id": update.contentID, "updated_at": update.updatedAt.iso8601, update.data.jsonAttribute.key: update.data.jsonAttribute.value ] diff --git a/Emitron/Emitron/Networking/Requests/WatchStatsRequest.swift b/Emitron/Emitron/Networking/Requests/WatchStatsRequest.swift index 3885ab56..61554148 100644 --- a/Emitron/Emitron/Networking/Requests/WatchStatsRequest.swift +++ b/Emitron/Emitron/Networking/Requests/WatchStatsRequest.swift @@ -29,7 +29,7 @@ import Foundation protocol WatchStat { - var contentId: Int { get } + var contentID: Int { get } var secondsWatched: Int { get } var dateWatched: Date { get } } @@ -46,7 +46,7 @@ struct WatchStatsUpdateRequest: Request { [ "type": "watch_stats", "attributes": [ - "content_id": stat.contentId, + "content_id": stat.contentID, "seconds": stat.secondsWatched, "watched_on": stat.dateWatched.iso8601 ] diff --git a/Emitron/Emitron/Persistence/Models/Content+Persistence.swift b/Emitron/Emitron/Persistence/Models/Content+Persistence.swift index d4a4afea..a462c25c 100644 --- a/Emitron/Emitron/Persistence/Models/Content+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/Content+Persistence.swift @@ -45,7 +45,7 @@ extension Content: FetchableRecord, TableRecord, PersistableRecord { static let cardArtworkURL = Column("cardArtworkURL") static let technologyTriple = Column("technologyTriple") static let contributors = Column("contributors") - static let groupId = Column("groupId") + static let groupID = Column("groupId") static let ordinal = Column("ordinal") } } diff --git a/Emitron/Emitron/Persistence/Models/Download+Persistence.swift b/Emitron/Emitron/Persistence/Models/Download+Persistence.swift index 433b7da5..b783a23b 100644 --- a/Emitron/Emitron/Persistence/Models/Download+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/Download+Persistence.swift @@ -37,7 +37,7 @@ extension Download: TableRecord, FetchableRecord, MutablePersistableRecord { static let remoteURL = Column("remoteURL") static let progress = Column("progress") static let state = Column("state") - static let contentId = Column("contentId") + static let contentID = Column("contentId") static let ordinal = Column("ordinal") } } diff --git a/Emitron/Emitron/Persistence/Models/Group+Persistence.swift b/Emitron/Emitron/Persistence/Models/Group+Persistence.swift index 9d4d3d2d..36d625ef 100644 --- a/Emitron/Emitron/Persistence/Models/Group+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/Group+Persistence.swift @@ -36,7 +36,7 @@ extension Group { static let name = Column("name") static let description = Column("description") static let ordinal = Column("ordinal") - static let contentId = Column("contentId") + static let contentID = Column("contentID") } } diff --git a/Emitron/Emitron/Persistence/Models/SyncRequest+Persistence.swift b/Emitron/Emitron/Persistence/Models/SyncRequest+Persistence.swift index 0a3091fa..036d6425 100644 --- a/Emitron/Emitron/Persistence/Models/SyncRequest+Persistence.swift +++ b/Emitron/Emitron/Persistence/Models/SyncRequest+Persistence.swift @@ -33,8 +33,8 @@ extension SyncRequest: FetchableRecord, TableRecord, PersistableRecord { } extension SyncRequest { enum Columns { static let id = Column("id") - static let contentId = Column("contentId") - static let associatedRecordId = Column("associatedRecordId") + static let contentID = Column("contentId") + static let associatedRecordID = Column("associatedRecordId") static let category = Column("category") static let type = Column("type") static let date = Column("date") diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Categories.swift b/Emitron/Emitron/Persistence/PersistenceStore+Categories.swift index a43cd538..8b3503f3 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Categories.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Categories.swift @@ -40,10 +40,10 @@ extension PersistenceStore { } /// Get all the **Category** objects with the given keys - func categories(with categoryIds: [Int]) throws -> [Category] { + func categories(with categoryIDs: [Int]) throws -> [Category] { try db.read { db in try Category - .fetchAll(db, keys: categoryIds) + .fetchAll(db, keys: categoryIDs) .sorted { $0.ordinal <= $1.ordinal } } } diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Domains.swift b/Emitron/Emitron/Persistence/PersistenceStore+Domains.swift index e6ecc637..b78f2548 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Domains.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Domains.swift @@ -40,10 +40,10 @@ extension PersistenceStore { } /// Get all the **Domain** objects with the given keys - func domains(with domainIds: [Int]) throws -> [Domain] { + func domains(with domainIDs: [Int]) throws -> [Domain] { try db.read { db in try Domain - .fetchAll(db, keys: domainIds) + .fetchAll(db, keys: domainIDs) .sorted { $0.level.rawValue <= $1.level.rawValue } } } diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift index 8d0b97bb..0cb56ffb 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift @@ -50,10 +50,10 @@ extension PersistenceStore { } extension PersistenceStore { - func download(for contentId: Int) -> DatabasePublishers.Value { + func download(for contentID: Int) -> DatabasePublishers.Value { ValueObservation.tracking { db -> Download? in let request = Download - .filter(Download.Columns.contentId == contentId) + .filter(Download.Columns.contentID == contentID) return try Download.fetchOne(db, request) } .publisher(in: db) @@ -72,7 +72,7 @@ extension PersistenceStore { .including(all: Content.childContents) guard let contents = try ChildContentContainer.fetchOne(db, contentRequest) else { return nil } let groups = try Group - .filter(Group.Columns.contentId == id) + .filter(Group.Columns.contentID == id) .fetchAll(db) return ChildContentsState(contents: contents.contents, groups: groups) } @@ -131,18 +131,18 @@ extension PersistenceStore { /// Return a single `Download` from its id /// - Parameter id: The UUID of the download to find - func download(withId id: UUID) throws -> Download? { + func download(withID id: UUID) throws -> Download? { try db.read { db in try Download.fetchOne(db, key: id) } } /// Return a single `Download` from its content id - /// - Parameter contentId: The ID of the item of content this download refers to - func download(forContentId contentId: Int) throws -> Download? { + /// - Parameter contentID: The ID of the item of content this download refers to + func download(forContentID contentID: Int) throws -> Download? { try db.read { db in try Download - .filter(Download.Columns.contentId == contentId) + .filter(Download.Columns.contentID == contentID) .fetchOne(db) } } @@ -172,10 +172,10 @@ extension PersistenceStore { } /// Summary download stats for the children of the given collection - /// - Parameter contentId: ID representing an item of `Content` with `ContentType` of `.collection` - func collectionDownloadSummary(forContentId contentId: Int) throws -> CollectionDownloadSummary { + /// - Parameter contentID: ID representing an item of `Content` with `ContentType` of `.collection` + func collectionDownloadSummary(forContentID contentID: Int) throws -> CollectionDownloadSummary { try db.read { db in - guard let content = try Content.fetchOne(db, key: contentId), + guard let content = try Content.fetchOne(db, key: contentID), content.contentType == .collection else { throw PersistenceStoreError.argumentError } @@ -199,7 +199,7 @@ extension PersistenceStore { /// - Parameters: /// - id: The UUID of the download to transition /// - state: The new `Download.State` to transition to. - func transitionDownload(withId id: UUID, to state: Download.State) throws { + func transitionDownload(withID id: UUID, to state: Download.State) throws { try db.write { db in if var download = try Download.fetchOne(db, key: id) { try download.updateChanges(db) { @@ -252,7 +252,7 @@ extension PersistenceStore { /// Update the collection download to match the current status of its children /// - Parameter collectionDownload: A `Download` that is associated with a collection `Content` private func updateCollectionDownloadState(collectionDownload: Download) throws { - let downloadSummary = try collectionDownloadSummary(forContentId: collectionDownload.contentId) + let downloadSummary = try collectionDownloadSummary(forContentID: collectionDownload.contentID) var download = collectionDownload _ = try db.write { db in @@ -271,7 +271,7 @@ extension PersistenceStore { /// - Parameters: /// - id: The UUID of the download to update /// - progress: The new value of progress (0–1) - func updateDownload(withId id: UUID, withProgress progress: Double) throws { + func updateDownload(withID id: UUID, withProgress progress: Double) throws { try db.write { db in if var download = try Download.fetchOne(db, key: id) { try download.updateChanges(db) { @@ -292,7 +292,7 @@ extension PersistenceStore { /// Delete a download /// - Parameter id: The UUID of the download to delete - func deleteDownload(withId id: UUID) throws -> Bool { + func deleteDownload(withID id: UUID) throws -> Bool { try db.write { db in if let download = try Download.fetchOne(db, key: id) { let parentDownload = try download.parentDownload.fetchOne(db) @@ -308,7 +308,7 @@ extension PersistenceStore { /// Delete the downloads without selected IDs. /// - Parameter ids: Array of UUIDs for the downloads to delete - func deleteDownloads(withIds ids: [UUID]) -> Future { + func deleteDownloads(withIDs ids: [UUID]) -> Future { Future { promise in self.workerQueue.async { [weak self] in guard let self = self else { return } @@ -403,7 +403,7 @@ extension PersistenceStore { private func persistContentItem(for contentPersistableState: ContentPersistableState, inDatabase db: Database, withChildren: Bool = false, withParent: Bool = false, contentLookup: ContentLookup? = nil) throws { // 1. Need to do parent first—we need foreign key - // constraints on the groupId for child content + // constraints on the groupID for child content if withParent, let parentContent = contentPersistableState.parentContent, let contentLookup = contentLookup, diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift b/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift index e575df72..6d8f9c29 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift @@ -32,11 +32,11 @@ import GRDB // MARK: - Synchronisation Request Creation extension PersistenceStore { @discardableResult - func createBookmarkSyncRequest(for contentId: Int) throws -> SyncRequest? { + func createBookmarkSyncRequest(for contentID: Int) throws -> SyncRequest? { try db.write { db in // Do we already have a bookmark request? if let syncRequest = try SyncRequest - .filter(SyncRequest.Columns.contentId == contentId) + .filter(SyncRequest.Columns.contentID == contentID) .filter(SyncRequest.Columns.category == SyncRequest.Category.bookmark.rawValue) .fetchOne(db) { try syncRequest.delete(db) @@ -44,7 +44,7 @@ extension PersistenceStore { } else { // Need to create a new one let syncRequest = SyncRequest( - contentId: contentId, + contentID: contentID, category: .bookmark, type: .createBookmark, date: Date(), @@ -57,11 +57,11 @@ extension PersistenceStore { } @discardableResult - func deleteBookmarkSyncRequest(for contentId: Int, bookmarkId: Int) throws -> SyncRequest? { + func deleteBookmarkSyncRequest(for contentID: Int, bookmarkID: Int) throws -> SyncRequest? { try db.write { db in // Do we already have a bookmark request? if let syncRequest = try SyncRequest - .filter(SyncRequest.Columns.contentId == contentId) + .filter(SyncRequest.Columns.contentID == contentID) .filter(SyncRequest.Columns.category == SyncRequest.Category.bookmark.rawValue) .fetchOne(db) { try syncRequest.delete(db) @@ -69,8 +69,8 @@ extension PersistenceStore { } else { // Need to create a new one let syncRequest = SyncRequest( - contentId: contentId, - associatedRecordId: bookmarkId, + contentID: contentID, + associatedRecordID: bookmarkID, category: .bookmark, type: .deleteBookmark, date: Date(), @@ -83,12 +83,12 @@ extension PersistenceStore { } @discardableResult - func markContentAsCompleteSyncRequest(for contentId: Int) throws -> SyncRequest { + func markContentAsCompleteSyncRequest(for contentID: Int) throws -> SyncRequest { try db.write { db in // Do we already have a progress request? let syncRequest: SyncRequest if var request = try SyncRequest - .filter(SyncRequest.Columns.contentId == contentId) + .filter(SyncRequest.Columns.contentID == contentID) .filter(SyncRequest.Columns.category == SyncRequest.Category.progress.rawValue) .fetchOne(db) { request.type = .markContentComplete @@ -97,7 +97,7 @@ extension PersistenceStore { } else { // Need to create a new one syncRequest = SyncRequest( - contentId: contentId, + contentID: contentID, category: .progress, type: .markContentComplete, date: Date(), @@ -110,12 +110,12 @@ extension PersistenceStore { } @discardableResult - func updateProgressSyncRequest(for contentId: Int, progress: Int) throws -> SyncRequest { + func updateProgressSyncRequest(for contentID: Int, progress: Int) throws -> SyncRequest { try db.write { db in // Do we already have a progress request? let syncRequest: SyncRequest if var request = try SyncRequest - .filter(SyncRequest.Columns.contentId == contentId) + .filter(SyncRequest.Columns.contentID == contentID) .filter(SyncRequest.Columns.category == SyncRequest.Category.progress.rawValue) .fetchOne(db) { request.type = .updateProgress @@ -125,7 +125,7 @@ extension PersistenceStore { } else { // Need to create a new one syncRequest = SyncRequest( - contentId: contentId, + contentID: contentID, category: .progress, type: .updateProgress, date: Date(), @@ -138,23 +138,23 @@ extension PersistenceStore { } @discardableResult - func removeProgressSyncRequest(for contentId: Int, progressionId: Int) throws -> SyncRequest { + func removeProgressSyncRequest(for contentID: Int, progressionID: Int) throws -> SyncRequest { try db.write { db in // Do we already have a progress request? let syncRequest: SyncRequest if var request = try SyncRequest - .filter(SyncRequest.Columns.contentId == contentId) + .filter(SyncRequest.Columns.contentID == contentID) .filter(SyncRequest.Columns.category == SyncRequest.Category.progress.rawValue) .fetchOne(db) { request.type = .deleteProgression - request.associatedRecordId = progressionId + request.associatedRecordID = progressionID request.date = Date() syncRequest = request } else { // Need to create a new one syncRequest = SyncRequest( - contentId: contentId, - associatedRecordId: progressionId, + contentID: contentID, + associatedRecordID: progressionID, category: .progress, type: .deleteProgression, date: Date(), @@ -167,12 +167,12 @@ extension PersistenceStore { } @discardableResult - func watchStatsSyncRequest(for contentId: Int, secondsWatched: Int) throws -> SyncRequest { + func watchStatsSyncRequest(for contentID: Int, secondsWatched: Int) throws -> SyncRequest { try db.write { db in // Do we already have a watch stats request? let syncRequest: SyncRequest if var request = try SyncRequest - .filter(SyncRequest.Columns.contentId == contentId) + .filter(SyncRequest.Columns.contentID == contentID) .filter(SyncRequest.Columns.category == SyncRequest.Category.watchStat.rawValue) .filter(SyncRequest.Columns.date == Date.topOfTheHour) .fetchOne(db) { @@ -188,7 +188,7 @@ extension PersistenceStore { } else { // Need to create a new one syncRequest = SyncRequest( - contentId: contentId, + contentID: contentID, category: .watchStat, type: .recordWatchStats, date: Date.topOfTheHour, diff --git a/Emitron/Emitron/UI/Downloads/DownloadDeletionConfirmation+Alert.swift b/Emitron/Emitron/UI/Downloads/DownloadDeletionConfirmation+Alert.swift index 27db4cf6..dfd7da45 100644 --- a/Emitron/Emitron/UI/Downloads/DownloadDeletionConfirmation+Alert.swift +++ b/Emitron/Emitron/UI/Downloads/DownloadDeletionConfirmation+Alert.swift @@ -41,6 +41,6 @@ extension DownloadDeletionConfirmation { extension DownloadDeletionConfirmation: Identifiable { var id: Int { - contentId + contentID } } diff --git a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift index 6a8dd2fa..65d5d431 100644 --- a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift @@ -193,7 +193,7 @@ private extension ContentListView { let content = contentRepository.contents[index] downloadAction - .deleteDownload(contentId: content.id) + .deleteDownload(contentID: content.id) .receive(on: RunLoop.main) .sink( receiveCompletion: { completion in diff --git a/Emitron/emitronTests/Data/DataCacheTest.swift b/Emitron/emitronTests/Data/DataCacheTest.swift index 69cade16..771cdc80 100644 --- a/Emitron/emitronTests/Data/DataCacheTest.swift +++ b/Emitron/emitronTests/Data/DataCacheTest.swift @@ -196,9 +196,9 @@ class DataCacheTest: XCTestCase { XCTAssert(!persistableState.groups.isEmpty) XCTAssert(!persistableState.childContents.isEmpty) - let exampleChildId = persistableState.childContents.first!.id + let exampleChildID = persistableState.childContents.first!.id - let publisher = cache.contentSummaryState(for: [exampleChildId]) + let publisher = cache.contentSummaryState(for: [exampleChildID]) let recorder = publisher.record() let summary = try wait(for: recorder.next(), timeout: 10) diff --git a/Emitron/emitronTests/Data/States/ContentPersistableState+Mocks.swift b/Emitron/emitronTests/Data/States/ContentPersistableState+Mocks.swift index ab6a0cf4..f418b3dc 100644 --- a/Emitron/emitronTests/Data/States/ContentPersistableState+Mocks.swift +++ b/Emitron/emitronTests/Data/States/ContentPersistableState+Mocks.swift @@ -33,29 +33,29 @@ extension ContentPersistableState { persistableState(for: content.id, with: cacheUpdate) } - static func persistableState(for contentId: Int, with cacheUpdate: DataCacheUpdate) -> ContentPersistableState { + static func persistableState(for contentID: Int, with cacheUpdate: DataCacheUpdate) -> ContentPersistableState { - guard let content = cacheUpdate.contents.first(where: { $0.id == contentId }) else { preconditionFailure("Invalid cache update") } + guard let content = cacheUpdate.contents.first(where: { $0.id == contentID }) else { preconditionFailure("Invalid cache update") } var parentContent: Content? - if let groupId = content.groupId { + if let groupID = content.groupID { // There must be parent content - if let parentGroup = cacheUpdate.groups.first(where: { $0.id == groupId }) { - parentContent = cacheUpdate.contents.first { $0.id == parentGroup.contentId } + if let parentGroup = cacheUpdate.groups.first(where: { $0.id == groupID }) { + parentContent = cacheUpdate.contents.first { $0.id == parentGroup.contentID } } } - let groups = cacheUpdate.groups.filter { $0.contentId == content.id } - let groupIds = groups.map(\.id) - let childContent = cacheUpdate.contents.filter { groupIds.contains($0.groupId ?? -1) } + let groups = cacheUpdate.groups.filter { $0.contentID == content.id } + let groupIDs = groups.map(\.id) + let childContent = cacheUpdate.contents.filter { groupIDs.contains($0.groupID ?? -1) } return ContentPersistableState( content: content, - contentDomains: cacheUpdate.contentDomains.filter({ $0.contentId == content.id }), - contentCategories: cacheUpdate.contentCategories.filter({ $0.contentId == content.id }), - bookmark: cacheUpdate.bookmarks.first(where: { $0.contentId == content.id }), + contentDomains: cacheUpdate.contentDomains.filter({ $0.contentID == content.id }), + contentCategories: cacheUpdate.contentCategories.filter({ $0.contentID == content.id }), + bookmark: cacheUpdate.bookmarks.first(where: { $0.contentID == content.id }), parentContent: parentContent, - progression: cacheUpdate.progressions.first(where: { $0.contentId == content.id }), + progression: cacheUpdate.progressions.first(where: { $0.contentID == content.id }), groups: groups, childContents: childContent ) diff --git a/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift b/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift index a56ea69f..5d0ee766 100644 --- a/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift @@ -85,24 +85,24 @@ class DownloadQueueManagerTest: XCTestCase { func persistableState(for content: Content, with cacheUpdate: DataCacheUpdate) -> ContentPersistableState { var parentContent: Content? - if let groupId = content.groupId { + if let groupID = content.groupID { // There must be parent content - if let parentGroup = cacheUpdate.groups.first(where: { $0.id == groupId }) { - parentContent = cacheUpdate.contents.first { $0.id == parentGroup.contentId } + if let parentGroup = cacheUpdate.groups.first(where: { $0.id == groupID }) { + parentContent = cacheUpdate.contents.first { $0.id == parentGroup.contentID } } } - let groups = cacheUpdate.groups.filter { $0.contentId == content.id } - let groupIds = groups.map(\.id) - let childContent = cacheUpdate.contents.filter { groupIds.contains($0.groupId ?? -1) } + let groups = cacheUpdate.groups.filter { $0.contentID == content.id } + let groupIDs = groups.map(\.id) + let childContent = cacheUpdate.contents.filter { groupIDs.contains($0.groupID ?? -1) } return ContentPersistableState( content: content, - contentDomains: cacheUpdate.contentDomains.filter({ $0.contentId == content.id }), - contentCategories: cacheUpdate.contentCategories.filter({ $0.contentId == content.id }), - bookmark: cacheUpdate.bookmarks.first(where: { $0.contentId == content.id }), + contentDomains: cacheUpdate.contentDomains.filter({ $0.contentID == content.id }), + contentCategories: cacheUpdate.contentCategories.filter({ $0.contentID == content.id }), + bookmark: cacheUpdate.bookmarks.first(where: { $0.contentID == content.id }), parentContent: parentContent, - progression: cacheUpdate.progressions.first(where: { $0.contentId == content.id }), + progression: cacheUpdate.progressions.first(where: { $0.contentID == content.id }), groups: groups, childContents: childContent ) @@ -110,7 +110,7 @@ class DownloadQueueManagerTest: XCTestCase { func sampleDownload() throws -> Download { let screencast = ContentTest.Mocks.screencast - let recorder = downloadService.requestDownload(contentId: screencast.0.id) { _ in + let recorder = downloadService.requestDownload(contentID: screencast.0.id) { _ in self.persistableState(for: screencast.0, with: screencast.1) } .record() diff --git a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift index 25cd457d..1d0a1426 100644 --- a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift @@ -82,7 +82,7 @@ class DownloadServiceTest: XCTestCase { func sampleDownloadQueueItem() throws -> PersistenceStore.DownloadQueueItem { let screencast = ContentTest.Mocks.screencast - let recorder = downloadService.requestDownload(contentId: screencast.0.id) { _ in + let recorder = downloadService.requestDownload(contentID: screencast.0.id) { _ in ContentPersistableState.persistableState(for: screencast.0, with: screencast.1) } .record() @@ -133,7 +133,7 @@ class DownloadServiceTest: XCTestCase { //: requestDownload(content:) Tests func testRequestDownloadScreencastAddsContentToLocalStore() throws { let screencast = ContentTest.Mocks.screencast - let recorder = downloadService.requestDownload(contentId: screencast.0.id) { _ in + let recorder = downloadService.requestDownload(contentID: screencast.0.id) { _ in ContentPersistableState.persistableState(for: screencast.0, with: screencast.1) } .record() @@ -174,7 +174,7 @@ class DownloadServiceTest: XCTestCase { XCTAssertEqual(1, getAllContents().count) // Now execute the download request - let recorder = downloadService.requestDownload(contentId: screencast.id) { _ in + let recorder = downloadService.requestDownload(contentID: screencast.id) { _ in ContentPersistableState.persistableState(for: screencast, with: screencastModel.1) } .record() @@ -198,18 +198,18 @@ class DownloadServiceTest: XCTestCase { let fullState = ContentPersistableState.persistableState(for: collection.0, with: collection.1) let episode = fullState.childContents.first! - let recorder = downloadService.requestDownload(contentId: episode.id) { contentId in - ContentPersistableState.persistableState(for: contentId, with: collection.1) + let recorder = downloadService.requestDownload(contentID: episode.id) { contentID in + ContentPersistableState.persistableState(for: contentID, with: collection.1) } .record() let completion = try wait(for: recorder.completion, timeout: 10) XCTAssert(completion == .finished) - let allContentIds = fullState.childContents.map(\.id) + [collection.0.id] + let allContentIDs = fullState.childContents.map(\.id) + [collection.0.id] - XCTAssertEqual(allContentIds.count, getAllContents().count) - XCTAssertEqual(allContentIds.sorted(), getAllContents().map { Int($0.id) }.sorted()) + XCTAssertEqual(allContentIDs.count, getAllContents().count) + XCTAssertEqual(allContentIDs.sorted(), getAllContents().map { Int($0.id) }.sorted()) } func testRequestDownloadEpisodeUpdatesLocalDataStore() throws { @@ -218,8 +218,8 @@ class DownloadServiceTest: XCTestCase { let fullState = ContentPersistableState.persistableState(for: collection, with: collectionModel.1) let episode = fullState.childContents.first! - let recorder = persistenceStore.persistContentGraph(for: fullState, contentLookup: { contentId in - ContentPersistableState.persistableState(for: contentId, with: collectionModel.1) + let recorder = persistenceStore.persistContentGraph(for: fullState, contentLookup: { contentID in + ContentPersistableState.persistableState(for: contentID, with: collectionModel.1) }) .record() @@ -249,7 +249,7 @@ class DownloadServiceTest: XCTestCase { } // Now execute the download request - let anotherRecorder = downloadService.requestDownload(contentId: episode.id) { _ in + let anotherRecorder = downloadService.requestDownload(contentID: episode.id) { _ in ContentPersistableState.persistableState(for: collection, with: collectionModel.1) } .record() @@ -272,8 +272,8 @@ class DownloadServiceTest: XCTestCase { let collection = ContentTest.Mocks.collection let fullState = ContentPersistableState.persistableState(for: collection.0, with: collection.1) - let recorder = downloadService.requestDownload(contentId: collection.0.id) { contentId in - ContentPersistableState.persistableState(for: contentId, with: collection.1) + let recorder = downloadService.requestDownload(contentID: collection.0.id) { contentID in + ContentPersistableState.persistableState(for: contentID, with: collection.1) } .record() @@ -293,8 +293,8 @@ class DownloadServiceTest: XCTestCase { var episode = fullState.childContents.first! - let recorder = persistenceStore.persistContentGraph(for: fullState, contentLookup: { contentId in - ContentPersistableState.persistableState(for: contentId, with: collectionModel.1) + let recorder = persistenceStore.persistContentGraph(for: fullState, contentLookup: { contentID in + ContentPersistableState.persistableState(for: contentID, with: collectionModel.1) }) .record() @@ -323,8 +323,8 @@ class DownloadServiceTest: XCTestCase { } // Now execute the download request - let recorder2 = downloadService.requestDownload(contentId: collectionModel.0.id) { contentId in - ContentPersistableState.persistableState(for: contentId, with: collectionModel.1) + let recorder2 = downloadService.requestDownload(contentID: collectionModel.0.id) { contentID in + ContentPersistableState.persistableState(for: contentID, with: collectionModel.1) } .record() @@ -347,8 +347,8 @@ class DownloadServiceTest: XCTestCase { let fullState = ContentPersistableState.persistableState(for: collection.0, with: collection.1) let episode = fullState.childContents.first! - let recorder = downloadService.requestDownload(contentId: episode.id) { contentId in - ContentPersistableState.persistableState(for: contentId, with: collection.1) + let recorder = downloadService.requestDownload(contentID: episode.id) { contentID in + ContentPersistableState.persistableState(for: contentID, with: collection.1) } .record() @@ -358,7 +358,7 @@ class DownloadServiceTest: XCTestCase { XCTAssertEqual(2, getAllDownloads().count) let download = getAllDownloads().first! - XCTAssertEqual(episode.id, download.contentId) + XCTAssertEqual(episode.id, download.contentID) } func testRequestAdditionalEpisodesUpdatesTheCollectionDownload() throws { @@ -366,16 +366,16 @@ class DownloadServiceTest: XCTestCase { let fullState = ContentPersistableState.persistableState(for: collection.0, with: collection.1) let episodes = fullState.childContents - let recorder1 = downloadService.requestDownload(contentId: episodes[0].id) { contentId in - ContentPersistableState.persistableState(for: contentId, with: collection.1) + let recorder1 = downloadService.requestDownload(contentID: episodes[0].id) { contentID in + ContentPersistableState.persistableState(for: contentID, with: collection.1) } .record() - let recorder2 = downloadService.requestDownload(contentId: episodes[1].id) { contentId in - ContentPersistableState.persistableState(for: contentId, with: collection.1) + let recorder2 = downloadService.requestDownload(contentID: episodes[1].id) { contentID in + ContentPersistableState.persistableState(for: contentID, with: collection.1) } .record() - let recorder3 = downloadService.requestDownload(contentId: episodes[2].id) { contentId in - ContentPersistableState.persistableState(for: contentId, with: collection.1) + let recorder3 = downloadService.requestDownload(contentID: episodes[2].id) { contentID in + ContentPersistableState.persistableState(for: contentID, with: collection.1) } .record() @@ -388,7 +388,7 @@ class DownloadServiceTest: XCTestCase { func testRequestDownloadAddsDownloadToScreencasts() throws { let screencast = ContentTest.Mocks.screencast - let recorder = downloadService.requestDownload(contentId: screencast.0.id) { _ in + let recorder = downloadService.requestDownload(contentID: screencast.0.id) { _ in ContentPersistableState.persistableState(for: screencast.0, with: screencast.1) } .record() @@ -398,14 +398,14 @@ class DownloadServiceTest: XCTestCase { XCTAssertEqual(1, getAllDownloads().count) let download = getAllDownloads().first! - XCTAssertEqual(screencast.0.id, download.contentId) + XCTAssertEqual(screencast.0.id, download.contentID) } func testRequestDownloadAddsDownloadToCollection() throws { let collection = ContentTest.Mocks.collection let fullState = ContentPersistableState.persistableState(for: collection.0, with: collection.1) - let recorder = downloadService.requestDownload(contentId: collection.0.id) { contentId in - ContentPersistableState.persistableState(for: contentId, with: collection.1) + let recorder = downloadService.requestDownload(contentID: collection.0.id) { contentID in + ContentPersistableState.persistableState(for: contentID, with: collection.1) } .record() @@ -416,13 +416,13 @@ class DownloadServiceTest: XCTestCase { XCTAssertEqual(fullState.childContents.count + 1, getAllDownloads().count) XCTAssertEqual( (fullState.childContents.map(\.id) + [collection.0.id]).sorted(), - getAllDownloads().map(\.contentId).sorted() + getAllDownloads().map(\.contentID).sorted() ) } func testRequestDownloadAddsDownloadInPendingState() throws { let screencast = ContentTest.Mocks.screencast - let recorder = downloadService.requestDownload(contentId: screencast.0.id) { _ in + let recorder = downloadService.requestDownload(contentID: screencast.0.id) { _ in ContentPersistableState.persistableState(for: screencast.0, with: screencast.1) } .record() @@ -520,8 +520,8 @@ class DownloadServiceTest: XCTestCase { let fullState = ContentPersistableState.persistableState(for: collection.0, with: collection.1) let episode = fullState.childContents.first! - let recorder = downloadService.requestDownload(contentId: episode.id) { contentId in - ContentPersistableState.persistableState(for: contentId, with: collection.1) + let recorder = downloadService.requestDownload(contentID: episode.id) { contentID in + ContentPersistableState.persistableState(for: contentID, with: collection.1) } .record() @@ -549,7 +549,7 @@ class DownloadServiceTest: XCTestCase { func testRequestDownloadURLDoesNothingForCollection() throws { let collection = ContentTest.Mocks.collection - let recorder = downloadService.requestDownload(contentId: collection.0.id) { _ in + let recorder = downloadService.requestDownload(contentID: collection.0.id) { _ in ContentPersistableState.persistableState(for: collection.0, with: collection.1) } .record() diff --git a/Emitron/emitronTests/Models/UserTest.swift b/Emitron/emitronTests/Models/UserTest.swift index 3ae5e836..05bb7702 100644 --- a/Emitron/emitronTests/Models/UserTest.swift +++ b/Emitron/emitronTests/Models/UserTest.swift @@ -50,7 +50,7 @@ class UserTest: XCTestCase { return } - XCTAssertEqual(userDictionary["external_id"], user.externalId) + XCTAssertEqual(userDictionary["external_id"], user.externalID) XCTAssertEqual(userDictionary["email"], user.email) XCTAssertEqual(userDictionary["username"], user.username) XCTAssertEqual(userDictionary["avatar_url"], user.avatarURL.absoluteString) diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/BookmarkAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/BookmarkAdapterTest.swift index cc30e835..76baadf8 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/BookmarkAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/BookmarkAdapterTest.swift @@ -72,7 +72,7 @@ class BookmarkAdapterTest: XCTestCase { XCTAssertEqual(1234, bookmark.id) let cmpts = DateComponents(timeZone: TimeZone(secondsFromGMT: 0), year: 2020, month: 1, day: 1, hour: 12, minute: 0, second: 0) XCTAssertEqual(Calendar.current.date(from: cmpts), bookmark.createdAt) - XCTAssertEqual(4321, bookmark.contentId) + XCTAssertEqual(4321, bookmark.contentID) } func testInvalidTypeThrows() throws { diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentAdapterTest.swift index d67a3f4c..936c0064 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentAdapterTest.swift @@ -130,7 +130,7 @@ class ContentAdapterTest: XCTestCase { XCTAssertEqual("Katie Collins & Jessy Catterwaul", content.contributors) XCTAssertEqual(13, content.ordinal) XCTAssertEqual(.advanced, content.difficulty) - XCTAssertEqual(1234, content.groupId) + XCTAssertEqual(1234, content.groupID) XCTAssertEqual(2546, content.videoIdentifier) } @@ -291,7 +291,7 @@ class ContentAdapterTest: XCTestCase { let resource = try makeJsonAPIResource(for: sampleResource) let content = try ContentAdapter.process(resource: resource, relationships: relationships) - XCTAssertNil(content.groupId) + XCTAssertNil(content.groupID) } func testFirstRelationshipIsChosenToDetermineGroup() throws { @@ -300,7 +300,7 @@ class ContentAdapterTest: XCTestCase { let resource = try makeJsonAPIResource(for: sampleResource) let content = try ContentAdapter.process(resource: resource, relationships: relationships) - XCTAssertEqual(4321, content.groupId) + XCTAssertEqual(4321, content.groupID) } func testNullOrdinalIsAcceptable() throws { diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentCategoryAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentCategoryAdapterTest.swift index c1b2dc17..6c4ae226 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentCategoryAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentCategoryAdapterTest.swift @@ -41,10 +41,10 @@ class ContentCategoryAdapterTest: XCTestCase { let contentCategories = try ContentCategoryAdapter.process(relationships: relationships) XCTAssertEqual(2, contentCategories.count) - XCTAssertEqual(34, contentCategories[0].categoryId) - XCTAssertEqual(23, contentCategories[0].contentId) - XCTAssertEqual(56, contentCategories[1].categoryId) - XCTAssertEqual(45, contentCategories[1].contentId) + XCTAssertEqual(34, contentCategories[0].categoryID) + XCTAssertEqual(23, contentCategories[0].contentID) + XCTAssertEqual(56, contentCategories[1].categoryID) + XCTAssertEqual(45, contentCategories[1].contentID) XCTAssertNil(contentCategories[0].id) XCTAssertNil(contentCategories[1].id) } diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentDomainAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentDomainAdapterTest.swift index 24b46f60..4bcf7681 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentDomainAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ContentDomainAdapterTest.swift @@ -41,10 +41,10 @@ class ContentDomainAdapterTest: XCTestCase { let contentDomains = try ContentDomainAdapter.process(relationships: relationships) XCTAssertEqual(2, contentDomains.count) - XCTAssertEqual(34, contentDomains[0].domainId) - XCTAssertEqual(23, contentDomains[0].contentId) - XCTAssertEqual(56, contentDomains[1].domainId) - XCTAssertEqual(45, contentDomains[1].contentId) + XCTAssertEqual(34, contentDomains[0].domainID) + XCTAssertEqual(23, contentDomains[0].contentID) + XCTAssertEqual(56, contentDomains[1].domainID) + XCTAssertEqual(45, contentDomains[1].contentID) XCTAssertNil(contentDomains[0].id) XCTAssertNil(contentDomains[1].id) } diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/GroupAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/GroupAdapterTest.swift index e7e3882a..ae2513a9 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/GroupAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/GroupAdapterTest.swift @@ -79,7 +79,7 @@ class GroupAdapterTest: XCTestCase { XCTAssertEqual(1234, group.id) XCTAssertEqual("Sample Group", group.name) XCTAssertEqual("Group description", group.description) - XCTAssertEqual(12, group.contentId) + XCTAssertEqual(12, group.contentID) XCTAssertEqual(5, group.ordinal) } @@ -133,7 +133,7 @@ class GroupAdapterTest: XCTestCase { let group = try GroupAdapter.process(resource: resource, relationships: relationships) - XCTAssertEqual(15, group.contentId) + XCTAssertEqual(15, group.contentID) } func testMissingDescriptionIsAcceptable() throws { diff --git a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ProgressionAdapterTest.swift b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ProgressionAdapterTest.swift index 41f801b2..a13a59f0 100644 --- a/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ProgressionAdapterTest.swift +++ b/Emitron/emitronTests/Networking/Adapters/EntityAdapters/ProgressionAdapterTest.swift @@ -82,7 +82,7 @@ class ProgressionAdapterTest: XCTestCase { cmpts.day = 2 cmpts.hour = 14 XCTAssertEqual(Calendar.current.date(from: cmpts), progression.updatedAt) - XCTAssertEqual(4321, progression.contentId) + XCTAssertEqual(4321, progression.contentID) } func testInvalidTypeThrows() throws { diff --git a/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift b/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift index 9e7fe084..d18fb976 100644 --- a/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift +++ b/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift @@ -46,7 +46,7 @@ enum PersistenceMocks { cardArtworkURL: URL(string: "https://example.com/card_artwork.png")!, technologyTriple: "Some Tech", contributors: "HELLO", - groupId: nil, + groupID: nil, ordinal: 0) } @@ -54,6 +54,6 @@ enum PersistenceMocks { Download(id: UUID(), requestedAt: Date(), state: .pending, - contentId: content.id) + contentID: content.id) } } diff --git a/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift b/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift index 1d0cc87b..2f57b94a 100644 --- a/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift +++ b/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift @@ -62,8 +62,8 @@ class PersistenceStore_DownloadsTest: XCTestCase { func populateSampleScreencast() throws -> Content { let screencast = ContentTest.Mocks.screencast let fullState = ContentPersistableState.persistableState(for: screencast.0, with: screencast.1) - let recorder = persistenceStore.persistContentGraph(for: fullState) { contentId -> (ContentPersistableState?) in - ContentPersistableState.persistableState(for: contentId, with: screencast.1) + let recorder = persistenceStore.persistContentGraph(for: fullState) { contentID -> (ContentPersistableState?) in + ContentPersistableState.persistableState(for: contentID, with: screencast.1) } .record() @@ -75,8 +75,8 @@ class PersistenceStore_DownloadsTest: XCTestCase { func populateSampleCollection() throws -> Content { let collection = ContentTest.Mocks.collection let fullState = ContentPersistableState.persistableState(for: collection.0, with: collection.1) - let recorder = persistenceStore.persistContentGraph(for: fullState) { contentId -> (ContentPersistableState?) in - ContentPersistableState.persistableState(for: contentId, with: collection.1) + let recorder = persistenceStore.persistContentGraph(for: fullState) { contentID -> (ContentPersistableState?) in + ContentPersistableState.persistableState(for: contentID, with: collection.1) } .record() @@ -98,7 +98,7 @@ class PersistenceStore_DownloadsTest: XCTestCase { try episodeDownload.save(db) } - try persistenceStore.transitionDownload(withId: episodeDownload.id, to: .inProgress) + try persistenceStore.transitionDownload(withID: episodeDownload.id, to: .inProgress) let collectionExpectation = XCTestExpectation() @@ -130,8 +130,8 @@ class PersistenceStore_DownloadsTest: XCTestCase { try episodeDownload2.save(db) } - try persistenceStore.transitionDownload(withId: episodeDownload.id, to: .inProgress) - try persistenceStore.transitionDownload(withId: episodeDownload2.id, to: .complete) + try persistenceStore.transitionDownload(withID: episodeDownload.id, to: .inProgress) + try persistenceStore.transitionDownload(withID: episodeDownload2.id, to: .complete) let collectionExpectation = XCTestExpectation() @@ -168,7 +168,7 @@ class PersistenceStore_DownloadsTest: XCTestCase { } try episodeDownloads.forEach { - try persistenceStore.transitionDownload(withId: $0.id, to: .complete) + try persistenceStore.transitionDownload(withID: $0.id, to: .complete) } let collectionExpectation = XCTestExpectation() @@ -201,8 +201,8 @@ class PersistenceStore_DownloadsTest: XCTestCase { try episodeDownload2.save(db) } - try persistenceStore.transitionDownload(withId: episodeDownload.id, to: .complete) - try persistenceStore.transitionDownload(withId: episodeDownload2.id, to: .complete) + try persistenceStore.transitionDownload(withID: episodeDownload.id, to: .complete) + try persistenceStore.transitionDownload(withID: episodeDownload2.id, to: .complete) let collectionExpectation = XCTestExpectation() @@ -236,10 +236,10 @@ class PersistenceStore_DownloadsTest: XCTestCase { } try episodeDownloads[0..<5].forEach { - try persistenceStore.transitionDownload(withId: $0.id, to: .complete) + try persistenceStore.transitionDownload(withID: $0.id, to: .complete) } - let collectionDownloadSummary = try persistenceStore.collectionDownloadSummary(forContentId: collection.id) + let collectionDownloadSummary = try persistenceStore.collectionDownloadSummary(forContentID: collection.id) XCTAssertEqual( PersistenceStore.CollectionDownloadSummary( @@ -264,10 +264,10 @@ class PersistenceStore_DownloadsTest: XCTestCase { } try episodeDownloads[0..<5].forEach { - try persistenceStore.transitionDownload(withId: $0.id, to: .complete) + try persistenceStore.transitionDownload(withID: $0.id, to: .complete) } - let collectionDownloadSummary = try persistenceStore.collectionDownloadSummary(forContentId: collection.id) + let collectionDownloadSummary = try persistenceStore.collectionDownloadSummary(forContentID: collection.id) XCTAssertEqual( PersistenceStore.CollectionDownloadSummary( @@ -292,10 +292,10 @@ class PersistenceStore_DownloadsTest: XCTestCase { } try episodeDownloads.forEach { - try persistenceStore.transitionDownload(withId: $0.id, to: .complete) + try persistenceStore.transitionDownload(withID: $0.id, to: .complete) } - let collectionDownloadSummary = try persistenceStore.collectionDownloadSummary(forContentId: collection.id) + let collectionDownloadSummary = try persistenceStore.collectionDownloadSummary(forContentID: collection.id) XCTAssertEqual( PersistenceStore.CollectionDownloadSummary( @@ -320,10 +320,10 @@ class PersistenceStore_DownloadsTest: XCTestCase { } try episodeDownloads.forEach { - try persistenceStore.transitionDownload(withId: $0.id, to: .complete) + try persistenceStore.transitionDownload(withID: $0.id, to: .complete) } - let collectionDownloadSummary = try persistenceStore.collectionDownloadSummary(forContentId: collection.id) + let collectionDownloadSummary = try persistenceStore.collectionDownloadSummary(forContentID: collection.id) XCTAssertEqual( PersistenceStore.CollectionDownloadSummary( @@ -343,7 +343,7 @@ class PersistenceStore_DownloadsTest: XCTestCase { try download.save(db) } - XCTAssertThrowsError(try persistenceStore.collectionDownloadSummary(forContentId: screencast.id)) { error in + XCTAssertThrowsError(try persistenceStore.collectionDownloadSummary(forContentID: screencast.id)) { error in XCTAssertEqual(.argumentError, error as! PersistenceStoreError) } } @@ -444,11 +444,11 @@ class PersistenceStore_DownloadsTest: XCTestCase { let downloads = getAllDownloads().sorted { $0.requestedAt < $1.requestedAt } let episodes = getAllContents().filter { $0.contentType == .episode } try downloads.forEach { download in - try persistenceStore.transitionDownload(withId: download.id, to: .inProgress) + try persistenceStore.transitionDownload(withID: download.id, to: .inProgress) } try downloads.forEach { download in - try persistenceStore.transitionDownload(withId: download.id, to: .complete) + try persistenceStore.transitionDownload(withID: download.id, to: .complete) } // Will start with a nil @@ -468,13 +468,13 @@ class PersistenceStore_DownloadsTest: XCTestCase { let recorder = persistenceStore.downloadQueue(withMaxLength: 4).record() let episodes = getAllContents().filter({ $0.contentType == .episode }) - let episodeIds = episodes.map(\.id) - let collectionDownload = getAllDownloads().first { !episodeIds.contains($0.contentId) } - let episodeDownloads = getAllDownloads().filter { episodeIds.contains($0.contentId) } + let episodeIDs = episodes.map(\.id) + let collectionDownload = getAllDownloads().first { !episodeIDs.contains($0.contentID) } + let episodeDownloads = getAllDownloads().filter { episodeIDs.contains($0.contentID) } - try persistenceStore.transitionDownload(withId: episodeDownloads[1].id, to: .inProgress) - try persistenceStore.transitionDownload(withId: collectionDownload!.id, to: .inProgress) - try persistenceStore.transitionDownload(withId: episodeDownloads[0].id, to: .inProgress) + try persistenceStore.transitionDownload(withID: episodeDownloads[1].id, to: .inProgress) + try persistenceStore.transitionDownload(withID: collectionDownload!.id, to: .inProgress) + try persistenceStore.transitionDownload(withID: episodeDownloads[0].id, to: .inProgress) let downloadQueue = try wait(for: recorder.next(3), timeout: 10) @@ -491,17 +491,17 @@ class PersistenceStore_DownloadsTest: XCTestCase { let recorder = persistenceStore.downloadQueue(withMaxLength: 4).record() let episodes = getAllContents().filter({ $0.contentType == .episode }) - let episodeIds = episodes.map(\.id) - let collectionDownload = getAllDownloads().first { !episodeIds.contains($0.contentId) } - let episodeDownloads = getAllDownloads().filter { episodeIds.contains($0.contentId) } - - try persistenceStore.transitionDownload(withId: episodeDownloads[1].id, to: .inProgress) - try persistenceStore.transitionDownload(withId: collectionDownload!.id, to: .inProgress) - try persistenceStore.transitionDownload(withId: episodeDownloads[0].id, to: .inProgress) - try persistenceStore.transitionDownload(withId: episodeDownloads[5].id, to: .inProgress) - try persistenceStore.transitionDownload(withId: episodeDownloads[4].id, to: .inProgress) - try persistenceStore.transitionDownload(withId: episodeDownloads[3].id, to: .inProgress) - try persistenceStore.transitionDownload(withId: episodeDownloads[2].id, to: .inProgress) + let episodeIDs = episodes.map(\.id) + let collectionDownload = getAllDownloads().first { !episodeIDs.contains($0.contentID) } + let episodeDownloads = getAllDownloads().filter { episodeIDs.contains($0.contentID) } + + try persistenceStore.transitionDownload(withID: episodeDownloads[1].id, to: .inProgress) + try persistenceStore.transitionDownload(withID: collectionDownload!.id, to: .inProgress) + try persistenceStore.transitionDownload(withID: episodeDownloads[0].id, to: .inProgress) + try persistenceStore.transitionDownload(withID: episodeDownloads[5].id, to: .inProgress) + try persistenceStore.transitionDownload(withID: episodeDownloads[4].id, to: .inProgress) + try persistenceStore.transitionDownload(withID: episodeDownloads[3].id, to: .inProgress) + try persistenceStore.transitionDownload(withID: episodeDownloads[2].id, to: .inProgress) let downloadQueue = try wait(for: recorder.next(7), timeout: 10) @@ -515,7 +515,7 @@ class PersistenceStore_DownloadsTest: XCTestCase { XCTAssertEqual([0, 1, 2, 3].map { episodeDownloads[$0].id }, downloadQueue[6].map(\.download.id)) } - func testDownloadWithIdReturnsCorrectDownload() throws { + func testDownloadWithIDReturnsCorrectDownload() throws { let screencast = try populateSampleScreencast() var download = PersistenceMocks.download(for: screencast) @@ -523,14 +523,14 @@ class PersistenceStore_DownloadsTest: XCTestCase { try download.save(db) } - XCTAssertEqual(download, try persistenceStore.download(withId: download.id)) + XCTAssertEqual(download, try persistenceStore.download(withID: download.id)) } - func testDownloadWithIdReturnsNilForNoDownload() throws { - XCTAssertNil(try persistenceStore.download(withId: UUID())) + func testDownloadWithIDReturnsNilForNoDownload() throws { + XCTAssertNil(try persistenceStore.download(withID: UUID())) } - func testDownloadForContentIdReturnsCorrectDownload() throws { + func testDownloadForContentIDReturnsCorrectDownload() throws { let screencast = try populateSampleScreencast() var download = PersistenceMocks.download(for: screencast) @@ -538,16 +538,16 @@ class PersistenceStore_DownloadsTest: XCTestCase { try download.save(db) } - XCTAssertEqual(download, try persistenceStore.download(forContentId: screencast.id)) + XCTAssertEqual(download, try persistenceStore.download(forContentID: screencast.id)) } - func testDownloadForContentIdReturnsNilForNoDownload() throws { + func testDownloadForContentIDReturnsNilForNoDownload() throws { let screencast = try populateSampleScreencast() - XCTAssertNil(try persistenceStore.download(forContentId: screencast.id)) + XCTAssertNil(try persistenceStore.download(forContentID: screencast.id)) } - func testDownloadForContentIdReturnsNilForNoContent() throws { - XCTAssertNil(try persistenceStore.download(forContentId: 1234)) + func testDownloadForContentIDReturnsNilForNoContent() throws { + XCTAssertNil(try persistenceStore.download(forContentID: 1234)) } } From 6861db9a328e742f0d7c3a7c34d1378022a62113 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 2 Feb 2022 12:20:56 +0300 Subject: [PATCH 40/69] Fix minor swiftlint warnings Signed-off-by: Franklin Byaruhanga --- Emitron/Emitron/Extensions/Binding+Extensions.swift | 3 +-- Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Emitron/Emitron/Extensions/Binding+Extensions.swift b/Emitron/Emitron/Extensions/Binding+Extensions.swift index c27efaa2..149c31aa 100644 --- a/Emitron/Emitron/Extensions/Binding+Extensions.swift +++ b/Emitron/Emitron/Extensions/Binding+Extensions.swift @@ -29,11 +29,10 @@ import struct SwiftUI.Binding public extension Binding where Value == Bool { - prefix static func !(binding: Self) -> Self { + prefix static func ! (binding: Self) -> Self { .init( get: { !binding.wrappedValue }, set: { binding.wrappedValue = !$0 } ) } } - diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift b/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift index 16a0becd..f5d0c494 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Keychain.swift @@ -42,8 +42,8 @@ extension PersistenceStore { } return KeychainSwift().set(encoded, - forKey: ssoUserKey, - withAccess: .accessibleAfterFirstUnlock) + forKey: ssoUserKey, + withAccess: .accessibleAfterFirstUnlock) } func userFromKeychain(_ decoder: JSONDecoder = .init()) -> User? { From 747b9ba7c571ed216640625b85d8808c3c6e9a4b Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Wed, 2 Feb 2022 11:45:41 -0500 Subject: [PATCH 41/69] swiftlint:disable:next operator_whitespace It doesn't make sense for prefix functions. --- Emitron/Emitron/Extensions/Binding+Extensions.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Emitron/Emitron/Extensions/Binding+Extensions.swift b/Emitron/Emitron/Extensions/Binding+Extensions.swift index 149c31aa..4128e76b 100644 --- a/Emitron/Emitron/Extensions/Binding+Extensions.swift +++ b/Emitron/Emitron/Extensions/Binding+Extensions.swift @@ -29,7 +29,8 @@ import struct SwiftUI.Binding public extension Binding where Value == Bool { - prefix static func ! (binding: Self) -> Self { + // swiftlint:disable:next operator_whitespace + prefix static func !(binding: Self) -> Self { .init( get: { !binding.wrappedValue }, set: { binding.wrappedValue = !$0 } From 012164834b8ae47aff43c5d2d055568c66e871e5 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Wed, 2 Feb 2022 11:47:17 -0500 Subject: [PATCH 42/69] Make SwiftLint work on M1 Macs --- Emitron/Emitron.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index 5c55fbfb..b5008aba 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -1932,7 +1932,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if which swiftlint >/dev/null; then\n swiftlint --no-cache\nelse\n echo \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\nfi\n"; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\n\nif which swiftlint >/dev/null; then\n swiftlint --no-cache\nelse\n echo \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ From 0f0b5c62bb878f562d004f1aaa4cb2b1541914e6 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Sat, 5 Feb 2022 02:50:59 -0500 Subject: [PATCH 43/69] Undo "Fix bar appearance in iOS 15" Originally commited as a80f1f2b376bc97c8e2b6442042b27fbcc3466aa. Apple has changed rendering since then. --- Emitron/Emitron/UI/App Root/TabNavView.swift | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Emitron/Emitron/UI/App Root/TabNavView.swift b/Emitron/Emitron/UI/App Root/TabNavView.swift index 5f7ebda6..891703c8 100644 --- a/Emitron/Emitron/UI/App Root/TabNavView.swift +++ b/Emitron/Emitron/UI/App Root/TabNavView.swift @@ -44,19 +44,6 @@ struct TabNavView< self.myTutorialsView = myTutorialsView self.downloadsView = downloadsView self.settingsView = settingsView - - // Without the following, in iOS 15, this ugliness occurs: - // - // The nav bar renders as a rectangle that does not extend vertically past the safe area. - // Scrolling content is visible above it. - // - // The tab bar renders transparently, on top of other views. - if #available(iOS 15.0, *) { - let barAppearance = UIBarAppearance() - barAppearance.configureWithOpaqueBackground() - UITabBar.appearance().scrollEdgeAppearance = .init(barAppearance: barAppearance) - UINavigationBar.appearance().scrollEdgeAppearance = .init(barAppearance: barAppearance) - } } @EnvironmentObject private var model: TabViewModel From 23f16797c3d7c907aa7f42718a8fe72a837d7de1 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Mon, 7 Feb 2022 14:16:26 +0300 Subject: [PATCH 44/69] Remove Deprecated navigationBarTitle Remove Deprecated navigationBarTitle(_:) repplaced with navigationTitle(_:) Signed-off-by: Franklin Byaruhanga --- Emitron/Emitron/UI/Downloads/DownloadsView.swift | 2 +- Emitron/Emitron/UI/Library/LibraryView.swift | 2 +- Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift | 2 +- Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift | 2 +- Emitron/Emitron/UI/Settings/SettingsSelectionView.swift | 6 +++--- Emitron/Emitron/UI/Settings/SettingsView.swift | 2 +- .../UI/Shared/Content Detail/ContentDetailView.swift | 3 ++- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Emitron/Emitron/UI/Downloads/DownloadsView.swift b/Emitron/Emitron/UI/Downloads/DownloadsView.swift index d6cb0854..c9ca2c3f 100644 --- a/Emitron/Emitron/UI/Downloads/DownloadsView.swift +++ b/Emitron/Emitron/UI/Downloads/DownloadsView.swift @@ -48,6 +48,6 @@ struct DownloadsView: View { downloadAction: downloadService, contentScreen: contentScreen ) - .navigationBarTitle(String.downloads) + .navigationTitle(String.downloads) } } diff --git a/Emitron/Emitron/UI/Library/LibraryView.swift b/Emitron/Emitron/UI/Library/LibraryView.swift index f14d3e9c..9eadaf62 100644 --- a/Emitron/Emitron/UI/Library/LibraryView.swift +++ b/Emitron/Emitron/UI/Library/LibraryView.swift @@ -43,7 +43,7 @@ struct LibraryView: View { var body: some View { contentView - .navigationBarTitle( + .navigationTitle( Text(String.library) ) .sheet(isPresented: $filtersPresented) { diff --git a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift index f496cc4b..5078e797 100644 --- a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift +++ b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift @@ -105,7 +105,7 @@ struct MyTutorialsView { extension MyTutorialsView: View { var body: some View { contentView - .navigationBarTitle(String.myTutorials) + .navigationTitle(String.myTutorials) } } diff --git a/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift b/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift index 77be907e..3f6af064 100644 --- a/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift +++ b/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift @@ -45,7 +45,7 @@ struct LicenseListView: View { .font(.uiLabel) .foregroundColor(.contentText) } - .navigationBarTitle("Software Licenses") + .navigationTitle("Software Licenses") .navigationBarItems(trailing: dismissButton) } } diff --git a/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift b/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift index 66a5e454..ef284e09 100644 --- a/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift @@ -61,12 +61,12 @@ struct SettingsSelectionView: View { Spacer() } - .navigationBarTitle( + .navigationTitle( Text(title) .font(.uiTitle5) - .foregroundColor(.titleText), - displayMode: .inline + .foregroundColor(.titleText) ) + .navigationBarTitleDisplayMode(.inline) .padding(20) .background(Color.background.edgesIgnoringSafeArea(.all)) } diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index ad307113..3fe2ba85 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -119,7 +119,7 @@ struct SettingsView: View { } .padding([.bottom, .horizontal], 18) } - .navigationBarTitle(String.settings) + .navigationTitle(String.settings) .background(Color.background.edgesIgnoringSafeArea(.all)) } } diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index f937e0ff..c67ba018 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -88,7 +88,8 @@ private extension ContentDetailView { } } } - .navigationBarTitle(Text(""), displayMode: .inline) + .navigationTitle(Text("")) + .navigationBarTitleDisplayMode(.inline) .background(Color.background) .onReceive(videoCompletedNotification) { _ in checkReviewRequest = true From 461007ec2709f63374475aa0c6c2a04c206b85c4 Mon Sep 17 00:00:00 2001 From: jason goodney Date: Mon, 7 Feb 2022 06:34:02 -0500 Subject: [PATCH 45/69] Sets vstack spacing to zero --- Emitron/Emitron/UI/Settings/SettingsView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index abadc22c..9f29d0ee 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -46,7 +46,7 @@ struct SettingsView: View { } var body: some View { - VStack { + VStack(spacing: 0) { Link(destination: URL(string: "https://accounts.raywenderlich.com")!) { SettingsDisclosureRow(title: "My Account", value: "") } From 54209db49915fe3e85262d69f79a6361535d0aae Mon Sep 17 00:00:00 2001 From: jason goodney Date: Mon, 7 Feb 2022 06:34:43 -0500 Subject: [PATCH 46/69] Code formatting --- Emitron/Emitron/UI/Settings/SettingsView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 9f29d0ee..632c761e 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -56,6 +56,7 @@ struct SettingsView: View { settingsManager: _settingsManager, canDownload: sessionController.user?.canDownload ?? false ).padding(.horizontal, 20) + Section( header: HStack { Text("App Icon") From e7d211d91cf7450f1a9e846b9151e071d8dfa6fe Mon Sep 17 00:00:00 2001 From: jason goodney Date: Mon, 7 Feb 2022 06:36:50 -0500 Subject: [PATCH 47/69] Adds spacing between icon chooser and the section title --- Emitron/Emitron/UI/Settings/SettingsView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 632c761e..821b746c 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -68,6 +68,7 @@ struct SettingsView: View { .padding(.top, 20) ) { IconChooserView() + .padding(.top, 10) } .padding(.horizontal, 20) From 9de75dcc9c80a76462836aa35616da4e13c5b7a9 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Mon, 7 Feb 2022 08:36:21 -0500 Subject: [PATCH 48/69] Re-indent within SettingsSelectionView --- .../UI/Settings/SettingsSelectionView.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift b/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift index ef284e09..567b2aae 100644 --- a/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift @@ -61,13 +61,13 @@ struct SettingsSelectionView: View { Spacer() } - .navigationTitle( - Text(title) - .font(.uiTitle5) - .foregroundColor(.titleText) - ) - .navigationBarTitleDisplayMode(.inline) - .padding(20) + .navigationTitle( + Text(title) + .font(.uiTitle5) + .foregroundColor(.titleText) + ) + .navigationBarTitleDisplayMode(.inline) + .padding(20) .background(Color.background.edgesIgnoringSafeArea(.all)) } } From f63a278aed2185daa7b3bba22cdd958571b0415c Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 07:44:07 -0500 Subject: [PATCH 49/69] Remove `Nav` from Emitron.TabView --- Emitron/Emitron.xcodeproj/project.pbxproj | 8 ++++---- Emitron/Emitron/UI/App Root/MainView.swift | 4 ++-- Emitron/Emitron/UI/App Root/TabNavView.swift | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index b5008aba..277c146b 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -247,6 +247,7 @@ 492E632627A6B96900CD1F19 /* Binding+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492E632527A6B96900CD1F19 /* Binding+Extensions.swift */; }; 493DA0A127266049006ED195 /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = 493DA0A027266049006ED195 /* GRDB */; }; 494A79A82465C8C90097E8F4 /* RefreshableTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 494A79A72465C8C90097E8F4 /* RefreshableTestCase.swift */; }; + 49971FEA27B297DA00FBCCEA /* TabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49971FE927B297DA00FBCCEA /* TabView.swift */; }; 8B283DEF23169A1F001F1B17 /* ProgressBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B283DEE23169A1E001F1B17 /* ProgressBarView.swift */; }; 8B7E96DD2357A65F0083DA38 /* ProTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B7E96DC2357A65F0083DA38 /* ProTag.swift */; }; 8B7E96DF2357A66A0083DA38 /* CompletedTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B7E96DE2357A66A0083DA38 /* CompletedTag.swift */; }; @@ -271,7 +272,6 @@ B695033622D4BE7800CE0391 /* Bitter-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B6D7DC4A22C7AFEC006DD325 /* Bitter-Regular.ttf */; }; B695033722D4BE7900CE0391 /* Bitter-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B6D7DC4B22C7AFEC006DD325 /* Bitter-Italic.ttf */; }; B695033822D4BED700CE0391 /* OFL.txt in Resources */ = {isa = PBXBuildFile; fileRef = B6D7DC4922C7AFEC006DD325 /* OFL.txt */; }; - B6C0F0CF22D5D3EE00012839 /* TabNavView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C0F0CE22D5D3EE00012839 /* TabNavView.swift */; }; B6C0F0D122D5D43B00012839 /* MyTutorialsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C0F0D022D5D43B00012839 /* MyTutorialsView.swift */; }; B6C0F0D322D5D46D00012839 /* DownloadsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C0F0D222D5D46D00012839 /* DownloadsView.swift */; }; B6C0F0D522D5D47600012839 /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C0F0D422D5D47600012839 /* LibraryView.swift */; }; @@ -587,6 +587,7 @@ 22FDB2ED23CAC7E6001F883E /* ChildContentListingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChildContentListingView.swift; sourceTree = ""; }; 492E632527A6B96900CD1F19 /* Binding+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Extensions.swift"; sourceTree = ""; }; 494A79A72465C8C90097E8F4 /* RefreshableTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshableTestCase.swift; sourceTree = ""; }; + 49971FE927B297DA00FBCCEA /* TabView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabView.swift; sourceTree = ""; }; 8B283DEE23169A1E001F1B17 /* ProgressBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressBarView.swift; sourceTree = ""; }; 8B7E96DC2357A65F0083DA38 /* ProTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProTag.swift; sourceTree = ""; }; 8B7E96DE2357A66A0083DA38 /* CompletedTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletedTag.swift; sourceTree = ""; }; @@ -614,7 +615,6 @@ B6419BB222EF5622003AC14E /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; B66778AB2305D2D4003EEBAB /* MainButtonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainButtonView.swift; sourceTree = ""; }; B682F8C622EB096300CBF2CC /* Filters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filters.swift; sourceTree = ""; }; - B6C0F0CE22D5D3EE00012839 /* TabNavView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabNavView.swift; sourceTree = ""; }; B6C0F0D022D5D43B00012839 /* MyTutorialsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyTutorialsView.swift; sourceTree = ""; }; B6C0F0D222D5D46D00012839 /* DownloadsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadsView.swift; sourceTree = ""; }; B6C0F0D422D5D47600012839 /* LibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryView.swift; sourceTree = ""; }; @@ -1373,7 +1373,7 @@ B62B9A7F22DF76A500122CE8 /* MainView.swift */, 2228E3FF23B9891600E103AA /* LoginView.swift */, 2228E40123B98D5500E103AA /* LogoutView.swift */, - B6C0F0CE22D5D3EE00012839 /* TabNavView.swift */, + 49971FE927B297DA00FBCCEA /* TabView.swift */, 22BFE75423D9905500495BA9 /* SnackbarView.swift */, 22BFE75B23D9AF4100495BA9 /* MessageBarView.swift */, 22BE655223F2D0CB00717369 /* PermissionsLoadingView.swift */, @@ -2022,7 +2022,6 @@ 22532605240E986800F5FD07 /* Ownable.swift in Sources */, B60FF7BE23000ABD00F36B32 /* CardView.swift in Sources */, 8BFC74C32364A9BE001979F1 /* ContentScreen.swift in Sources */, - B6C0F0CF22D5D3EE00012839 /* TabNavView.swift in Sources */, B6DF2F9122CA00820081A3A3 /* Request.swift in Sources */, 22C3F15724279692002812CB /* FullScreenVideoPlayerViewController.swift in Sources */, B62B9A8022DF76A500122CE8 /* MainView.swift in Sources */, @@ -2082,6 +2081,7 @@ B6C4F3D222E6ECA50087ED10 /* CheckmarkView.swift in Sources */, 22B8265D23AF37FE00D4BA23 /* ProgressionAdapter.swift in Sources */, 222C0AB023DF554E00D65EBD /* SettingsOption.swift in Sources */, + 49971FEA27B297DA00FBCCEA /* TabView.swift in Sources */, 2278AE50240A74C400855221 /* UIApplication+DismissKeyboard.swift in Sources */, B6C0F0DD22D5FA1C00012839 /* ContentDetailModel+Extensions.swift in Sources */, 22C0513A23A4FBB0004D1223 /* ContentCategory+Persistence.swift in Sources */, diff --git a/Emitron/Emitron/UI/App Root/MainView.swift b/Emitron/Emitron/UI/App Root/MainView.swift index f01b71bd..c1c70384 100644 --- a/Emitron/Emitron/UI/App Root/MainView.swift +++ b/Emitron/Emitron/UI/App Root/MainView.swift @@ -79,7 +79,7 @@ private extension MainView { @ViewBuilder var tabBarView: some View { switch sessionController.sessionState { case .online : - TabNavView( + TabView( libraryView: { LibraryView( filters: dataManager.filters, @@ -100,7 +100,7 @@ private extension MainView { ) .environmentObject(tabViewModel) case .offline: - TabNavView( + TabView( libraryView: OfflineView.init, myTutorialsView: OfflineView.init, downloadsView: downloadsView, diff --git a/Emitron/Emitron/UI/App Root/TabNavView.swift b/Emitron/Emitron/UI/App Root/TabNavView.swift index 891703c8..1d66f426 100644 --- a/Emitron/Emitron/UI/App Root/TabNavView.swift +++ b/Emitron/Emitron/UI/App Root/TabNavView.swift @@ -28,7 +28,7 @@ import SwiftUI -struct TabNavView< +struct TabView< LibraryView: View, DownloadsView: View, MyTutorialsView: View, @@ -56,7 +56,7 @@ struct TabNavView< } // MARK: - View -extension TabNavView: View { +extension TabView: View { var body: some View { TabView(selection: $model.selectedTab) { tab( @@ -107,9 +107,9 @@ private func tab( .accessibility(label: .init(text)) } -struct TabNavView_Previews: PreviewProvider { +struct TabView_Previews: PreviewProvider { static var previews: some View { - TabNavView( + TabView( libraryView: { Text("LIBRARY") }, myTutorialsView: { Text("MY TUTORIALS") }, downloadsView: { Text("DOWNLOADS") }, From b91bf77aa8c4b80b1ec0d14321b00333e35b0de8 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 07:51:33 -0500 Subject: [PATCH 50/69] MARK: - `private` --- Emitron/Emitron/UI/App Root/TabNavView.swift | 79 ++++++++++---------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/Emitron/Emitron/UI/App Root/TabNavView.swift b/Emitron/Emitron/UI/App Root/TabNavView.swift index 1d66f426..094f44d5 100644 --- a/Emitron/Emitron/UI/App Root/TabNavView.swift +++ b/Emitron/Emitron/UI/App Root/TabNavView.swift @@ -59,61 +59,64 @@ struct TabView< extension TabView: View { var body: some View { TabView(selection: $model.selectedTab) { - tab( - content: libraryView, - text: .library, - imageName: "library", - tag: .library - ) + tab( + content: libraryView, + text: .library, + imageName: "library", + tab: .library + ) - tab( - content: downloadsView, - text: .downloads, - imageName: "downloadTabInactive", - tag: .downloads - ) + tab( + content: downloadsView, + text: .downloads, + imageName: "downloadTabInactive", + tab: .downloads + ) - tab( - content: myTutorialsView, - text: .myTutorials, - imageName: "myTutorials", - tag: .myTutorials - ) + tab( + content: myTutorialsView, + text: .myTutorials, + imageName: "myTutorials", + tab: .myTutorials + ) - tab( - content: settingsView, - text: .settings, - imageName: "settings", - tag: .settings - ) + tab( + content: settingsView, + text: .settings, + imageName: "settings", + tab: .settings + ) + } } .accentColor(.accent) } } +struct TabView_Previews: PreviewProvider { + static var previews: some View { + TabView( + libraryView: { Text("LIBRARY") }, + myTutorialsView: { Text("MY TUTORIALS") }, + downloadsView: { Text("DOWNLOADS") }, + settingsView: { Text("SETTINGS") } + ).environmentObject(TabViewModel()) + } +} + + +// MARK: - private private func tab( content: () -> Content, text: String, imageName: String, - tag: MainTab + tab: MainTab ) -> some View { NavigationView(content: content) .tabItem { Text(text) Image(imageName) } - .tag(tag) - .navigationViewStyle(StackNavigationViewStyle()) + .tag(tab) + .navigationViewStyle(.stack) .accessibility(label: .init(text)) } - -struct TabView_Previews: PreviewProvider { - static var previews: some View { - TabView( - libraryView: { Text("LIBRARY") }, - myTutorialsView: { Text("MY TUTORIALS") }, - downloadsView: { Text("DOWNLOADS") }, - settingsView: { Text("SETTINGS") } - ).environmentObject(TabViewModel()) - } -} From 4aa1dd77dbdd5a08468143d9f1bbeb44f795dd0f Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 09:23:33 -0500 Subject: [PATCH 51/69] Date.now --- Emitron/Emitron/App.swift | 2 +- .../Data Synchronisation/ProgressEngine.swift | 4 +- .../Data Synchronisation/SyncEngine.swift | 2 +- .../Displayable/ContentDisplayable.swift | 2 +- .../Emitron/Downloads/DownloadService.swift | 2 +- .../ContentDetailModel+Extensions.swift | 2 +- .../Emitron/Extensions/Date+Extensions.swift | 16 +++++-- Emitron/Emitron/Models/Download.swift | 2 +- Emitron/Emitron/Models/Progression.swift | 4 +- .../PersistenceStore+Synchronisation.swift | 16 +++---- Emitron/Emitron/Protocols/Refreshable.swift | 2 +- .../Emitron/Sessions/SessionController.swift | 6 ++- .../Content Detail/ContentDetailView.swift | 20 ++++---- .../Models/Mocks/Permissions+Mocks.swift | 12 +++-- .../Persistence/Models/PersistenceMocks.swift | 46 ++++++++++--------- 15 files changed, 77 insertions(+), 61 deletions(-) diff --git a/Emitron/Emitron/App.swift b/Emitron/Emitron/App.swift index 4eb8eb65..8a6e2e41 100644 --- a/Emitron/Emitron/App.swift +++ b/Emitron/Emitron/App.swift @@ -197,7 +197,7 @@ private extension App { func setupAppReview() { if NSUbiquitousKeyValueStore.default.object(forKey: LookupKey.requestReview) == nil { - NSUbiquitousKeyValueStore.default.set(Date().timeIntervalSince1970, forKey: LookupKey.requestReview) + NSUbiquitousKeyValueStore.default.set(Date.now.timeIntervalSince1970, forKey: LookupKey.requestReview) } } } diff --git a/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift b/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift index 461e01b8..6403fc95 100644 --- a/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift +++ b/Emitron/Emitron/Data Synchronisation/ProgressEngine.swift @@ -167,8 +167,8 @@ final class ProgressEngine { id: -1, target: target ?? content?.duration ?? 0, progress: progress, - createdAt: Date(), - updatedAt: Date(), + createdAt: .now, + updatedAt: .now, contentID: contentID ) } diff --git a/Emitron/Emitron/Data Synchronisation/SyncEngine.swift b/Emitron/Emitron/Data Synchronisation/SyncEngine.swift index 6756fddc..0c370212 100644 --- a/Emitron/Emitron/Data Synchronisation/SyncEngine.swift +++ b/Emitron/Emitron/Data Synchronisation/SyncEngine.swift @@ -309,7 +309,7 @@ extension SyncEngine: SyncAction { try persistenceStore.createBookmarkSyncRequest(for: contentID) // 2. Create cache update and pass to repository - let bookmark = Bookmark(id: -1, createdAt: Date(), contentID: contentID) + let bookmark = Bookmark(id: -1, createdAt: .now, contentID: contentID) let cacheUpdate = DataCacheUpdate(bookmarks: [bookmark]) repository.apply(update: cacheUpdate) diff --git a/Emitron/Emitron/Displayable/ContentDisplayable.swift b/Emitron/Emitron/Displayable/ContentDisplayable.swift index d15c20d6..0a6e6718 100644 --- a/Emitron/Emitron/Displayable/ContentDisplayable.swift +++ b/Emitron/Emitron/Displayable/ContentDisplayable.swift @@ -131,7 +131,7 @@ protocol ContentListDisplayable: Ownable { extension ContentListDisplayable { var releasedAtDateTimeString: String { var start = releasedAt.cardString - if Calendar.current.isDate(Date(), inSameDayAs: releasedAt) { + if Calendar.current.isDate(.now, inSameDayAs: releasedAt) { start = .today } diff --git a/Emitron/Emitron/Downloads/DownloadService.swift b/Emitron/Emitron/Downloads/DownloadService.swift index db26f2cf..f03919a3 100644 --- a/Emitron/Emitron/Downloads/DownloadService.swift +++ b/Emitron/Emitron/Downloads/DownloadService.swift @@ -316,7 +316,7 @@ extension DownloadService { .log() case .success(let attachment): download.remoteURL = attachment.url - download.lastValidatedAt = Date() + download.lastValidatedAt = .now download.state = .readyForDownload } diff --git a/Emitron/Emitron/Extensions/ContentDetailModel+Extensions.swift b/Emitron/Emitron/Extensions/ContentDetailModel+Extensions.swift index c36af766..52437483 100644 --- a/Emitron/Emitron/Extensions/ContentDetailModel+Extensions.swift +++ b/Emitron/Emitron/Extensions/ContentDetailModel+Extensions.swift @@ -31,7 +31,7 @@ import UIKit extension Content { var contentSummaryMetadataString: String { var start = releasedAt.cardString - if Calendar.current.isDate(Date(), inSameDayAs: releasedAt) { + if Calendar.current.isDate(.now, inSameDayAs: releasedAt) { start = .today } diff --git a/Emitron/Emitron/Extensions/Date+Extensions.swift b/Emitron/Emitron/Extensions/Date+Extensions.swift index 31202a8e..7af1d2af 100644 --- a/Emitron/Emitron/Extensions/Date+Extensions.swift +++ b/Emitron/Emitron/Extensions/Date+Extensions.swift @@ -29,6 +29,17 @@ import Foundation extension Date { + @available( + iOS, deprecated: 15, + message: "Delete this extension now; it was added to Foundation." + ) + static var now: Self { .init() } // swiftlint:disable:this let_var_whitespace + + static var topOfTheHour: Date { + let cmpts = Calendar.current.dateComponents([.year, .month, .day, .hour], from: .now) + return Calendar.current.date(from: cmpts)! + } + var cardString: String { let formatter = DateFormatter.cardDateFormatter return formatter.string(from: self) @@ -42,9 +53,4 @@ extension Date { func equalEnough(to otherDate: Date, epsilon: Double = 0.001) -> Bool { abs(timeIntervalSince(otherDate)) < epsilon } - - static var topOfTheHour: Date { - let cmpts = Calendar.current.dateComponents([.year, .month, .day, .hour], from: Date()) - return Calendar.current.date(from: cmpts)! - } } diff --git a/Emitron/Emitron/Models/Download.swift b/Emitron/Emitron/Models/Download.swift index 766a86d7..14a65077 100644 --- a/Emitron/Emitron/Models/Download.swift +++ b/Emitron/Emitron/Models/Download.swift @@ -93,7 +93,7 @@ extension Download { static func create(for content: Content) -> Download { Download( id: UUID(), - requestedAt: Date(), + requestedAt: .now, lastValidatedAt: nil, fileName: nil, remoteURL: nil, diff --git a/Emitron/Emitron/Models/Progression.swift b/Emitron/Emitron/Models/Progression.swift index 08b84d13..9014d5d3 100644 --- a/Emitron/Emitron/Models/Progression.swift +++ b/Emitron/Emitron/Models/Progression.swift @@ -79,8 +79,8 @@ extension Progression { id: -1, target: content.duration, progress: progress, - createdAt: Date(), - updatedAt: Date(), + createdAt: .now, + updatedAt: .now, contentID: content.id ) } diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift b/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift index 6d8f9c29..f1aa4df1 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Synchronisation.swift @@ -47,7 +47,7 @@ extension PersistenceStore { contentID: contentID, category: .bookmark, type: .createBookmark, - date: Date(), + date: .now, attributes: [] ) try syncRequest.save(db) @@ -73,7 +73,7 @@ extension PersistenceStore { associatedRecordID: bookmarkID, category: .bookmark, type: .deleteBookmark, - date: Date(), + date: .now, attributes: [] ) try syncRequest.save(db) @@ -92,7 +92,7 @@ extension PersistenceStore { .filter(SyncRequest.Columns.category == SyncRequest.Category.progress.rawValue) .fetchOne(db) { request.type = .markContentComplete - request.date = Date() + request.date = .now syncRequest = request } else { // Need to create a new one @@ -100,7 +100,7 @@ extension PersistenceStore { contentID: contentID, category: .progress, type: .markContentComplete, - date: Date(), + date: .now, attributes: [] ) } @@ -119,7 +119,7 @@ extension PersistenceStore { .filter(SyncRequest.Columns.category == SyncRequest.Category.progress.rawValue) .fetchOne(db) { request.type = .updateProgress - request.date = Date() + request.date = .now request.attributes = [.progress(progress)] syncRequest = request } else { @@ -128,7 +128,7 @@ extension PersistenceStore { contentID: contentID, category: .progress, type: .updateProgress, - date: Date(), + date: .now, attributes: [.progress(progress)] ) } @@ -148,7 +148,7 @@ extension PersistenceStore { .fetchOne(db) { request.type = .deleteProgression request.associatedRecordID = progressionID - request.date = Date() + request.date = .now syncRequest = request } else { // Need to create a new one @@ -157,7 +157,7 @@ extension PersistenceStore { associatedRecordID: progressionID, category: .progress, type: .deleteProgression, - date: Date(), + date: .now, attributes: [] ) } diff --git a/Emitron/Emitron/Protocols/Refreshable.swift b/Emitron/Emitron/Protocols/Refreshable.swift index a4858120..162d2822 100644 --- a/Emitron/Emitron/Protocols/Refreshable.swift +++ b/Emitron/Emitron/Protocols/Refreshable.swift @@ -76,6 +76,6 @@ enum RefreshableTimeSpan: Int { case short = 1 var date: Date { - Date().dateByAddingNumberOfDays(-rawValue) + .now.dateByAddingNumberOfDays(-rawValue) } } diff --git a/Emitron/Emitron/Sessions/SessionController.swift b/Emitron/Emitron/Sessions/SessionController.swift index ccfff6a3..771767be 100644 --- a/Emitron/Emitron/Sessions/SessionController.swift +++ b/Emitron/Emitron/Sessions/SessionController.swift @@ -97,9 +97,11 @@ final class SessionController: NSObject, UserModelController, ObservablePrePostF var hasCurrentDownloadPermissions: Bool { guard user?.canDownload == true else { return false } - if case .loaded(let date) = permissionState, + if + case .loaded(let date) = permissionState, let permissionsLastConfirmedDate = date, - Date().timeIntervalSince(permissionsLastConfirmedDate) < .videoPlaybackOfflinePermissionsCheckPeriod { + Date.now.timeIntervalSince(permissionsLastConfirmedDate) < .videoPlaybackOfflinePermissionsCheckPeriod + { return true } return false diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index c67ba018..ffd38b1c 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -95,15 +95,17 @@ private extension ContentDetailView { checkReviewRequest = true } .onAppear { - if checkReviewRequest { - guard let lastPrompted = NSUbiquitousKeyValueStore.default.object(forKey: LookupKey.requestReview) as? TimeInterval else { return } - let lastPromptedDate = Date(timeIntervalSince1970: lastPrompted) - let currentDate = Date() - if case .completed = dynamicContentViewModel.viewProgress { - if isPastTwoWeeks(currentDate, from: lastPromptedDate) { - NotificationCenter.default.post(name: .requestReview, object: nil) - NSUbiquitousKeyValueStore.default.set(Date().timeIntervalSince1970, forKey: LookupKey.requestReview) - } + guard + checkReviewRequest, + let lastPrompted = NSUbiquitousKeyValueStore.default.object(forKey: LookupKey.requestReview) as? TimeInterval + else { return } + + let lastPromptedDate = Date(timeIntervalSince1970: lastPrompted) + let currentDate = Date.now + if case .completed = dynamicContentViewModel.viewProgress { + if isPastTwoWeeks(currentDate, from: lastPromptedDate) { + NotificationCenter.default.post(name: .requestReview, object: nil) + NSUbiquitousKeyValueStore.default.set(Date.now.timeIntervalSince1970, forKey: LookupKey.requestReview) } } } diff --git a/Emitron/emitronTests/Models/Mocks/Permissions+Mocks.swift b/Emitron/emitronTests/Models/Mocks/Permissions+Mocks.swift index c46070d1..2bd8c3e8 100644 --- a/Emitron/emitronTests/Models/Mocks/Permissions+Mocks.swift +++ b/Emitron/emitronTests/Models/Mocks/Permissions+Mocks.swift @@ -31,10 +31,12 @@ import struct Foundation.Date extension Permission { static var downloadVideos: Permission { - Permission(id: 431, - name: "Download Videos", - tag: .download, - createdAt: Date(), - updatedAt: Date()) + .init( + id: 431, + name: "Download Videos", + tag: .download, + createdAt: .now, + updatedAt: .now + ) } } diff --git a/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift b/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift index d18fb976..87b5dfa0 100644 --- a/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift +++ b/Emitron/emitronTests/Persistence/Models/PersistenceMocks.swift @@ -31,29 +31,33 @@ import Foundation enum PersistenceMocks { static var content: Content { - Content(id: 1, - uri: "rw://betamax/collections/1", - name: "Sample Contents", - descriptionHtml: "Description", - descriptionPlainText: "Description", - releasedAt: Date(), - free: false, - professional: true, - difficulty: .intermediate, - contentType: .screencast, - duration: 1234, - videoIdentifier: nil, - cardArtworkURL: URL(string: "https://example.com/card_artwork.png")!, - technologyTriple: "Some Tech", - contributors: "HELLO", - groupID: nil, - ordinal: 0) + Content( + id: 1, + uri: "rw://betamax/collections/1", + name: "Sample Contents", + descriptionHtml: "Description", + descriptionPlainText: "Description", + releasedAt: Date(), + free: false, + professional: true, + difficulty: .intermediate, + contentType: .screencast, + duration: 1234, + videoIdentifier: nil, + cardArtworkURL: URL(string: "https://example.com/card_artwork.png")!, + technologyTriple: "Some Tech", + contributors: "HELLO", + groupID: nil, + ordinal: 0 + ) } @discardableResult static func download(for content: Content) -> Download { - Download(id: UUID(), - requestedAt: Date(), - state: .pending, - contentID: content.id) + Download( + id: .init(), + requestedAt: .now, + state: .pending, + contentID: content.id + ) } } From 096ab58ff259933f126c5ddbbb2d67966bc37136 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 09:46:11 -0500 Subject: [PATCH 52/69] Scroll to top of non-detail views --- .../Data/ViewModels/TabViewModel.swift | 19 ++++++++++++++++++- .../{TabNavView.swift => TabView.swift} | 19 +++++++++++++++++-- .../Shared/Content List/ContentListView.swift | 6 +++++- 3 files changed, 40 insertions(+), 4 deletions(-) rename Emitron/Emitron/UI/App Root/{TabNavView.swift => TabView.swift} (88%) diff --git a/Emitron/Emitron/Data/ViewModels/TabViewModel.swift b/Emitron/Emitron/Data/ViewModels/TabViewModel.swift index f73a23fe..d80452ac 100644 --- a/Emitron/Emitron/Data/ViewModels/TabViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/TabViewModel.swift @@ -27,14 +27,31 @@ // THE SOFTWARE. import Combine +import SwiftUI -enum MainTab: Hashable { +enum MainTab { case library case downloads case myTutorials case settings } +// MARK: - Environment +extension MainTab: EnvironmentKey { + static var defaultValue: Self { .library } +} + +extension EnvironmentValues { + var mainTab: MainTab { + get { self[MainTab.self] } + set { self[MainTab.self] = newValue } + } +} + +// MARK: - Hashable +extension MainTab: Hashable { } + +// MARK: - final class TabViewModel: ObservableObject { @Published var selectedTab: MainTab = .library } diff --git a/Emitron/Emitron/UI/App Root/TabNavView.swift b/Emitron/Emitron/UI/App Root/TabView.swift similarity index 88% rename from Emitron/Emitron/UI/App Root/TabNavView.swift rename to Emitron/Emitron/UI/App Root/TabView.swift index 094f44d5..c9987731 100644 --- a/Emitron/Emitron/UI/App Root/TabNavView.swift +++ b/Emitron/Emitron/UI/App Root/TabView.swift @@ -58,7 +58,22 @@ struct TabView< // MARK: - View extension TabView: View { var body: some View { - TabView(selection: $model.selectedTab) { + ScrollViewReader { proxy in + SwiftUI.TabView( + selection: .init( + get: { model.selectedTab }, + set: { selection in + switch model.selectedTab { + case selection: + withAnimation { + proxy.scrollTo(selection, anchor: .top) + } + default: + model.selectedTab = selection + } + } + ) + ) { tab( content: libraryView, text: .library, @@ -103,7 +118,6 @@ struct TabView_Previews: PreviewProvider { } } - // MARK: - private private func tab( content: () -> Content, @@ -117,6 +131,7 @@ private func tab( Image(imageName) } .tag(tab) + .environment(\.mainTab, tab) // for scrolling to top .navigationViewStyle(.stack) .accessibility(label: .init(text)) } diff --git a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift index 65d5d431..3678cb94 100644 --- a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift @@ -49,6 +49,7 @@ struct ContentListView { @State private var deleteSubscriptions: Set = [] @EnvironmentObject private var messageBus: MessageBus + @Environment(\.mainTab) private var mainTab } // MARK: - View @@ -125,7 +126,10 @@ private extension ContentListView { var listView: some View { List { - Section(header: header) { + Section( + header: header + .id(mainTab) // for scrolling to top + ) { cardsView loadMoreView } From b69912cadd33b9f2c37ec20e5191bc2b7d75cb22 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 10:53:03 -0500 Subject: [PATCH 53/69] Clean up ContentDetailView headers --- .../Content Detail/ContentDetailView.swift | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index ffd38b1c..5107d71e 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -70,11 +70,7 @@ private extension ContentDetailView { GeometryReader { geometry in ScrollView { VStack { - if content.professional && !canStreamPro { - headerImageLockedProContent(for: geometry.size.width) - } else { - headerImagePlayableContent(for: geometry.size.width) - } + headerImage(width: geometry.size.width) ContentSummaryView(content: content, dynamicContentViewModel: dynamicContentViewModel) .padding([.leading, .trailing], 20) @@ -142,12 +138,15 @@ private extension ContentDetailView { } } - func isPastTwoWeeks(_ currentWeek: Date, from lastWeek: Date) -> Bool { - let components = Calendar.current.dateComponents([.weekOfYear], from: lastWeek, to: currentWeek) - return components.weekOfYear ?? 0 >= 2 + @ViewBuilder func headerImage(width: Double) -> some View { + if content.professional && !canStreamPro { + headerImageLockedProContent(for: width) + } else { + headerImagePlayableContent(for: width) + } } - func headerImagePlayableContent(for width: CGFloat) -> some View { + func headerImagePlayableContent(for width: Double) -> some View { VStack(spacing: 0) { ZStack(alignment: .center) { VerticalFadeImageView( @@ -160,11 +159,13 @@ private extension ContentDetailView { continueOrPlayButton } - progressBar + if case .inProgress(let progress) = dynamicContentViewModel.viewProgress { + ProgressBarView(progress: progress, isRounded: false) + } } } - func headerImageLockedProContent(for width: CGFloat) -> some View { + func headerImageLockedProContent(for width: Double) -> some View { ZStack { VerticalFadeImageView( imageURL: content.cardArtworkURL, @@ -176,11 +177,9 @@ private extension ContentDetailView { ProContentLockedOverlayView() } } - - var progressBar: ProgressBarView? { - guard case .inProgress(let progress) = dynamicContentViewModel.viewProgress - else { return nil } - return .init(progress: progress, isRounded: false) + func isPastTwoWeeks(_ currentWeek: Date, from lastWeek: Date) -> Bool { + let components = Calendar.current.dateComponents([.weekOfYear], from: lastWeek, to: currentWeek) + return components.weekOfYear ?? 0 >= 2 } } From 65186b76ce88701df59c8beb6d6118714a260b71 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 11:46:16 -0500 Subject: [PATCH 54/69] Scroll to top of all views --- .../Data/ViewModels/TabViewModel.swift | 41 ++++++++++++------- Emitron/Emitron/UI/App Root/TabView.swift | 11 +++-- .../Content Detail/ContentDetailView.swift | 2 + .../Shared/Content List/ContentListView.swift | 8 +++- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/Emitron/Emitron/Data/ViewModels/TabViewModel.swift b/Emitron/Emitron/Data/ViewModels/TabViewModel.swift index d80452ac..f2dbf0e5 100644 --- a/Emitron/Emitron/Data/ViewModels/TabViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/TabViewModel.swift @@ -29,29 +29,42 @@ import Combine import SwiftUI -enum MainTab { - case library - case downloads - case myTutorials - case settings +final class TabViewModel: ObservableObject { + enum MainTab { + case library + case downloads + case myTutorials + case settings + } + + /// An ID that tells a scroll view proxy what view to scroll to when tapping an already-selected tab. + struct ScrollToTopID { + let mainTab: MainTab + let detail: Bool + } + + @Published var selectedTab: MainTab = .library + + var showingDetailView = Dictionary( + uniqueKeysWithValues: zip(MainTab.allCases, AnyIterator { false }) + ) } +// MARK: - CaseIterable +extension TabViewModel.MainTab: CaseIterable { } + // MARK: - Environment -extension MainTab: EnvironmentKey { +extension TabViewModel.MainTab: EnvironmentKey { static var defaultValue: Self { .library } } extension EnvironmentValues { - var mainTab: MainTab { - get { self[MainTab.self] } - set { self[MainTab.self] = newValue } + var mainTab: TabViewModel.MainTab { + get { self[TabViewModel.MainTab.self] } + set { self[TabViewModel.MainTab.self] = newValue } } } // MARK: - Hashable -extension MainTab: Hashable { } +extension TabViewModel.ScrollToTopID: Hashable { } -// MARK: - -final class TabViewModel: ObservableObject { - @Published var selectedTab: MainTab = .library -} diff --git a/Emitron/Emitron/UI/App Root/TabView.swift b/Emitron/Emitron/UI/App Root/TabView.swift index c9987731..2a74364d 100644 --- a/Emitron/Emitron/UI/App Root/TabView.swift +++ b/Emitron/Emitron/UI/App Root/TabView.swift @@ -66,7 +66,12 @@ extension TabView: View { switch model.selectedTab { case selection: withAnimation { - proxy.scrollTo(selection, anchor: .top) + proxy.scrollTo( + TabViewModel.ScrollToTopID( + mainTab: selection, detail: model.showingDetailView[selection]! + ), + anchor: .top + ) } default: model.selectedTab = selection @@ -123,7 +128,7 @@ private func tab( content: () -> Content, text: String, imageName: String, - tab: MainTab + tab: TabViewModel.MainTab ) -> some View { NavigationView(content: content) .tabItem { @@ -131,7 +136,7 @@ private func tab( Image(imageName) } .tag(tab) - .environment(\.mainTab, tab) // for scrolling to top + .environment(\.mainTab, tab) // for constructing `ScrollToTopID`s .navigationViewStyle(.stack) .accessibility(label: .init(text)) } diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index 5107d71e..d4c247ca 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -46,6 +46,7 @@ struct ContentDetailView { @EnvironmentObject private var sessionController: SessionController @EnvironmentObject private var messageBus: MessageBus + @Environment(\.mainTab) private var mainTab @State private var currentlyDisplayedVideoPlaybackViewModel: VideoPlaybackViewModel? private let videoCompletedNotification = NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime) @@ -71,6 +72,7 @@ private extension ContentDetailView { ScrollView { VStack { headerImage(width: geometry.size.width) + .id(TabViewModel.ScrollToTopID(mainTab: mainTab, detail: true)) ContentSummaryView(content: content, dynamicContentViewModel: dynamicContentViewModel) .padding([.leading, .trailing], 20) diff --git a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift index 3678cb94..cd74c77a 100644 --- a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift @@ -49,6 +49,7 @@ struct ContentListView { @State private var deleteSubscriptions: Set = [] @EnvironmentObject private var messageBus: MessageBus + @EnvironmentObject private var tabViewModel: TabViewModel @Environment(\.mainTab) private var mainTab } @@ -57,6 +58,7 @@ extension ContentListView: View { var body: some View { contentView .onAppear { + tabViewModel.showingDetailView[mainTab] = false UIApplication.dismissKeyboard() reloadIfRequired() } @@ -103,7 +105,9 @@ private extension ContentListView { content: partialContent, childContentsViewModel: contentRepository.childContentsViewModel(for: partialContent.id), dynamicContentViewModel: contentRepository.dynamicContentViewModel(for: partialContent.id) - ), + ).onAppear { + tabViewModel.showingDetailView[mainTab] = true + }, // This EmptyView and the 0 opacity below are used for `label` // instead of the CardView, in order to hide navigation chevrons on the right. label: EmptyView.init @@ -128,7 +132,7 @@ private extension ContentListView { List { Section( header: header - .id(mainTab) // for scrolling to top + .id(TabViewModel.ScrollToTopID(mainTab: mainTab, detail: false)) ) { cardsView loadMoreView From e69f8ddc63c658b41a34a9a9cf1414e6a99f77f2 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 10:27:17 -0500 Subject: [PATCH 55/69] Clean up ContentDetailView --- .../Content Detail/ContentDetailView.swift | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index d4c247ca..40000ee5 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -86,7 +86,7 @@ private extension ContentDetailView { } } } - .navigationTitle(Text("")) + .navigationTitle("") .navigationBarTitleDisplayMode(.inline) .background(Color.background) .onReceive(videoCompletedNotification) { _ in @@ -95,17 +95,13 @@ private extension ContentDetailView { .onAppear { guard checkReviewRequest, - let lastPrompted = NSUbiquitousKeyValueStore.default.object(forKey: LookupKey.requestReview) as? TimeInterval + case .completed = dynamicContentViewModel.viewProgress, + let lastPrompted = NSUbiquitousKeyValueStore.default.object(forKey: LookupKey.requestReview) as? TimeInterval, + isPastTwoWeeks(.now, from: .init(timeIntervalSince1970: lastPrompted)) else { return } - let lastPromptedDate = Date(timeIntervalSince1970: lastPrompted) - let currentDate = Date.now - if case .completed = dynamicContentViewModel.viewProgress { - if isPastTwoWeeks(currentDate, from: lastPromptedDate) { - NotificationCenter.default.post(name: .requestReview, object: nil) - NSUbiquitousKeyValueStore.default.set(Date.now.timeIntervalSince1970, forKey: LookupKey.requestReview) - } - } + NotificationCenter.default.post(name: .requestReview, object: nil) + NSUbiquitousKeyValueStore.default.set(Date.now.timeIntervalSince1970, forKey: LookupKey.requestReview) } } @@ -133,7 +129,7 @@ private extension ContentDetailView { } else { HStack { Spacer() - ProgressView().scaleEffect(1.0, anchor: .center) + ProgressView().scaleEffect(1, anchor: .center) Spacer() } } From cf6ce48a7b607da681a11690525d8a87f1628f5f Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 07:24:22 -0500 Subject: [PATCH 56/69] Clean up another file that had "id"s in it --- .../Emitron/Downloads/DownloadProcessor.swift | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/Emitron/Emitron/Downloads/DownloadProcessor.swift b/Emitron/Emitron/Downloads/DownloadProcessor.swift index 260a6eed..3c327b9c 100644 --- a/Emitron/Emitron/Downloads/DownloadProcessor.swift +++ b/Emitron/Emitron/Downloads/DownloadProcessor.swift @@ -47,25 +47,15 @@ protocol DownloadProcessorDelegate: AnyObject { private extension URLSessionDownloadTask { var downloadID: UUID? { - get { - guard let taskDescription = taskDescription else { return .none } - return UUID(uuidString: taskDescription) - } - set { - taskDescription = newValue?.uuidString ?? "" - } + get { taskDescription.flatMap(UUID.init) } + set { taskDescription = newValue?.uuidString ?? "" } } } private extension AVAssetDownloadTask { var downloadID: UUID? { - get { - guard let taskDescription = taskDescription else { return .none } - return UUID(uuidString: taskDescription) - } - set { - taskDescription = newValue?.uuidString ?? "" - } + get { taskDescription.flatMap(UUID.init) } + set { taskDescription = newValue?.uuidString ?? "" } } } @@ -185,9 +175,10 @@ extension DownloadProcessor: AVAssetDownloadDelegate { } func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) { - - guard let downloadID = assetDownloadTask.downloadID, - let delegate = delegate else { return } + guard + let downloadID = assetDownloadTask.downloadID, + let delegate = delegate + else { return } let download = delegate.downloadProcessor(self, downloadModelForDownloadWithID: downloadID) guard let localURL = download?.localURL else { return } From acb6bc9cbf6e7af471cc981a34c2d571d1267dd6 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 11:50:08 -0500 Subject: [PATCH 57/69] Delete old bug workaround --- Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift index 5078e797..01af3511 100644 --- a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift +++ b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift @@ -82,13 +82,8 @@ struct MyTutorialsView { self.domainRepository = domainRepository } - @State private var state: MyTutorialsState - - // We need to pull these in to pass them to the settings view. We don't actually use them here. - // I think this is a bug. - @EnvironmentObject private var sessionController: SessionController - @EnvironmentObject private var tabViewModel: TabViewModel @EnvironmentObject private var downloadService: DownloadService + @State private var state: MyTutorialsState private let inProgressRepository: InProgressRepository private let completedRepository: CompletedRepository @@ -159,7 +154,8 @@ private extension MyTutorialsView { reloadCompleted = false } } - }) + } + ) .padding(.top, .sidePadding) } .padding(.horizontal, .sidePadding) From 9be0a03224f5c29ea5782e11e78f10af52430914 Mon Sep 17 00:00:00 2001 From: Brian Douglas Moakley Date: Mon, 4 Oct 2021 15:15:06 -0400 Subject: [PATCH 58/69] Removes gate for free accounts --- Emitron/Emitron/UI/App Root/MainView.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Emitron/Emitron/UI/App Root/MainView.swift b/Emitron/Emitron/UI/App Root/MainView.swift index c1c70384..43315741 100644 --- a/Emitron/Emitron/UI/App Root/MainView.swift +++ b/Emitron/Emitron/UI/App Root/MainView.swift @@ -60,11 +60,7 @@ private extension MainView { } else { switch sessionController.permissionState { case .loaded: - if sessionController.hasPermissionToUseApp { - tabBarView - } else { - LogoutView() - } + tabBarView case .notLoaded, .loading: PermissionsLoadingView() case .error: From 596bd64efec326623af36ee9daa398203011d3d5 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 12:17:08 -0500 Subject: [PATCH 59/69] Clean up `User` --- .../Data/ViewModels/TabViewModel.swift | 1 - Emitron/Emitron/Models/User.swift | 61 +++++++++---------- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/Emitron/Emitron/Data/ViewModels/TabViewModel.swift b/Emitron/Emitron/Data/ViewModels/TabViewModel.swift index f2dbf0e5..a9cb0461 100644 --- a/Emitron/Emitron/Data/ViewModels/TabViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/TabViewModel.swift @@ -67,4 +67,3 @@ extension EnvironmentValues { // MARK: - Hashable extension TabViewModel.ScrollToTopID: Hashable { } - diff --git a/Emitron/Emitron/Models/User.swift b/Emitron/Emitron/Models/User.swift index 25ab3e09..9acccc80 100644 --- a/Emitron/Emitron/Models/User.swift +++ b/Emitron/Emitron/Models/User.swift @@ -29,8 +29,6 @@ import struct Foundation.URL public struct User: Equatable, Codable { - - // MARK: - Properties public let externalID: String public let email: String public let username: String @@ -38,30 +36,10 @@ public struct User: Equatable, Codable { public let name: String public let token: String let permissions: [Permission]? - - public var canStreamPro: Bool { - guard let permissions = permissions else { return false } - - return !permissions.filter { $0.tag == .streamPro }.isEmpty - } - - public var canStream: Bool { - guard let permissions = permissions else { return false } - - return !permissions.filter { $0.tag == .streamBeginner }.isEmpty - } - - public var canDownload: Bool { - guard let permissions = permissions else { return false } - - return !permissions.filter { $0.tag == .download }.isEmpty - } - - public var hasPermissionToUseApp: Bool { - canStreamPro || canStream || canDownload - } - - // MARK: - Initializers +} + +// MARK: - internal +extension User { init?(dictionary: [String: String]) { guard let externalID = dictionary["external_id"], @@ -71,8 +49,7 @@ public struct User: Equatable, Codable { let avatarURL = URL(string: avatarURLString), let name = dictionary["name"]?.replacingOccurrences(of: "+", with: " "), let token = dictionary["token"] - else - { return nil } + else { return nil } self.externalID = externalID self.email = email @@ -82,8 +59,26 @@ public struct User: Equatable, Codable { self.token = token permissions = .none } - - private init(user: User, permissions: [Permission]) { + + func with(permissions: [Permission]) -> User { + .init(user: self, permissions: permissions) + } +} + +// MARK: public +public extension User { + var canStreamPro: Bool { can(.streamPro) } + var canStream: Bool { can(.streamBeginner) } + var canDownload: Bool { can(.download) } + + var hasPermissionToUseApp: Bool { + canStreamPro || canStream || canDownload + } +} + +// MARK: - private +private extension User { + init(user: User, permissions: [Permission]) { externalID = user.externalID email = user.email username = user.username @@ -92,8 +87,8 @@ public struct User: Equatable, Codable { token = user.token self.permissions = permissions } - - func with(permissions: [Permission]) -> User { - User(user: self, permissions: permissions) + + private func can(_ tag: Permission.Tag) -> Bool { + permissions?.lazy.map(\.tag).contains(tag) == true } } From c4848edbebc9f66e2f1ab09615223fc09b879684 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 10 Feb 2022 02:43:20 +0300 Subject: [PATCH 60/69] Fix Warning: Non-constant range: argument must be an integer literal --- Emitron/Emitron/UI/Generic/PagingIndicatorView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emitron/Emitron/UI/Generic/PagingIndicatorView.swift b/Emitron/Emitron/UI/Generic/PagingIndicatorView.swift index 2a4f4b22..09464d92 100644 --- a/Emitron/Emitron/UI/Generic/PagingIndicatorView.swift +++ b/Emitron/Emitron/UI/Generic/PagingIndicatorView.swift @@ -34,7 +34,7 @@ struct PagingIndicatorView: View { var body: some View { HStack(spacing: 9) { - ForEach(0.. Date: Thu, 10 Feb 2022 04:03:08 +0300 Subject: [PATCH 61/69] increase completion time for testRequestDownloadAddsDownloadToCollection --- Emitron/emitronTests/Downloads/DownloadServiceTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift index 1d0a1426..f050cc27 100644 --- a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift @@ -409,7 +409,7 @@ class DownloadServiceTest: XCTestCase { } .record() - let completion = try wait(for: recorder.completion, timeout: 10) + let completion = try wait(for: recorder.completion, timeout: 20) XCTAssert(completion == .finished) // Adds downloads to the collection and the individual episodes From db30fca985da38a90b1d11f9ed7fd36516baaeda Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Wed, 9 Feb 2022 11:04:59 -0500 Subject: [PATCH 62/69] Standardize formatting --- .../ContentRepository.swift | 198 ++++++++++-------- .../DownloadRepository.swift | 60 +++--- .../LibraryRepository.swift | 39 ++-- .../DataCacheChildContentsViewModel.swift | 34 +-- .../ViewModels/VideoPlaybackViewModel.swift | 20 +- ...tSummaryState+ContentListDisplayable.swift | 95 +++------ .../Networking/Services/ContentsService.swift | 31 ++- .../Emitron/Networking/Services/Service.swift | 21 +- .../Protocols/ContentPaginatable.swift | 10 +- .../UI/Library/Filtering/FiltersView.swift | 2 - Emitron/Emitron/UI/Library/LibraryView.swift | 4 +- .../Shared/Content List/ContentListView.swift | 7 +- 12 files changed, 258 insertions(+), 263 deletions(-) diff --git a/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift b/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift index 1debdef6..342d7e77 100644 --- a/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift @@ -29,65 +29,40 @@ import Combine class ContentRepository: ObservableObject, ContentPaginatable { + var invalidationPublisher: AnyPublisher? { nil } + let repository: Repository - let contentsService: ContentsService - let downloadAction: DownloadAction - weak var syncAction: SyncAction? - let serviceAdapter: ContentServiceAdapter! let messageBus: MessageBus let settingsManager: SettingsManager let sessionController: SessionController + private(set) weak var syncAction: SyncAction? - private (set) var currentPage: Int = 1 - private (set) var totalContentNum: Int = 0 - - // This should be @Published, but it /sometimes/ crashes the app with EXC_BAD_ACCESS - // when you try and reference it. Which is handy. - var contents: [ContentListDisplayable] = [] { - willSet { - objectWillChange.send() - } - } + private let contentsService: ContentsService + private let downloadAction: DownloadAction + private let serviceAdapter: ContentServiceAdapter! - // This should be @Published too, but it crashes the compiler (Version 11.3 (11C29)) - // Let's see if we actually need it to be @Published... - var state: DataState = .initial - private var contentIDs: [Int] = [] private var contentSubscription: AnyCancellable? // Provide a value for this in a subclass to subscribe to invalidation notifications - var invalidationPublisher: AnyPublisher? { nil } private var invalidationSubscription: AnyCancellable? - - var isEmpty: Bool { - contents.isEmpty - } - - var nonPaginationParameters = [Parameter]() { - didSet { - if state != .initial { reload() } + + // MARK: - ContentPaginatable + + private(set) var currentPage = 1 + + // This should be @Published too, but it crashes the compiler (Version 11.3 (11C29)) + // Let's see if we actually need it to be @Published... + var state: DataState = .initial + + private(set) var totalContentNum = 0 + + // This should be @Published, but it /sometimes/ crashes the app with EXC_BAD_ACCESS + // when you try and reference it. Which is handy. + var contents: [ContentListDisplayable] = [] { + willSet { + objectWillChange.send() } } - - // Initialiser - init(repository: Repository, - contentsService: ContentsService, - downloadAction: DownloadAction, - syncAction: SyncAction, - serviceAdapter: ContentServiceAdapter?, - messageBus: MessageBus, - settingsManager: SettingsManager, - sessionController: SessionController) { - self.repository = repository - self.contentsService = contentsService - self.downloadAction = downloadAction - self.syncAction = syncAction - self.serviceAdapter = serviceAdapter - self.messageBus = messageBus - self.settingsManager = settingsManager - self.sessionController = sessionController - configureInvalidationSubscription() - } func loadMore() { if state == .loading || state == .loadingAdditional { @@ -127,7 +102,7 @@ class ContentRepository: ObservableObject, ContentPaginatable { } func reload() { - if state == .loading || state == .loadingAdditional { + if [.loading, .loadingAdditional].contains(state) { return } @@ -138,7 +113,7 @@ class ContentRepository: ObservableObject, ContentPaginatable { // Reset current page to 1 currentPage = startingPage - serviceAdapter.findContent(parameters: nonPaginationParameters) { [weak self] result in + serviceAdapter.findContent(parameters: nonPaginationParameters) { [weak self] result in guard let self = self else { return } @@ -160,50 +135,37 @@ class ContentRepository: ObservableObject, ContentPaginatable { } } } - - private func configureContentSubscription() { - contentSubscription = repository - .contentSummaryState(for: contentIDs) - .removeDuplicates() - .sink(receiveCompletion: { [weak self] error in - guard let self = self else { return } - - Failure - .repositoryLoad(from: String(describing: type(of: self)), reason: "Unable to receive content summary update: \(error)") - .log() - }, receiveValue: { [weak self] contentSummaryStates in - guard let self = self else { return } - - self.contents = contentSummaryStates - }) - } - - private func configureInvalidationSubscription() { - if let invalidationPublisher = invalidationPublisher { - invalidationSubscription = invalidationPublisher - .sink { [weak self] in - guard let self = self else { return } - - // If we're invalidating the cache then we need to set this to initial status again - self.state = .initial - // We're not gonna broadcast this change. If you do it'll wreak havoc with the content - // list and nav view—where the nav link for the currently displayed detail view disappears - // from underneath us. Instead we check the state of this repo each time the content - // listing appears. This doesn't feel that great, but it's what seems to work. - } + + // MARK: - + + + var nonPaginationParameters: [Parameter] = [] { + didSet { + if state != .initial { + reload() + } } } - func dynamicContentViewModel(for contentID: Int) -> DynamicContentViewModel { - DynamicContentViewModel( - contentID: contentID, - repository: repository, - downloadAction: downloadAction, - syncAction: syncAction, - messageBus: messageBus, - settingsManager: settingsManager, - sessionController: sessionController - ) + init( + repository: Repository, + contentsService: ContentsService, + downloadAction: DownloadAction, + syncAction: SyncAction, + serviceAdapter: ContentServiceAdapter! = nil, + messageBus: MessageBus, + settingsManager: SettingsManager, + sessionController: SessionController + ) { + self.repository = repository + self.contentsService = contentsService + self.downloadAction = downloadAction + self.syncAction = syncAction + self.serviceAdapter = serviceAdapter + self.messageBus = messageBus + self.settingsManager = settingsManager + self.sessionController = sessionController + configureInvalidationSubscription() } func childContentsViewModel(for contentID: Int) -> ChildContentsViewModel { @@ -220,3 +182,59 @@ class ContentRepository: ObservableObject, ContentPaginatable { ) } } + +// MARK: - internal +extension ContentRepository { + var isEmpty: Bool { contents.isEmpty } + + func dynamicContentViewModel(for contentID: Int) -> DynamicContentViewModel { + .init( + contentID: contentID, + repository: repository, + downloadAction: downloadAction, + syncAction: syncAction, + messageBus: messageBus, + settingsManager: settingsManager, + sessionController: sessionController + ) + } +} + +// MARK: - private +private extension ContentRepository { + func configureInvalidationSubscription() { + if let invalidationPublisher = invalidationPublisher { + invalidationSubscription = invalidationPublisher + .sink { [weak self] in + guard let self = self else { return } + + // If we're invalidating the cache then we need to set this to initial status again + self.state = .initial + // We're not gonna broadcast this change. If you do it'll wreak havoc with the content + // list and nav view—where the nav link for the currently displayed detail view disappears + // from underneath us. Instead we check the state of this repo each time the content + // listing appears. This doesn't feel that great, but it's what seems to work. + } + } + } + + func configureContentSubscription() { + contentSubscription = repository + .contentSummaryState(for: contentIDs) + .removeDuplicates() + .sink( + receiveCompletion: { [weak self] error in + guard let self = self else { return } + + Failure + .repositoryLoad(from: String(describing: type(of: self)), reason: "Unable to receive content summary update: \(error)") + .log() + }, + receiveValue: { [weak self] contentSummaryStates in + guard let self = self else { return } + + self.contents = contentSummaryStates + } + ) + } +} diff --git a/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift b/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift index c0343f02..6358c1a9 100644 --- a/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift @@ -33,23 +33,26 @@ final class DownloadRepository: ContentRepository { private var contentSubscription: AnyCancellable? - init(repository: Repository, - contentsService: ContentsService, - downloadService: DownloadService, - syncAction: SyncAction, - messageBus: MessageBus, - settingsManager: SettingsManager, - sessionController: SessionController) { + init( + repository: Repository, + contentsService: ContentsService, + downloadService: DownloadService, + syncAction: SyncAction, + messageBus: MessageBus, + settingsManager: SettingsManager, + sessionController: SessionController + ) { self.downloadService = downloadService // Don't need the repository or the service adapter - super.init(repository: repository, - contentsService: contentsService, - downloadAction: downloadService, - syncAction: syncAction, - serviceAdapter: nil, - messageBus: messageBus, - settingsManager: settingsManager, - sessionController: sessionController) + super.init( + repository: repository, + contentsService: contentsService, + downloadAction: downloadService, + syncAction: syncAction, + messageBus: messageBus, + settingsManager: settingsManager, + sessionController: sessionController + ) } override func loadMore() { @@ -78,17 +81,20 @@ final class DownloadRepository: ContentRepository { private func configureSubscription() { contentSubscription = downloadService - .downloadList() - .sink(receiveCompletion: { [weak self] error in - guard let self = self else { return } - self.state = .failed - Failure - .loadFromPersistentStore(from: String(describing: type(of: self)), reason: "Unable to retrieve download content summaries: \(error)") - .log() - }, receiveValue: { [weak self] contentSummaryStates in - guard let self = self else { return } - self.contents = contentSummaryStates - self.state = .hasData - }) + .downloadList() + .sink( + receiveCompletion: { [weak self] error in + guard let self = self else { return } + self.state = .failed + Failure + .loadFromPersistentStore(from: String(describing: type(of: self)), reason: "Unable to retrieve download content summaries: \(error)") + .log() + }, + receiveValue: { [weak self] contentSummaryStates in + guard let self = self else { return } + self.contents = contentSummaryStates + self.state = .hasData + } + ) } } diff --git a/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift b/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift index 3aab84ac..a655ae9d 100644 --- a/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift @@ -29,25 +29,28 @@ import Combine final class LibraryRepository: ContentRepository { - - init(repository: Repository, - contentsService: ContentsService, - downloadAction: DownloadAction, - syncAction: SyncAction, - serviceAdapter: ContentServiceAdapter?, - messageBus: MessageBus, - settingsManager: SettingsManager, - sessionController: SessionController, - filters: Filters) { + init( + repository: Repository, + contentsService: ContentsService, + downloadAction: DownloadAction, + syncAction: SyncAction, + serviceAdapter: ContentServiceAdapter, + messageBus: MessageBus, + settingsManager: SettingsManager, + sessionController: SessionController, + filters: Filters + ) { self.filters = filters - super.init(repository: repository, - contentsService: contentsService, - downloadAction: downloadAction, - syncAction: syncAction, - serviceAdapter: serviceAdapter, - messageBus: messageBus, - settingsManager: settingsManager, - sessionController: sessionController) + super.init( + repository: repository, + contentsService: contentsService, + downloadAction: downloadAction, + syncAction: syncAction, + serviceAdapter: serviceAdapter, + messageBus: messageBus, + settingsManager: settingsManager, + sessionController: sessionController + ) nonPaginationParameters = filters.appliedParameters } diff --git a/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift b/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift index 8c907eed..c3583e82 100644 --- a/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/DataCacheChildContentsViewModel.swift @@ -29,22 +29,26 @@ final class DataCacheChildContentsViewModel: ChildContentsViewModel { private let service: ContentsService - init(parentContentID: Int, - downloadAction: DownloadAction, - syncAction: SyncAction?, - repository: Repository, - service: ContentsService, - messageBus: MessageBus, - settingsManager: SettingsManager, - sessionController: SessionController) { + init( + parentContentID: Int, + downloadAction: DownloadAction, + syncAction: SyncAction?, + repository: Repository, + service: ContentsService, + messageBus: MessageBus, + settingsManager: SettingsManager, + sessionController: SessionController + ) { self.service = service - super.init(parentContentID: parentContentID, - downloadAction: downloadAction, - syncAction: syncAction, - repository: repository, - messageBus: messageBus, - settingsManager: settingsManager, - sessionController: sessionController) + super.init( + parentContentID: parentContentID, + downloadAction: downloadAction, + syncAction: syncAction, + repository: repository, + messageBus: messageBus, + settingsManager: settingsManager, + sessionController: sessionController + ) } override func loadContentDetailsIntoCache() { diff --git a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift index 714c0c20..bb75289a 100644 --- a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift @@ -113,15 +113,17 @@ final class VideoPlaybackViewModel { private var shouldBePlaying = false let settingsManager: SettingsManager - init(contentID: Int, - repository: Repository, - videosService: VideosService, - contentsService: ContentsService, - syncAction: SyncAction?, - sessionController: SessionController, - messageBus: MessageBus, - settingsManager: SettingsManager, - dismissClosure: @escaping () -> Void = { }) { + init( + contentID: Int, + repository: Repository, + videosService: VideosService, + contentsService: ContentsService, + syncAction: SyncAction?, + sessionController: SessionController, + messageBus: MessageBus, + settingsManager: SettingsManager, + dismissClosure: @escaping () -> Void = { } + ) { initialContentID = contentID self.repository = repository self.videosService = videosService diff --git a/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift b/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift index 18fe61cd..e8533d63 100644 --- a/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift +++ b/Emitron/Emitron/Displayable/ContentSummaryState+ContentListDisplayable.swift @@ -28,74 +28,18 @@ import Foundation +// MARK: - internal +extension ContentSummaryState { + var professional: Bool { content.professional } + var free: Bool { content.free } + var groupID: Int? { content.groupID } +} + +// MARK: - ContentListDisplayable extension ContentSummaryState: ContentListDisplayable { - // MARK: - Proxied from content - var id: Int { - content.id - } - - var releasedAt: Date { - content.releasedAt - } - - var duration: Int { - content.duration - } - - var name: String { - content.name - } - - var descriptionPlainText: String { - content.descriptionPlainText.replacingOccurrences(of: "\n", with: "") - } - - var professional: Bool { - content.professional - } - - var free: Bool { - content.free - } - - var cardArtworkURL: URL? { - content.cardArtworkURL - } - - var contentType: ContentType { - content.contentType - } - - var ordinal: Int? { - content.ordinal - } - - var technologyTripleString: String { - content.technologyTriple - } - - var contentSummaryMetadataString: String { - content.contentSummaryMetadataString - } - - var contributorString: String { - content.contributors - } - - var groupID: Int? { - content.groupID - } - - var videoIdentifier: Int? { - content.videoIdentifier - } - - // MARK: - Proxied from Other Records - var parentName: String? { - parentContent?.name - } - - // MARK: - Evaluated + var id: Int { content.id } + var name: String { content.name } + var cardViewSubtitle: String { if domains.count == 1 { return domains.first!.name @@ -104,4 +48,21 @@ extension ContentSummaryState: ContentListDisplayable { } return "" } + + var descriptionPlainText: String { + content.descriptionPlainText.replacingOccurrences(of: "\n", with: "") + } + + var releasedAt: Date { content.releasedAt } + var duration: Int { content.duration } + + var parentName: String? { parentContent?.name } // Proxied from Other Records + + var contentType: ContentType { content.contentType } + var cardArtworkURL: URL? { content.cardArtworkURL } + var ordinal: Int? { content.ordinal } + var technologyTripleString: String { content.technologyTriple } + var contentSummaryMetadataString: String { content.contentSummaryMetadataString } + var contributorString: String { content.contributors } + var videoIdentifier: Int? { content.videoIdentifier } } diff --git a/Emitron/Emitron/Networking/Services/ContentsService.swift b/Emitron/Emitron/Networking/Services/ContentsService.swift index 466d259b..20ddd207 100644 --- a/Emitron/Emitron/Networking/Services/ContentsService.swift +++ b/Emitron/Emitron/Networking/Services/ContentsService.swift @@ -26,22 +26,31 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -class ContentsService: Service { +final class ContentsService: Service { } - // MARK: - Internal - func allContents(parameters: [Parameter], - completion: @escaping (_ response: Result) -> Void) { +// MARK: - internal +extension ContentsService { + func allContents( + parameters: [Parameter], + completion: @escaping (_ response: Result) -> Void + ) { let request = ContentsRequest() - makeAndProcessRequest(request: request, - parameters: parameters, - completion: completion) + makeAndProcessRequest( + request: request, + parameters: parameters, + completion: completion + ) } - func contentDetails(for id: Int, - completion: @escaping (_ response: Result) -> Void) { + func contentDetails( + for id: Int, + completion: @escaping (_ response: Result) -> Void + ) { let request = ContentDetailsRequest(id: id) - makeAndProcessRequest(request: request, - completion: completion) + makeAndProcessRequest( + request: request, + completion: completion + ) } func getBeginPlaybackToken(completion: @escaping(_ response: Result) -> Void) { diff --git a/Emitron/Emitron/Networking/Services/Service.swift b/Emitron/Emitron/Networking/Services/Service.swift index f8f723e5..b192cf9d 100644 --- a/Emitron/Emitron/Networking/Services/Service.swift +++ b/Emitron/Emitron/Networking/Services/Service.swift @@ -44,12 +44,12 @@ class Service { var isAuthenticated: Bool { !networkClient.authToken.isEmpty } // MARK: - Internal - func makeAndProcessRequest( - request: R, + func makeAndProcessRequest( + request: Request, parameters: [Parameter]? = nil, - completion: @escaping (Result) -> Void) { - - let handleResponse: (Result) -> Void = { result in + completion: @escaping (Result) -> Void + ) { + let handleResponse = { result in DispatchQueue.main.async { completion(result) } @@ -60,12 +60,11 @@ class Service { } let task = session.dataTask(with: urlRequest) { data, response, error in - - guard let httpResponse = response as? HTTPURLResponse, - 200..<300 ~= httpResponse.statusCode else { - let statusCode = (response as? HTTPURLResponse)?.statusCode ?? 0 - handleResponse(.failure(.requestFailed(error, statusCode))) - return + let statusCode = (response as? HTTPURLResponse)?.statusCode + guard statusCode.map((200..<300).contains) == true + else { + handleResponse(.failure(.requestFailed(error, statusCode ?? 0))) + return } do { diff --git a/Emitron/Emitron/Protocols/ContentPaginatable.swift b/Emitron/Emitron/Protocols/ContentPaginatable.swift index 0e2b519c..5cff530c 100644 --- a/Emitron/Emitron/Protocols/ContentPaginatable.swift +++ b/Emitron/Emitron/Protocols/ContentPaginatable.swift @@ -42,12 +42,8 @@ protocol ContentPaginatable: ObservableObject where ObjectWillChangePublisher == } extension ContentPaginatable { - // All content that currently conforms to this prootocol is 1-indexed - var startingPage: Int { - 1 - } + // All content that currently conforms to this protocol is 1-indexed + var startingPage: Int { 1 } - var defaultPageSize: Int { - 20 - } + var defaultPageSize: Int { 20 } } diff --git a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift index ddfde2fe..ce573f2f 100644 --- a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift @@ -35,9 +35,7 @@ struct FiltersView: View { var body: some View { VStack { - HStack(alignment: .center) { - Rectangle() .frame(width: 27, height: 27, alignment: .center) .foregroundColor(.clear) diff --git a/Emitron/Emitron/UI/Library/LibraryView.swift b/Emitron/Emitron/UI/Library/LibraryView.swift index 9eadaf62..9b99e70b 100644 --- a/Emitron/Emitron/UI/Library/LibraryView.swift +++ b/Emitron/Emitron/UI/Library/LibraryView.swift @@ -43,9 +43,7 @@ struct LibraryView: View { var body: some View { contentView - .navigationTitle( - Text(String.library) - ) + .navigationTitle(Text(String.library)) .sheet(isPresented: $filtersPresented) { FiltersView(libraryRepository: libraryRepository, filters: filters) .background(Color.background.edgesIgnoringSafeArea(.all)) diff --git a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift index cd74c77a..74b990b7 100644 --- a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift @@ -187,9 +187,10 @@ private extension ContentListView { Spacer() ProgressView().scaleEffect(1.0, anchor: .center) Spacer() - }.padding() - .background(Color.background.edgesIgnoringSafeArea(.all)) - .onAppear(perform: contentRepository.loadMore) + } + .padding() + .background(Color.background.edgesIgnoringSafeArea(.all)) + .onAppear(perform: contentRepository.loadMore) } } From 2da72cd7c0e58587c30038cbc4b665919b500a4c Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Wed, 9 Feb 2022 11:07:29 -0500 Subject: [PATCH 63/69] Change `[Edge.Set.leading, .trailing]` to `.horizontal` --- Emitron/Emitron/UI/App Root/LoginView.swift | 2 +- Emitron/Emitron/UI/App Root/LogoutView.swift | 4 ++-- Emitron/Emitron/UI/Empty States/ErrorView.swift | 4 ++-- Emitron/Emitron/UI/Empty States/NoResultsView.swift | 4 ++-- Emitron/Emitron/UI/Empty States/OfflineView.swift | 4 ++-- .../Emitron/UI/Library/Filtering/FiltersHeaderView.swift | 2 +- Emitron/Emitron/UI/Library/Filtering/FiltersView.swift | 2 +- .../UI/Shared/Content Detail/ChildContentListingView.swift | 6 +++--- .../UI/Shared/Content Detail/ContentDetailView.swift | 2 +- .../Shared/Content Detail/ProContentLockedOverlayView.swift | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Emitron/Emitron/UI/App Root/LoginView.swift b/Emitron/Emitron/UI/App Root/LoginView.swift index 485ca20b..cfcc2263 100644 --- a/Emitron/Emitron/UI/App Root/LoginView.swift +++ b/Emitron/Emitron/UI/App Root/LoginView.swift @@ -93,7 +93,7 @@ struct LoginView: View { MainButtonView(title: "Sign In", type: .primary(withArrow: true)) { sessionController.login() } - .padding([.leading, .trailing], 18) + .padding(.horizontal, 18) .padding([.bottom], 38) } .background(Color.background) diff --git a/Emitron/Emitron/UI/App Root/LogoutView.swift b/Emitron/Emitron/UI/App Root/LogoutView.swift index f7f5c51d..5fb0c24d 100644 --- a/Emitron/Emitron/UI/App Root/LogoutView.swift +++ b/Emitron/Emitron/UI/App Root/LogoutView.swift @@ -49,7 +49,7 @@ struct LogoutView: View { .font(.uiLabel) .foregroundColor(.contentText) .multilineTextAlignment(.center) - .padding([.leading, .trailing], 55) + .padding(.horizontal, 55) Spacer() @@ -58,7 +58,7 @@ struct LogoutView: View { type: .destructive(withArrow: true), callback: sessionController.logout ) - .padding([.leading, .trailing], 18) + .padding(.horizontal, 18) .padding(.bottom, 38) } .background(Color.background) diff --git a/Emitron/Emitron/UI/Empty States/ErrorView.swift b/Emitron/Emitron/UI/Empty States/ErrorView.swift index 26377218..01278aa5 100644 --- a/Emitron/Emitron/UI/Empty States/ErrorView.swift +++ b/Emitron/Emitron/UI/Empty States/ErrorView.swift @@ -81,14 +81,14 @@ extension ErrorView: View { .font(.uiTitle2) .foregroundColor(.titleText) .multilineTextAlignment(.center) - .padding([.leading, .trailing, .bottom], 20) + .padding([.horizontal, .bottom], 20) Text(bodyText) .lineSpacing(8) .font(.uiLabel) .foregroundColor(.contentText) .multilineTextAlignment(.center) - .padding([.leading, .trailing], 20) + .padding(.horizontal, 20) Spacer() diff --git a/Emitron/Emitron/UI/Empty States/NoResultsView.swift b/Emitron/Emitron/UI/Empty States/NoResultsView.swift index 12b72e8d..8e41f1e3 100644 --- a/Emitron/Emitron/UI/Empty States/NoResultsView.swift +++ b/Emitron/Emitron/UI/Empty States/NoResultsView.swift @@ -63,7 +63,7 @@ extension NoResultsView: View { .foregroundColor(.titleText) .multilineTextAlignment(.center) .padding([.bottom], 20) - .padding([.leading, .trailing], 20) + .padding(.horizontal, 20) Text(contentScreen.detailMesage) .lineSpacing(5) @@ -71,7 +71,7 @@ extension NoResultsView: View { .foregroundColor(.contentText) .multilineTextAlignment(.center) .padding([.bottom], 20) - .padding([.leading, .trailing], 20) + .padding(.horizontal, 20) Spacer() diff --git a/Emitron/Emitron/UI/Empty States/OfflineView.swift b/Emitron/Emitron/UI/Empty States/OfflineView.swift index 782bf17e..31ca4901 100644 --- a/Emitron/Emitron/UI/Empty States/OfflineView.swift +++ b/Emitron/Emitron/UI/Empty States/OfflineView.swift @@ -38,14 +38,14 @@ struct OfflineView: View { .font(.uiTitle2) .foregroundColor(.titleText) .multilineTextAlignment(.center) - .padding([.leading, .trailing, .bottom], 20) + .padding([.horizontal, .bottom], 20) Text("Please check internet connection and try again.") .font(.uiLabel) .lineSpacing(8) .foregroundColor(.contentText) .multilineTextAlignment(.center) - .padding([.leading, .trailing], 20) + .padding(.horizontal, 20) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.background) diff --git a/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift b/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift index fd18a117..39ce25e9 100644 --- a/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift @@ -91,7 +91,7 @@ struct FiltersHeaderView: View { filter.isOn.toggle() filters.update(with: filter) }) - .padding([.leading, .trailing], 14) + .padding(.horizontal, 14) } } } diff --git a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift index ce573f2f..feaa8d74 100644 --- a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift @@ -79,7 +79,7 @@ struct FiltersView: View { // Which ones are currently being applied to the content listing applyFiltersButton() } - .padding([.leading, .trailing, .bottom], 18) + .padding([.horizontal, .bottom], 18) } .background(Color.background) } diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift index 93d25cea..25cee7c1 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift @@ -64,7 +64,7 @@ private extension ChildContentListingView { .foregroundColor(.titleText) .padding([.top, .bottom]) Spacer() - }.padding([.leading, .trailing], 20) + }.padding(.horizontal, 20) } } .listRowBackground(Color.background) @@ -92,7 +92,7 @@ private extension ChildContentListingView { LoadingView() Spacer() } - .listRowInsets(EdgeInsets()) + .listRowInsets(.init()) .listRowBackground(Color.background) .background(Color.background) } @@ -115,7 +115,7 @@ private extension ChildContentListingView { return ForEach(onlyContentWithVideoID, id: \.id) { model in episodeRow(model: model) - .listRowInsets(EdgeInsets()) + .listRowInsets(.init()) .listRowBackground(Color.background) } } diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index 40000ee5..3cc3de13 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -75,7 +75,7 @@ private extension ContentDetailView { .id(TabViewModel.ScrollToTopID(mainTab: mainTab, detail: true)) ContentSummaryView(content: content, dynamicContentViewModel: dynamicContentViewModel) - .padding([.leading, .trailing], 20) + .padding(.horizontal, 20) .background(Color.background) ChildContentListingView( diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift index c977311d..2ceb0460 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift @@ -43,7 +43,7 @@ struct ProContentLockedOverlayView: View { .multilineTextAlignment(.center) .font(.uiLabel) .foregroundColor(.titleText) - .padding([.leading, .trailing], 20) + .padding(.horizontal, 20) .lineLimit(3) .fixedSize(horizontal: false, vertical: true) } From 2ecc58e35cecd7b13fecaea6e27954835e300f0e Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Wed, 9 Feb 2022 11:09:12 -0500 Subject: [PATCH 64/69] Use implicit typing for `EdgeInsets.init` --- .../Emitron/UI/Library/Filtering/FiltersView.swift | 2 +- .../UI/Shared/Content List/ContentListView.swift | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift index feaa8d74..5688385b 100644 --- a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift @@ -64,7 +64,7 @@ struct FiltersView: View { .padding(.top, 20) constructScrollView() - .padding([.leading, .trailing, .top], 20) + .padding([.horizontal, .top], 20) HStack { diff --git a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift index 74b990b7..b6e1bda2 100644 --- a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift @@ -116,16 +116,18 @@ private extension ContentListView { } } .if(allowDelete) { $0.onDelete(perform: delete) } - .listRowInsets(EdgeInsets()) + .listRowInsets(.init()) .padding([.horizontal, .top], .sidePadding) .background(Color.background) } var allowDelete: Bool { - if case .downloads = contentScreen { + switch contentScreen { + case .downloads: return true + default: + return false } - return false } var listView: some View { @@ -137,7 +139,7 @@ private extension ContentListView { cardsView loadMoreView } - .listRowInsets(EdgeInsets()) + .listRowInsets(.init()) .textCase(nil) } .if(!allowDelete) { @@ -185,7 +187,7 @@ private extension ContentListView { if contentRepository.totalContentNum > contentRepository.contents.count { HStack { Spacer() - ProgressView().scaleEffect(1.0, anchor: .center) + ProgressView().scaleEffect(1, anchor: .center) Spacer() } .padding() From ed58b79f7cdd2fe9def74d88b442fa53db3806fb Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Wed, 9 Feb 2022 11:10:41 -0500 Subject: [PATCH 65/69] Organize access using extensions --- .../DownloadRepository.swift | 7 ++- .../LibraryRepository.swift | 8 ++-- Emitron/Emitron/Models/User.swift | 2 +- .../UI/Library/Filtering/FiltersView.swift | 29 ++++++++---- Emitron/Emitron/UI/Library/LibraryView.swift | 47 ++++++++++++------- .../ChildContentListingView.swift | 16 +++++-- 6 files changed, 72 insertions(+), 37 deletions(-) diff --git a/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift b/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift index 6358c1a9..5176ee77 100644 --- a/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/DownloadRepository.swift @@ -77,8 +77,11 @@ final class DownloadRepository: ContentRepository { sessionController: sessionController ) } - - private func configureSubscription() { +} + +// MARK: - private +private extension DownloadRepository { + func configureSubscription() { contentSubscription = downloadService .downloadList() diff --git a/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift b/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift index a655ae9d..9b11143d 100644 --- a/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/LibraryRepository.swift @@ -60,7 +60,9 @@ final class LibraryRepository: ContentRepository { nonPaginationParameters = filters.appliedParameters } } - var currentAppliedFilters: [Filter] { - filters.applied - } +} + +// MARK: internal +extension LibraryRepository { + var currentAppliedFilters: [Filter] { filters.applied } } diff --git a/Emitron/Emitron/Models/User.swift b/Emitron/Emitron/Models/User.swift index 9acccc80..373a2690 100644 --- a/Emitron/Emitron/Models/User.swift +++ b/Emitron/Emitron/Models/User.swift @@ -88,7 +88,7 @@ private extension User { self.permissions = permissions } - private func can(_ tag: Permission.Tag) -> Bool { + func can(_ tag: Permission.Tag) -> Bool { permissions?.lazy.map(\.tag).contains(tag) == true } } diff --git a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift index 5688385b..125120eb 100644 --- a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift @@ -29,9 +29,17 @@ import SwiftUI struct FiltersView: View { - @ObservedObject var libraryRepository: LibraryRepository - @ObservedObject var filters: Filters - @Environment(\.presentationMode) var presentationMode: Binding + @ObservedObject private var libraryRepository: LibraryRepository + @ObservedObject private var filters: Filters + @Environment(\.presentationMode) private var presentationMode: Binding + + init( + libraryRepository: LibraryRepository, + filters: Filters + ) { + self.libraryRepository = libraryRepository + self.filters = filters + } var body: some View { VStack { @@ -83,8 +91,11 @@ struct FiltersView: View { } .background(Color.background) } - - private func constructScrollView() -> some View { +} + +// MARK: - private +private extension FiltersView { + func constructScrollView() -> some View { ScrollView(.vertical, showsIndicators: false) { VStack(alignment: .leading, spacing: 12) { ForEach(filters.filterGroups, id: \.self) { filterGroup in @@ -95,15 +106,15 @@ struct FiltersView: View { } } - private func constructFilterView(filterGroup: FilterGroup) -> FiltersHeaderView { - FiltersHeaderView( + func constructFilterView(filterGroup: FilterGroup) -> FiltersHeaderView { + .init( filterGroup: filterGroup, filters: filters, isExpanded: filterGroup.numApplied > 0 ) } - private func applyFiltersButton() -> MainButtonView { + func applyFiltersButton() -> MainButtonView { let title = "Apply Filters" let buttonView = MainButtonView(title: title, type: .primary(withArrow: false)) { @@ -114,7 +125,7 @@ struct FiltersView: View { return buttonView } - private func revertBackToPreviousFilters() { + func revertBackToPreviousFilters() { // Update filters with the currentFilters on contentsMC, to keep them in sync (aka, remove them) // First, turn all applied off diff --git a/Emitron/Emitron/UI/Library/LibraryView.swift b/Emitron/Emitron/UI/Library/LibraryView.swift index 9b99e70b..ddecbe84 100644 --- a/Emitron/Emitron/UI/Library/LibraryView.swift +++ b/Emitron/Emitron/UI/Library/LibraryView.swift @@ -28,18 +28,19 @@ import SwiftUI -private extension CGFloat { - static let filterButtonSide: CGFloat = 27 - static let searchFilterPadding: CGFloat = 15 - static let filterSpacing: CGFloat = 6 - static let filtersPaddingTop: CGFloat = 12 -} - struct LibraryView: View { @EnvironmentObject private var downloadService: DownloadService - @ObservedObject var filters: Filters - @ObservedObject var libraryRepository: LibraryRepository - @State var filtersPresented = false + @ObservedObject private var filters: Filters + @ObservedObject private var libraryRepository: LibraryRepository + @State private var filtersPresented = false + + init( + filters: Filters, + libraryRepository: LibraryRepository + ) { + self.filters = filters + self.libraryRepository = libraryRepository + } var body: some View { contentView @@ -49,8 +50,11 @@ struct LibraryView: View { .background(Color.background.edgesIgnoringSafeArea(.all)) } } +} - private var contentControlsSection: some View { +// MARK: - private +private extension LibraryView { + var contentControlsSection: some View { VStack { searchAndFilterControls .padding(.top, 15) @@ -67,14 +71,14 @@ struct LibraryView: View { .background(Color.background) } - private var searchField: some View { + var searchField: some View { SearchFieldView(searchString: filters.searchStr) { searchString in filters.searchStr = searchString updateFilters() } } - private var searchAndFilterControls: some View { + var searchAndFilterControls: some View { HStack { searchField @@ -92,7 +96,7 @@ struct LibraryView: View { } } - private var numberAndSortView: some View { + var numberAndSortView: some View { HStack { Text("\(libraryRepository.totalContentNum) \(String.tutorials.capitalized)") .font(.uiLabelBold) @@ -120,7 +124,7 @@ struct LibraryView: View { } } - private var filtersView: some View { + var filtersView: some View { ScrollView(.horizontal, showsIndicators: false) { HStack(alignment: .top, spacing: .filterSpacing) { if filters.applied.count > 1 { @@ -148,17 +152,17 @@ struct LibraryView: View { .padding([.horizontal], -.sidePadding) } - private func updateFilters() { + func updateFilters() { filters.searchQuery = filters.searchStr libraryRepository.filters = filters } - private func changeSort() { + func changeSort() { filters.changeSortFilter() libraryRepository.filters = filters } - private var contentView: some View { + var contentView: some View { ContentListView( contentRepository: libraryRepository, downloadAction: downloadService, @@ -167,3 +171,10 @@ struct LibraryView: View { ) } } + +private extension CGFloat { + static let filterButtonSide: CGFloat = 27 + static let searchFilterPadding: CGFloat = 15 + static let filterSpacing: CGFloat = 6 + static let filtersPaddingTop: CGFloat = 12 +} diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift index 25cee7c1..8c7b70b3 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift @@ -29,11 +29,19 @@ import SwiftUI struct ChildContentListingView: View { - @ObservedObject var childContentsViewModel: ChildContentsViewModel - @Binding var currentlyDisplayedVideoPlaybackViewModel: VideoPlaybackViewModel? - @EnvironmentObject var sessionController: SessionController - @EnvironmentObject var messageBus: MessageBus + @ObservedObject private var childContentsViewModel: ChildContentsViewModel + @Binding private var currentlyDisplayedVideoPlaybackViewModel: VideoPlaybackViewModel? + @EnvironmentObject private var sessionController: SessionController + @EnvironmentObject private var messageBus: MessageBus + init( + childContentsViewModel: ChildContentsViewModel, + currentlyDisplayedVideoPlaybackViewModel: Binding + ) { + self.childContentsViewModel = childContentsViewModel + _currentlyDisplayedVideoPlaybackViewModel = currentlyDisplayedVideoPlaybackViewModel + } + var body: some View { childContentsViewModel.initialiseIfRequired() return courseDetailsSection From e68cbe934d9bdac0ccab5dd5c3e1683ea703e811 Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Tue, 8 Feb 2022 19:10:53 -0500 Subject: [PATCH 66/69] Document and organize FullscreenVideoPlayer --- Emitron/Emitron.xcodeproj/project.pbxproj | 12 +- .../Content Detail/ContentDetailView.swift | 18 ++- .../UI/Video/FullScreenVideoPlayer.swift | 129 ++++++++++++++++++ .../FullScreenVideoPlayerRepresentable.swift | 10 +- .../FullScreenVideoPlayerViewController.swift | 107 --------------- 5 files changed, 149 insertions(+), 127 deletions(-) create mode 100644 Emitron/Emitron/UI/Video/FullScreenVideoPlayer.swift delete mode 100644 Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index 277c146b..0ca82208 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -195,8 +195,7 @@ 22C3F126241A2655002812CB /* app-icon--default.beta-ipadpro@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 22C3F120241A2654002812CB /* app-icon--default.beta-ipadpro@2x.png */; }; 22C3F127241A2655002812CB /* app-icon--default.dev-ipad@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 22C3F121241A2655002812CB /* app-icon--default.dev-ipad@2x.png */; }; 22C3F155242795A1002812CB /* PortraitHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C3F154242795A1002812CB /* PortraitHostingController.swift */; }; - 22C3F15724279692002812CB /* FullScreenVideoPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C3F15624279692002812CB /* FullScreenVideoPlayerViewController.swift */; }; - 22C3F159242796EC002812CB /* FullScreenVideoPlayerRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C3F158242796EC002812CB /* FullScreenVideoPlayerRepresentable.swift */; }; + 22C3F159242796EC002812CB /* FullScreenVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C3F158242796EC002812CB /* FullScreenVideoPlayer.swift */; }; 22C4EADB23DB8A5A001A3FDA /* WatchStatsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C4EADA23DB8A5A001A3FDA /* WatchStatsService.swift */; }; 22C4EADD23DB8B33001A3FDA /* WatchStatsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C4EADC23DB8B33001A3FDA /* WatchStatsRequest.swift */; }; 22C4EADF23DC4338001A3FDA /* SyncRequest+WatchStat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C4EADE23DC4338001A3FDA /* SyncRequest+WatchStat.swift */; }; @@ -533,8 +532,7 @@ 22C3F120241A2654002812CB /* app-icon--default.beta-ipadpro@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "app-icon--default.beta-ipadpro@2x.png"; sourceTree = ""; }; 22C3F121241A2655002812CB /* app-icon--default.dev-ipad@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "app-icon--default.dev-ipad@2x.png"; sourceTree = ""; }; 22C3F154242795A1002812CB /* PortraitHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortraitHostingController.swift; sourceTree = ""; }; - 22C3F15624279692002812CB /* FullScreenVideoPlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenVideoPlayerViewController.swift; sourceTree = ""; }; - 22C3F158242796EC002812CB /* FullScreenVideoPlayerRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenVideoPlayerRepresentable.swift; sourceTree = ""; }; + 22C3F158242796EC002812CB /* FullScreenVideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenVideoPlayer.swift; sourceTree = ""; }; 22C4EADA23DB8A5A001A3FDA /* WatchStatsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchStatsService.swift; sourceTree = ""; }; 22C4EADC23DB8B33001A3FDA /* WatchStatsRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchStatsRequest.swift; sourceTree = ""; }; 22C4EADE23DC4338001A3FDA /* SyncRequest+WatchStat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SyncRequest+WatchStat.swift"; sourceTree = ""; }; @@ -1030,8 +1028,7 @@ 229F0AC323BB29230004DD4F /* Video */ = { isa = PBXGroup; children = ( - 22C3F15624279692002812CB /* FullScreenVideoPlayerViewController.swift */, - 22C3F158242796EC002812CB /* FullScreenVideoPlayerRepresentable.swift */, + 22C3F158242796EC002812CB /* FullScreenVideoPlayer.swift */, ); path = Video; sourceTree = ""; @@ -2023,7 +2020,6 @@ B60FF7BE23000ABD00F36B32 /* CardView.swift in Sources */, 8BFC74C32364A9BE001979F1 /* ContentScreen.swift in Sources */, B6DF2F9122CA00820081A3A3 /* Request.swift in Sources */, - 22C3F15724279692002812CB /* FullScreenVideoPlayerViewController.swift in Sources */, B62B9A8022DF76A500122CE8 /* MainView.swift in Sources */, B66778AC2305D2D4003EEBAB /* MainButtonView.swift in Sources */, B6D7DC3122C79743006DD325 /* SceneDelegate.swift in Sources */, @@ -2104,7 +2100,7 @@ 22C4EAEF23DEF91D001A3FDA /* SettingsKey.swift in Sources */, B6DF2FC022CA861C0081A3A3 /* RandomString.swift in Sources */, B629AB4C22E60BD70037F4D8 /* CourseHeaderView.swift in Sources */, - 22C3F159242796EC002812CB /* FullScreenVideoPlayerRepresentable.swift in Sources */, + 22C3F159242796EC002812CB /* FullScreenVideoPlayer.swift in Sources */, 22BFE75523D9905500495BA9 /* SnackbarView.swift in Sources */, 223D77DF23B6515A005BE95D /* ContentRepository.swift in Sources */, 2228E40723B999D500E103AA /* CategoryRepository.swift in Sources */, diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index 3cc3de13..538dc93f 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -55,12 +55,16 @@ struct ContentDetailView { // MARK: - View extension ContentDetailView: View { var body: some View { - ZStack { + if let model = currentlyDisplayedVideoPlaybackViewModel { + try? FullScreenVideoPlayer( + model: model, + messageBus: messageBus, + handleDismissal: { + currentlyDisplayedVideoPlaybackViewModel = nil + } + ) + } else { contentView - - if currentlyDisplayedVideoPlaybackViewModel != nil { - FullScreenVideoPlayerRepresentable(viewModel: $currentlyDisplayedVideoPlaybackViewModel, messageBus: messageBus) - } } } } @@ -177,7 +181,7 @@ private extension ContentDetailView { } func isPastTwoWeeks(_ currentWeek: Date, from lastWeek: Date) -> Bool { - let components = Calendar.current.dateComponents([.weekOfYear], from: lastWeek, to: currentWeek) - return components.weekOfYear ?? 0 >= 2 + Calendar.current.dateComponents([.weekOfYear], from: lastWeek, to: currentWeek) + .weekOfYear.map { $0 >= 2 } == true } } diff --git a/Emitron/Emitron/UI/Video/FullScreenVideoPlayer.swift b/Emitron/Emitron/UI/Video/FullScreenVideoPlayer.swift new file mode 100644 index 00000000..c88cd7a6 --- /dev/null +++ b/Emitron/Emitron/UI/Video/FullScreenVideoPlayer.swift @@ -0,0 +1,129 @@ +// Copyright (c) 2022 Razeware LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, +// distribute, sublicense, create a derivative work, and/or sell copies of the +// Software in any work that is designed, intended, or marketed for pedagogical or +// instructional purposes related to programming, coding, application development, +// or information technology. Permission for such use, copying, modification, +// merger, publication, distribution, sublicensing, creation of derivative works, +// or sale is expressly withheld. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import SwiftUI +import AVKit + +/// - Note: Could be simplified to `AVKit.VideoPlayer` using `fullScreenCover` +/// if a dismissal button were included with that, as `AVPlayerViewController` provides. +struct FullScreenVideoPlayer { + init( + model: VideoPlaybackViewModel, + messageBus: MessageBus, + handleDismissal: @escaping () -> Void + ) throws { + model.reloadIfRequired() + + do { + try model.verifyCanPlay() + } catch { + if let viewModelError = error as? VideoPlaybackViewModel.Error { + messageBus.post( + message: .init( + level: viewModelError.messageLevel, + message: viewModelError.localizedDescription, + autoDismiss: viewModelError.messageAutoDismiss + ) + ) + } + + throw error + } + + self.model = model + self.handleDismissal = handleDismissal + } + + private let model: VideoPlaybackViewModel + private let handleDismissal: () -> Void +} + +// MARK: - UIViewControllerRepresentable +extension FullScreenVideoPlayer: UIViewControllerRepresentable { + /// - Bug: This is a View Controller because without presenting an `AVPlayerViewController`, + /// specifically in `viewDidAppear` and no earlier, + /// the user will have to press the "expand to fullscreen (↕️)" button once to get it to change into a dismissal (❎) button, + /// even through the player is already in fullscreen mode. + /// + /// This also explains why `Presenter` is the `UIViewControllerType` of this `UIViewControllerRepresentable`, + /// instead of `AVPlayerViewController` (with this type being a `Coordinator` `NSObject` instead, purely for delegation). + final class Presenter: UIViewController { + // swiftlint:disable:next strict_fileprivate + fileprivate init( + model: VideoPlaybackViewModel, + handleDismissal: @escaping () -> Void + ) { + self.model = model + self.handleDismissal = handleDismissal + super.init(nibName: nil, bundle: nil) + } + + private let model: VideoPlaybackViewModel + private let handleDismissal: () -> Void + private var playerIsPresented = false + + @available(*, unavailable) required init?(coder: NSCoder) { + preconditionFailure("init(coder:) has not been implemented") + } + } + + func makeUIViewController(context: Context) -> Presenter { + .init( + model: model, + handleDismissal: handleDismissal + ) + } + + func updateUIViewController(_: UIViewControllerType, context _: Context) { } +} + +extension FullScreenVideoPlayer.Presenter { + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if !playerIsPresented { + let viewController = AVPlayerViewController() + viewController.player = model.player + viewController.delegate = self + present(viewController, animated: true) + playerIsPresented = true + model.play() + } + } +} + +// MARK: - AVPlayerViewControllerDelegate +extension FullScreenVideoPlayer.Presenter: AVPlayerViewControllerDelegate { + func playerViewController( + _: AVPlayerViewController, + willEndFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator + ) { + model.stop() + handleDismissal() + } +} diff --git a/Emitron/Emitron/UI/Video/FullScreenVideoPlayerRepresentable.swift b/Emitron/Emitron/UI/Video/FullScreenVideoPlayerRepresentable.swift index 6fe47038..7d8aedde 100644 --- a/Emitron/Emitron/UI/Video/FullScreenVideoPlayerRepresentable.swift +++ b/Emitron/Emitron/UI/Video/FullScreenVideoPlayerRepresentable.swift @@ -27,16 +27,16 @@ // THE SOFTWARE. import SwiftUI +import struct AVKit.VideoPlayer +import MapKit struct FullScreenVideoPlayerRepresentable: UIViewControllerRepresentable { @Binding var viewModel: VideoPlaybackViewModel? let messageBus: MessageBus - func makeUIViewController(context: UIViewControllerRepresentableContext) -> FullScreenVideoPlayerViewController { + func makeUIViewController(context: Context) -> FullScreenVideoPlayerViewController { .init(viewModel: $viewModel, messageBus: messageBus) } - - func updateUIViewController(_ uiViewController: FullScreenVideoPlayerViewController, context: UIViewControllerRepresentableContext) { - // No-op - } + + func updateUIViewController(_: UIViewControllerType, context _: Context) { } } diff --git a/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift b/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift deleted file mode 100644 index db0fc06d..00000000 --- a/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2022 Razeware LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, -// distribute, sublicense, create a derivative work, and/or sell copies of the -// Software in any work that is designed, intended, or marketed for pedagogical or -// instructional purposes related to programming, coding, application development, -// or information technology. Permission for such use, copying, modification, -// merger, publication, distribution, sublicensing, creation of derivative works, -// or sale is expressly withheld. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -import UIKit -import AVKit -import SwiftUI - -class FullScreenVideoPlayerViewController: UIViewController { - @Binding var viewModel: VideoPlaybackViewModel? - private var isFullscreen = false - private let messageBus: MessageBus - - init(viewModel: Binding, messageBus: MessageBus) { - _viewModel = viewModel - self.messageBus = messageBus - super.init(nibName: nil, bundle: nil) - - self.viewModel?.reloadIfRequired() - verifyVideoPlaybackAllowed() - } - - @available(*, unavailable) required init?(coder: NSCoder) { - preconditionFailure("init(coder:) has not been implemented") - } -} - -extension FullScreenVideoPlayerViewController { - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - if !isFullscreen { - let viewController = AVPlayerViewController() - viewController.player = viewModel?.player - viewController.delegate = self - present(viewController, animated: true) - viewModel?.play() - } - } -} - -extension FullScreenVideoPlayerViewController { - private func verifyVideoPlaybackAllowed() { - do { - try viewModel?.verifyCanPlay() - } catch { - if let viewModelError = error as? VideoPlaybackViewModel.Error { - messageBus.post( - message: Message( - level: viewModelError.messageLevel, - message: viewModelError.localizedDescription, - autoDismiss: viewModelError.messageAutoDismiss - ) - ) - } - disappear() - } - } - - private func disappear() { - self.viewModel?.stop() - dismiss(animated: true) { - self.viewModel = nil - } - } -} - -extension FullScreenVideoPlayerViewController: AVPlayerViewControllerDelegate { - func playerViewController( - _ playerViewController: AVPlayerViewController, - willBeginFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) { - isFullscreen = true - } - - func playerViewController( - _ playerViewController: AVPlayerViewController, - willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) { - coordinator.animate(alongsideTransition: nil) { context in - guard !context.isCancelled else { return } - // Exited fullscreen, so let's disappear ourselves - self.disappear() - } - } -} From 3f10f30726c87f06e3946f1b3bde448c22376f3e Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Thu, 10 Feb 2022 08:52:09 -0500 Subject: [PATCH 67/69] Revert to using a ZStack It looks better because the content doesn't turn blank underneath the transition! --- .../ContentRepository.swift | 1 - .../Content Detail/ContentDetailView.swift | 20 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift b/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift index 342d7e77..9e555055 100644 --- a/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift +++ b/Emitron/Emitron/Data/ContentRepositories/ContentRepository.swift @@ -138,7 +138,6 @@ class ContentRepository: ObservableObject, ContentPaginatable { // MARK: - - var nonPaginationParameters: [Parameter] = [] { didSet { if state != .initial { diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index 538dc93f..7aff0a54 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -55,16 +55,18 @@ struct ContentDetailView { // MARK: - View extension ContentDetailView: View { var body: some View { - if let model = currentlyDisplayedVideoPlaybackViewModel { - try? FullScreenVideoPlayer( - model: model, - messageBus: messageBus, - handleDismissal: { - currentlyDisplayedVideoPlaybackViewModel = nil - } - ) - } else { + ZStack { contentView + + if let model = currentlyDisplayedVideoPlaybackViewModel { + try? FullScreenVideoPlayer( + model: model, + messageBus: messageBus, + handleDismissal: { + currentlyDisplayedVideoPlaybackViewModel = nil + } + ) + } } } } From 27e3e9da2aa55403f46a09d102733aa81467e3df Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Thu, 10 Feb 2022 09:11:20 -0500 Subject: [PATCH 68/69] Remove "Download enqueued" banner message --- Emitron/Emitron/Constants.swift | 1 - Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift | 2 +- Emitron/Emitron/Downloads/DownloadService.swift | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Emitron/Emitron/Constants.swift b/Emitron/Emitron/Constants.swift index 5be0fd46..9d641687 100644 --- a/Emitron/Emitron/Constants.swift +++ b/Emitron/Emitron/Constants.swift @@ -67,7 +67,6 @@ extension String { static let progressRemovedError = "There was a problem removing progress." static let progressMarkedAsCompleteError = "There was a problem marking content as complete." - static let downloadRequestedSuccessfully = "Download enqueued." static let downloadRequestedButQueueInactive = "Download will begin when WiFi available." static let downloadNotPermitted = "Download not permitted." static let downloadContentNotFound = "Invalid download request." diff --git a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift index cb64c378..3319ab61 100644 --- a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift @@ -95,7 +95,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable }) { [weak self] result in switch result { case .downloadRequestedSuccessfully: - self?.messageBus.post(message: Message(level: .success, message: .downloadRequestedSuccessfully)) + break case .downloadRequestedButQueueInactive: self?.messageBus.post(message: Message(level: .warning, message: .downloadRequestedButQueueInactive)) } diff --git a/Emitron/Emitron/Downloads/DownloadService.swift b/Emitron/Emitron/Downloads/DownloadService.swift index f03919a3..3d550a93 100644 --- a/Emitron/Emitron/Downloads/DownloadService.swift +++ b/Emitron/Emitron/Downloads/DownloadService.swift @@ -200,7 +200,7 @@ extension DownloadService: DownloadAction { try? persistenceStore.download(forContentID: $0) } // 2. Is it already downloading? - let currentlyDownloading = downloads.filter { $0.isDownloading } + let currentlyDownloading = downloads.filter(\.isDownloading) let notYetDownloading = downloads.filter { !$0.isDownloading } return Future { promise in From 2e377c500eb6430528f06d8f896d288722bdbb0b Mon Sep 17 00:00:00 2001 From: Jessy Catterwaul Date: Thu, 10 Feb 2022 19:26:04 -0500 Subject: [PATCH 69/69] =?UTF-8?q?emitron-ios-1.0.9-s=F0=9F=90=BCrels-paper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Emitron/Emitron.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index 0ca82208..cffd0a6b 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -2379,7 +2379,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.8; + MARKETING_VERSION = 1.0.9; PRODUCT_BUNDLE_IDENTIFIER = "com.razeware.emitron.ios$(BUNDLE_ID_SUFFIX)"; PRODUCT_MODULE_NAME = Emitron; PRODUCT_NAME = raywenderlich; @@ -2584,7 +2584,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.8; + MARKETING_VERSION = 1.0.9; PRODUCT_BUNDLE_IDENTIFIER = "com.razeware.emitron.ios$(BUNDLE_ID_SUFFIX)"; PRODUCT_MODULE_NAME = Emitron; PRODUCT_NAME = raywenderlich; @@ -2610,7 +2610,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.8; + MARKETING_VERSION = 1.0.9; PRODUCT_BUNDLE_IDENTIFIER = "com.razeware.emitron.ios$(BUNDLE_ID_SUFFIX)"; PRODUCT_MODULE_NAME = Emitron; PRODUCT_NAME = raywenderlich;