From aa369facd94d270625e1b2e9a3c2f5368faa0f32 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Fri, 13 Nov 2020 06:25:20 +0200 Subject: [PATCH 01/69] Fix conform type to 'Hashable' Warning --- Emitron/Emitron/Models/Attachment.swift | 4 ++++ Emitron/Emitron/Models/PlaybackSpeed.swift | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/Emitron/Emitron/Models/Attachment.swift b/Emitron/Emitron/Models/Attachment.swift index 8627a471..fdb55690 100644 --- a/Emitron/Emitron/Models/Attachment.swift +++ b/Emitron/Emitron/Models/Attachment.swift @@ -80,6 +80,10 @@ struct Attachment: Codable { static var selectableCases: [Attachment.Kind] { [.sdVideoFile, .hdVideoFile] } + + func hash(into hasher: inout Hasher) { + hasher.combine(display) + } } var id: Int diff --git a/Emitron/Emitron/Models/PlaybackSpeed.swift b/Emitron/Emitron/Models/PlaybackSpeed.swift index 2977cb26..d4701a5f 100644 --- a/Emitron/Emitron/Models/PlaybackSpeed.swift +++ b/Emitron/Emitron/Models/PlaybackSpeed.swift @@ -66,3 +66,9 @@ enum PlaybackSpeed: Int, CaseIterable, SettingsSelectable { allCases } } + +extension PlaybackSpeed { + func hash(into hasher: inout Hasher) { + hasher.combine(rate) + } +} From 98ca94d38eddab80c0ae94db0168f5842139b371 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Fri, 13 Nov 2020 14:29:27 +0200 Subject: [PATCH 02/69] Added comment's explaining the addition of hash(into: ) and link to the swift bug report. --- Emitron/Emitron/Models/Attachment.swift | 1 + Emitron/Emitron/Models/PlaybackSpeed.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Emitron/Emitron/Models/Attachment.swift b/Emitron/Emitron/Models/Attachment.swift index fdb55690..f28c283e 100644 --- a/Emitron/Emitron/Models/Attachment.swift +++ b/Emitron/Emitron/Models/Attachment.swift @@ -81,6 +81,7 @@ struct Attachment: Codable { [.sdVideoFile, .hdVideoFile] } + // Added hash(into: ) funciton because of this bug: https://bugs.swift.org/browse/SR-13851 func hash(into hasher: inout Hasher) { hasher.combine(display) } diff --git a/Emitron/Emitron/Models/PlaybackSpeed.swift b/Emitron/Emitron/Models/PlaybackSpeed.swift index d4701a5f..53ea1672 100644 --- a/Emitron/Emitron/Models/PlaybackSpeed.swift +++ b/Emitron/Emitron/Models/PlaybackSpeed.swift @@ -68,6 +68,7 @@ enum PlaybackSpeed: Int, CaseIterable, SettingsSelectable { } extension PlaybackSpeed { + // Added hash(into: ) funciton because of this bug: https://bugs.swift.org/browse/SR-13851 func hash(into hasher: inout Hasher) { hasher.combine(rate) } From cf77f89d765d70207d5189817075ec446049cbe6 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Fri, 13 Nov 2020 16:53:33 +0200 Subject: [PATCH 03/69] Change hasher to use "display" to match the rest settings. --- Emitron/Emitron/Models/PlaybackSpeed.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emitron/Emitron/Models/PlaybackSpeed.swift b/Emitron/Emitron/Models/PlaybackSpeed.swift index 53ea1672..affd2193 100644 --- a/Emitron/Emitron/Models/PlaybackSpeed.swift +++ b/Emitron/Emitron/Models/PlaybackSpeed.swift @@ -70,6 +70,6 @@ enum PlaybackSpeed: Int, CaseIterable, SettingsSelectable { extension PlaybackSpeed { // Added hash(into: ) funciton because of this bug: https://bugs.swift.org/browse/SR-13851 func hash(into hasher: inout Hasher) { - hasher.combine(rate) + hasher.combine(display) } } From 52ae8e97b7906c8d6719b1387ad02591fe73ed3d Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Sun, 6 Dec 2020 05:34:10 +0200 Subject: [PATCH 04/69] Minor Update of Kingfisher SPM dependency --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 15b300cf..c6cf4204 100644 --- a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/onevcat/Kingfisher", "state": { "branch": null, - "revision": "0efb20f022501327f4028aa9bf7cd4a0d390bea0", - "version": "5.15.2" + "revision": "2a10bf41da75599a9f8e872dbd44fe0155a2e00c", + "version": "5.15.8" } }, { From 1167f68876ff75fe8aeba33d7cc61a732823ac35 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Sun, 6 Dec 2020 09:15:56 +0200 Subject: [PATCH 05/69] Center ActivityIndicator in Hstack --- .../Emitron/UI/Shared/Content List/ContentListView.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift index 54cfcbe7..95ded1dd 100644 --- a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift @@ -214,8 +214,11 @@ private extension ContentListView { if contentRepository.totalContentNum > contentRepository.contents.count { // HACK: To put it in the middle we have to wrap it in Geometry Reader GeometryReader { _ in - ActivityIndicator() - .onAppear(perform: contentRepository.loadMore) + HStack { + Spacer() + ActivityIndicator() + Spacer() + }.onAppear(perform: contentRepository.loadMore) } } } From ac20d7d844cedbafe4b971b91fdb19390ec22e78 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Sun, 6 Dec 2020 09:16:33 +0200 Subject: [PATCH 06/69] Remove Geometry Reader Hack --- Emitron/Emitron/UI/Shared/Content List/ContentListView.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift index 95ded1dd..ba141ca8 100644 --- a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift @@ -212,14 +212,11 @@ private extension ContentListView { @ViewBuilder var loadMoreView: some View { if contentRepository.totalContentNum > contentRepository.contents.count { - // HACK: To put it in the middle we have to wrap it in Geometry Reader - GeometryReader { _ in HStack { Spacer() ActivityIndicator() Spacer() }.onAppear(perform: contentRepository.loadMore) - } } } From 9624fc3d1df2e876a6a45d446971e743711b54ee Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Sun, 6 Dec 2020 09:51:20 +0200 Subject: [PATCH 07/69] Removed Color.backgroundColor Hack for spacing at the bottom. Changed Hstack background Color --- .../Emitron/UI/Shared/Content List/ContentListView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift index ba141ca8..da964ace 100644 --- a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift @@ -92,8 +92,6 @@ private extension ContentListView { SwiftUI.Group { cardsView loadMoreView - // Hack to make sure there's some spacing at the bottom of the list - Color.backgroundColor } } @@ -216,7 +214,9 @@ private extension ContentListView { Spacer() ActivityIndicator() Spacer() - }.onAppear(perform: contentRepository.loadMore) + }.padding() + .background(Color.backgroundColor.edgesIgnoringSafeArea(.all)) + .onAppear(perform: contentRepository.loadMore) } } From b32be87ed1ca8f8e5dcc7987b76ef303b1f4911f Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Sun, 6 Dec 2020 11:04:02 +0200 Subject: [PATCH 08/69] Removed Padding causing too much authors vertical spacing --- Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift | 1 - .../Emitron/UI/Shared/Content Detail/ContentSummaryView.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index fd82d806..ea4afc19 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -75,7 +75,6 @@ private extension ContentDetailView { ContentSummaryView(content: content, dynamicContentViewModel: dynamicContentViewModel) .padding([.leading, .trailing], 20) - .padding(.bottom, 37) } .listRowInsets(EdgeInsets()) .listRowBackground(Color.backgroundColor) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift index baaa1f21..c9968241 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift @@ -104,7 +104,6 @@ extension ContentSummaryView: View { .font(.uiCaption) .foregroundColor(.contentText) .lineLimit(2) - .padding(.top, 10) .lineSpacing(3) } } From 65ac10d79cfb5ad71b85737527784f119dc2edbe Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Sun, 6 Dec 2020 12:24:14 +0200 Subject: [PATCH 09/69] Added SettingsView to tabBar --- .../Emitron/Data/ViewModels/TabViewModel.swift | 1 + Emitron/Emitron/UI/App Root/MainView.swift | 8 ++++++-- Emitron/Emitron/UI/App Root/TabNavView.swift | 16 ++++++++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Emitron/Emitron/Data/ViewModels/TabViewModel.swift b/Emitron/Emitron/Data/ViewModels/TabViewModel.swift index 1beec936..29310766 100644 --- a/Emitron/Emitron/Data/ViewModels/TabViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/TabViewModel.swift @@ -32,6 +32,7 @@ enum MainTab: Hashable { case library case downloads case myTutorials + case settings } final class TabViewModel: ObservableObject { diff --git a/Emitron/Emitron/UI/App Root/MainView.swift b/Emitron/Emitron/UI/App Root/MainView.swift index acb5212b..687befee 100644 --- a/Emitron/Emitron/UI/App Root/MainView.swift +++ b/Emitron/Emitron/UI/App Root/MainView.swift @@ -61,6 +61,8 @@ private extension MainView { contentScreen: .downloads(permitted: sessionController.user?.canDownload ?? false), downloadRepository: dataManager.downloadRepository ) + + let settingsView = SettingsView(showLogoutButton: true) switch sessionController.sessionState { case .online : @@ -80,13 +82,15 @@ private extension MainView { TabNavView( libraryView: libraryView, myTutorialsView: myTutorialsView, - downloadsView: downloadsView + downloadsView: downloadsView, + settingsView: settingsView ) .environmentObject(tabViewModel) case .offline: TabNavView(libraryView: OfflineView(), myTutorialsView: OfflineView(), - downloadsView: downloadsView) + downloadsView: downloadsView, + settingsView: settingsView) .environmentObject(tabViewModel) case .unknown: LoadingView() diff --git a/Emitron/Emitron/UI/App Root/TabNavView.swift b/Emitron/Emitron/UI/App Root/TabNavView.swift index 875f2d0f..58b32736 100644 --- a/Emitron/Emitron/UI/App Root/TabNavView.swift +++ b/Emitron/Emitron/UI/App Root/TabNavView.swift @@ -31,12 +31,14 @@ import SwiftUI struct TabNavView< LibraryView: View, MyTutorialsView: View, - DownloadsView: View + DownloadsView: View, + SettingsView: View >: View { @EnvironmentObject var tabViewModel: TabViewModel let libraryView: LibraryView let myTutorialsView: MyTutorialsView let downloadsView: DownloadsView + let settingsView: SettingsView var body: some View { TabView(selection: $tabViewModel.selectedTab) { @@ -70,6 +72,15 @@ struct TabNavView< .tag(MainTab.myTutorials) .navigationViewStyle(StackNavigationViewStyle()) .accessibility(label: .init(String.myTutorials)) + + NavigationView { settingsView } + .tabItem { + Text(String.settings) + Image(systemName: "gearshape.fill") + } + .tag(MainTab.settings) + .navigationViewStyle(StackNavigationViewStyle()) + .accessibility(label: .init(String.settings)) } .accentColor(.accent) } @@ -80,7 +91,8 @@ struct TabNavView_Previews: PreviewProvider { TabNavView( libraryView: Text("LIBRARY"), myTutorialsView: Text("MY TUTORIALS"), - downloadsView: Text("DOWNLOADS") + downloadsView: Text("DOWNLOADS"), + settingsView: Text("SETTINGS") ).environmentObject(TabViewModel()) } } From 3f71c334058ace92c08dfab41d8d0088716a5c85 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Sun, 6 Dec 2020 12:32:06 +0200 Subject: [PATCH 10/69] Remove SettingsView form MyTutorialView --- .../UI/My Tutorials/MyTutorialsView.swift | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift index 315b5198..0b2a506f 100644 --- a/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift +++ b/Emitron/Emitron/UI/My Tutorials/MyTutorialsView.swift @@ -95,26 +95,6 @@ extension MyTutorialView: View { var body: some View { contentView .navigationBarTitle(String.myTutorials) - .navigationBarItems(trailing: - SwiftUI.Group { - Button(action: { - settingsPresented = true - }) { - Image("settings") - .foregroundColor(.iconButton) - } - }) - .sheet(isPresented: $settingsPresented) { - SettingsView(showLogoutButton: true) - // We have to pass this cos the sheet is in a different view hierarchy, so doesn't 'inherit' it. - .environmentObject(sessionController) - .environmentObject(tabViewModel) - } - .onDisappear { - reloadProgression = true - reloadCompleted = true - reloadBookmarks = true - } } } From bc1bf6ed0ede2f700e7fd3de1708da30b7c228f0 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Sun, 6 Dec 2020 12:35:27 +0200 Subject: [PATCH 11/69] Remove dismissButton & NavigationView --- Emitron/Emitron/UI/Settings/SettingsView.swift | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 8e309b2d..5b24d4d7 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -43,15 +43,11 @@ struct SettingsView: View { var showLogoutButton: Bool var body: some View { - NavigationView { VStack { SettingsList( settingsManager: _settingsManager, canDownload: sessionController.user?.canDownload ?? false - ) - .navigationBarTitle(String.settings) - .navigationBarItems(trailing: dismissButton) - .padding([.horizontal], 20) + ).padding([.horizontal], 20) Section(header: HStack { @@ -99,17 +95,6 @@ struct SettingsView: View { } } .background(Color.backgroundColor) - } - .navigationViewStyle(StackNavigationViewStyle()) - } - - var dismissButton: some View { - Button(action: { - presentationMode.wrappedValue.dismiss() - }) { - Image.close - .foregroundColor(.iconButton) - } } } From 06cd5f07ca014058ff587abf1dfa9ca11c1e9997 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Sun, 6 Dec 2020 13:14:32 +0200 Subject: [PATCH 12/69] Removed Bool showLogoutButton, because settings can only be seen when logged in --- Emitron/Emitron/UI/App Root/MainView.swift | 2 +- Emitron/Emitron/UI/Settings/SettingsView.swift | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Emitron/Emitron/UI/App Root/MainView.swift b/Emitron/Emitron/UI/App Root/MainView.swift index 687befee..4220c0c8 100644 --- a/Emitron/Emitron/UI/App Root/MainView.swift +++ b/Emitron/Emitron/UI/App Root/MainView.swift @@ -62,7 +62,7 @@ private extension MainView { downloadRepository: dataManager.downloadRepository ) - let settingsView = SettingsView(showLogoutButton: true) + let settingsView = SettingsView() switch sessionController.sessionState { case .online : diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 5b24d4d7..ef736700 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -35,12 +35,10 @@ enum SettingsLayout { } struct SettingsView: View { - @Environment(\.presentationMode) var presentationMode: Binding @EnvironmentObject var sessionController: SessionController @EnvironmentObject var tabViewModel: TabViewModel @ObservedObject private var settingsManager = SettingsManager.current @State private var licensesPresented: Bool = false - var showLogoutButton: Bool var body: some View { VStack { @@ -75,7 +73,6 @@ struct SettingsView: View { } .padding([.bottom], 25) - if showLogoutButton { VStack { if sessionController.user != nil { Text("Logged in as \(sessionController.user?.username ?? "")") @@ -83,8 +80,6 @@ struct SettingsView: View { .foregroundColor(.contentText) } MainButtonView(title: "Sign Out", type: .destructive(withArrow: true)) { - presentationMode.wrappedValue.dismiss() - // This is hacky. But without it, the sheet doesn't actually dismiss. DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { sessionController.logout() tabViewModel.selectedTab = .library @@ -92,7 +87,6 @@ struct SettingsView: View { } } .padding([.bottom, .horizontal], 18) - } } .background(Color.backgroundColor) } From 0b02af04b80808f6c24a9cb7ce59f0435158c7d9 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Mon, 7 Dec 2020 01:42:45 +0200 Subject: [PATCH 13/69] Setting navigationBarTitle & background Color --- Emitron/Emitron/UI/Settings/SettingsSelectionView.swift | 2 +- Emitron/Emitron/UI/Settings/SettingsView.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift b/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift index e97dc97a..46a2d433 100644 --- a/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsSelectionView.swift @@ -69,7 +69,7 @@ struct SettingsSelectionView: View { displayMode: .inline ) .padding(20) - .background(Color.backgroundColor) + .background(Color.backgroundColor.edgesIgnoringSafeArea(.all)) } } diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index ef736700..dce9f223 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -87,8 +87,8 @@ struct SettingsView: View { } } .padding([.bottom, .horizontal], 18) - } - .background(Color.backgroundColor) + }.navigationBarTitle(String.settings) + .background(Color.backgroundColor.edgesIgnoringSafeArea(.all)) } } @@ -102,7 +102,7 @@ struct SettingsView: View { // } // // static var settingsView: some View { -// SettingsView(showLogoutButton: true) +// SettingsView() // .background(Color.backgroundColor) // } //} From 7ceae696aeeaf21190c2addb486b2419eb3e923e Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Mon, 7 Dec 2020 01:57:32 +0200 Subject: [PATCH 14/69] Removed settings icon form Assets, replaced it with SF Symbols --- .../Assets.xcassets/Icons/Contents.json | 6 ++--- .../Icons/settings.imageset/Contents.json | 23 ------------------ .../materialIconSettings.png | Bin 931 -> 0 bytes .../materialIconSettings@2x.png | Bin 1781 -> 0 bytes .../materialIconSettings@3x.png | Bin 2837 -> 0 bytes 5 files changed, 3 insertions(+), 26 deletions(-) delete mode 100644 Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/Contents.json delete mode 100644 Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/materialIconSettings.png delete mode 100644 Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/materialIconSettings@2x.png delete mode 100644 Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/materialIconSettings@3x.png diff --git a/Emitron/Emitron/Assets.xcassets/Icons/Contents.json b/Emitron/Emitron/Assets.xcassets/Icons/Contents.json index da4a164c..73c00596 100644 --- a/Emitron/Emitron/Assets.xcassets/Icons/Contents.json +++ b/Emitron/Emitron/Assets.xcassets/Icons/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/Contents.json b/Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/Contents.json deleted file mode 100644 index 6e03d421..00000000 --- a/Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "materialIconSettings.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "materialIconSettings@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "materialIconSettings@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/materialIconSettings.png b/Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/materialIconSettings.png deleted file mode 100644 index 118b513f85f7ecef23d3808145ff67b9176ac373..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 931 zcmV;U16=%xP)Px&T}ebiR7ef2R!wMAK@i^Aq)DMttsaF4)#^>DcoI)qO-&-T4Yctl{#``SgQAs6 z@fQ?P5W&ByC));^Qkq6m7n0qX`DVU1 zZ)e{uuzwgmsiBjS$y2~sP)evo*US6j(ausspkHYtl^R`@m$RRcY`O9P2H$Iz`9tAw zIIDEUX6|L|OQ&m{`n=4Y(TT{8^2+J#{1GP>-@tmOj$~J6z5VI)La+`1_7ojD$3=a=i(RmR}c3`>J=nm!@i4x6`2Fwbp+CGp7dQ(Hs(a>PGkc4l7 z%i952aiygt{0+MMhB}e)7#&bIc)=l1$?EisoVeuRNXW?NvEn?=p$Ud8p4(frXYET9 znaJ^CM1*MFv-1m1<0jhkPgda@IE^(SXFcr&C1yQQ%&u&u;J5^qQx0A;?uF-f&WSAr zhnc}u{QYz~D;YE2NL8m~ubJ$G^Rkf+_iGyeFY-i7^jZLdYwwYF_(M5q-^x| zfv!Y6dPv!9hnfSwZ!qCWhELu=<&8&W9Em58y$&HFBWg{vjZ7F~hi-JQ z&2vTxVGx(eX8akFkLca6!1^eciz7LUa~ zQ)exgK=-G3SnVH_&LSfP-5D%GYNP;nn2#s0Anq-cv2xRAv+dmFezqO^S0`G`ZclNF z-a%9dx+0jJTPbkv?#r|@CN60za|xW?Of<(%==$WqvvqohRyNx?Q%bKnlgU)ixxA?WP{+ZrS`F?TY6=CjZ4EAi$o$b)FyNc$)ysJ-yeE8C;eD0D7OFr002ovPDHLk FV1hDayFUN` diff --git a/Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/materialIconSettings@2x.png b/Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/materialIconSettings@2x.png deleted file mode 100644 index e25d27d37ce36b1396133a8cdfd462e195069cf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1781 zcmVPx*uSrBfRA>dwT3u{ZMHHShw@@rUu^5E}@dxk)f6!PH2^b|Qwrp40U8K7W2Jyw1 z@aTg?3B*JR>br^hf}rWP;d zySrp3Y3I(F^E2N)b7#)XSrRoYz3J@7B*aRg#4JJ7sDx-FB^m{xt;C7shK^OWG7r18 zDSesTDktxZduf*q^lYND;~OuYuzfPDpr4#$JG=dOlr6UT@r3QK39wXpIEtyZ+HX)w zsdf7^Baivvv^qpxN~jlHov^(t5s$v*r`H!) zF{oSWAb4Go7_yYu?<9kc2!hT{!+?VuWd`HQ@XKffmxkx zM-k7R)d|!S4b$!C?pXAMZcJKW-9$Di%IO5^iKYalOyki4D=?EfU=peq`ivP?`M~b5 z?i_`yuh$ER(s2-pA6{JF{$nZKZ5ut-*}3{(Y?Fsw#yYHx-r}(~cnuh#%wJypzI{Z$ zVB#^-Nt~ZQd&c6nwzkVU9^+I@We%px{b}Tv9X3T=Pr*R=xsGJ={iW9!8)+b>Wb{+Eov%D5imZ@=JU=Qn1~4&v8FQm zJ@fQqPsQV{p*HqxHrssZI(?66x!BKEX+J!ES_ru_5o_(>9>=#-*)?mM%<1Rk_DFOsZC?k}h66D*wh1 z`&A1U$C1_0C9!uMt5ngAfVNUK1$k^eu}T$Tb^TWM#G|ju2DuuZ^URF+7+b;ttq+fH zcPgFTh`ns7FQb8hLpo`RwaIwL%A$~}kW9e6JCp~$qIPZIE&a*?ZTH%>7hGC7HEw?A zUAXFe2buDZGDM@&qIVLVkt43nB4A7`?Ebia$HNEiwX)0XR|DLk10s}aB(+(0p)(DbYcTBHA{2f#r%`lAqtWxOF0YzBb@8gxTP`Tpi7WBf zkQMz%r{8!Uld*N5L*OT!kQWUDY~?7O16uaJ7cYNPJpICe0;~x++xo(!-{>(G2PDbflg)O!U{Bv%` z{_s(SMUi)4;W<@o5?d;?Uo&w|GjR^hWWWfle{^&{gpIB?DZE{D_Y^P})Mqo(IZZQef3mq?hy=mpu0-H>tt7@FAYx@qAx~G3=WTnoC7MN}4 zpU^SaBMm=-(QX-9V2N1tGfsonBao?M;xQ)Uk-bxC?z86xUC()FulzHE^905WUf>{% zpG45t*cSf5<Lt5=|@mqa{q3;o$E${pf#-9r2i X)nsp4pC-_O00000NkvXXu0mjfsM}hC diff --git a/Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/materialIconSettings@3x.png b/Emitron/Emitron/Assets.xcassets/Icons/settings.imageset/materialIconSettings@3x.png deleted file mode 100644 index 1c34721b9f3afcee98d8de3bb4daba570f76ea7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2837 zcmV+w3+nWVP)Px<&q+i_2N*pn zI3pt?;WKYs*lKdtf;?R8Y;WAHl0IA3bH{R^H}zwXK*rBpoE_&}>|YT!ld)KA)CtaY zKR7US>vYE4hf=gDSG<=n=>F=O>dL3<>guNRrSCp=Y=t@cK?AdR3toH_-PqJPIw=bQ|vCK>ok`FTv33=&uWg*Ybq~Rg6ON++^QGycCI2Z6Yi*&^tF3QmPt9 zZJiOW*yM%4Y+Z@==!+u4?m$y;JUiU)GhWK3uKv_!85}7RGyQ`*Q4;0X$+&2y-cM9l zZHYI1`3)CQspxoP<9TE4hqnUvgj51qp$y;K-Iu!EO+0T~f;S;(K9{B=>8-c{NynoK zQwh{XV7A_Yfg?3nGE*a1N_KsAxZh{IoZe8#_Qn_S&6r(TaC5Fi zzQTm@CBId;kp8Z|p{>l!94jQ_F^+~@$xuajcSqB<*RnC@NTfMgT4jY`ikO67ATH^mqZK=-p+O==%;KUBIzT&3uE+$ z2z-78P9i3{F6cL10ee zN0jy;w24;W)fw$>1vyl;TVDXscHly90TFFc0O8}XZ;|&sGT0}6| z=9jb}xq#R%eHR(6(VVqu#mfU`f3d8r^DPXz|C>inPcY_0$fd6btT0OVq2I29 zI}CF$!Ch!@FJlPx(+VE04cTjL7~6wQiu>$6c8ooM%BQ?V%-vioOmiZT+vzUrPa-ol z`6v*AX%De6lQZ$SST6R)>+9bkOBeK*_Fg;MDrWC=^0!fEJ{2hO=*ReGa_P^H)Ac*q zGqj0u^CjJ*0hGFnU~5mMvFaDi&08*K+q-~7Cz7^|**l&%>(prwi2NtbiI7X*(WUE7 zZas6{Iu3hlbzy=a%rJ_dRaIQ~L{rnoE4gs}rB))m`n4`z< z-S)`8diHtLpe=r}=F-FYx@wuYcecr!3!eYrN+Kng-NbWJbBR1rX@pyvo436+-ze4J zGbPaM(d|DBhs5VPnxlVmvq#GF+tJ+c7XbCSfvLn($)-sqdrm|p;XGMt181tRUI#3i zotI?@By*%lB*Bm&9@RdkB z&_8sSnaLcM0oEWxQS!26I{!B9j>@5>u&EH5%Jy8NhOpL7|D*M;%CHNQ%Vwrv$A= z&AaViLeI4+LT5!tTv8;Web^;|RemBkY=cfMam$!1iCEL}DSXu5;RaNW@fcpMrej~+ z^5#k+{C~ULK*~9u&dqVlpDT%6u$(?LH{f!Kr-O&w@*?%w_y02>3ezY>B0(%QBDJ4J z;=G5YLZk|T#Eom4=nzPEI!lWbWQWN;V{aagzmk*HB!#VI*eUur^qy2fR3(tGl}Mxn zl0=dlYZw{$u3?A`I1#Q^EH`Sf@aG$FYmE?2P#1KbUFMYpd zT0d7wlS>v=xEIy7Jy%>Y!j#)YokmrK);wviEbs?v(v~rQ5dKn^ltg<)0hVU?$pT(N zvKSkT3fWjv63H-H+k%#Pw2+bc;?NdlXs=zq_+o{ZoJ4zZki{SKl406oH-EFD7aYH7 zN=_nW;6o8>FRk}e$qY*2ByCbuNfVUpq9Bn@Lk20oRLUeaCpx=4mC^+uyeLSde1%zf n4~UdnB}Q_zyE6bjJVE{sEq}wjrv0y}00000NkvXXu0mjf>bGD! From 8b76834ea29dc564db606759f50ff09ad07ea89b Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Mon, 7 Dec 2020 03:53:13 +0200 Subject: [PATCH 15/69] Remove padding at the Top --- .../Emitron/UI/Shared/Content Detail/ContentSummaryView.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift index baaa1f21..939508fd 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift @@ -70,14 +70,12 @@ extension ContentSummaryView: View { Text(content.name) .font(.uiTitle1) .lineLimit(nil) - .padding([.top], 10) .foregroundColor(.titleText) Text(content.contentSummaryMetadataString) .font(.uiCaption) .foregroundColor(.contentText) .lineSpacing(3) - .padding([.top], 10) HStack(spacing: 30, content: { if canDownload { From 6922975c3d11cabf1e1f4d174b588c165910ca94 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Mon, 7 Dec 2020 04:48:52 +0200 Subject: [PATCH 16/69] Titles, subtitles and episode titles need kerning by -0.5 --- .../UI/Shared/Content Detail/ChildContentListingView.swift | 1 + Emitron/Emitron/UI/Shared/Content Detail/CourseHeaderView.swift | 1 + Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift | 1 + 3 files changed, 3 insertions(+) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift index 93ef0197..edfcfec5 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift @@ -58,6 +58,7 @@ private extension ChildContentListingView { if childContentsViewModel.contents.count > 1 { Text("Course Episodes") .font(.uiTitle2) + .kerning(-0.5) .foregroundColor(.titleText) .padding([.top, .bottom]) } diff --git a/Emitron/Emitron/UI/Shared/Content Detail/CourseHeaderView.swift b/Emitron/Emitron/UI/Shared/Content Detail/CourseHeaderView.swift index 8828aaa5..aac796b4 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/CourseHeaderView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/CourseHeaderView.swift @@ -37,6 +37,7 @@ struct CourseHeaderView: View { HStack { Text(name) .font(.uiTitle3) + .kerning(-0.5) .foregroundColor(.titleText) .padding(.leading, 20) Spacer() diff --git a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift index 298a6359..3c15ba33 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift @@ -57,6 +57,7 @@ struct TextListItemView: View { VStack(alignment: .leading, spacing: 5) { Text(content.name) .font(.uiTitle5) + .kerning(-0.5) .lineSpacing(3) .foregroundColor(.titleText) From 3f11ed76f392bd9c71c15cc2769d23e6c3f75524 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Mon, 7 Dec 2020 15:58:01 +0200 Subject: [PATCH 17/69] Added Title kerning in Content Summary View --- .../Emitron/UI/Shared/Content Detail/ContentSummaryView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift index baaa1f21..1b21c137 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift @@ -69,6 +69,7 @@ extension ContentSummaryView: View { Text(content.name) .font(.uiTitle1) + .kerning(-0.5) .lineLimit(nil) .padding([.top], 10) .foregroundColor(.titleText) From f4ffcce55d1d51877af8a6343b0c6c4e07b4c90b Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Mon, 7 Dec 2020 18:39:44 +0200 Subject: [PATCH 18/69] Added new Button Label font for side 15 & changed names to Large, Medium & Small Depending on size. --- Emitron/Emitron/Styleguide/Font+Extensions.swift | 11 +++++++---- Emitron/Emitron/UI/Shared/MainButtonView.swift | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Emitron/Emitron/Styleguide/Font+Extensions.swift b/Emitron/Emitron/Styleguide/Font+Extensions.swift index 6745dfd3..cf8355bc 100644 --- a/Emitron/Emitron/Styleguide/Font+Extensions.swift +++ b/Emitron/Emitron/Styleguide/Font+Extensions.swift @@ -88,9 +88,15 @@ extension Font { } // Can't have bold Font's - static var uiButtonLabel: Font { + static var uiButtonLabelLarge: Font { Font.system(size: UIFontMetrics.default.scaledValue(for: 17.0)).bold() } + static var uiButtonLabelMedium: Font { + Font.system(size: UIFontMetrics.default.scaledValue(for: 15)).weight(.bold) + } + static var uiButtonLabelSmall: Font { + Font.system(size: UIFontMetrics.default.scaledValue(for: 13.0)).weight(.semibold) + } static var uiBodyCustom: Font { Font.system(size: UIFontMetrics.default.scaledValue(for: 15.0)) } @@ -100,9 +106,6 @@ extension Font { static var uiLabel: Font { Font.system(size: UIFontMetrics.default.scaledValue(for: 16.0)) } - static var uiButtonLabelSmall: Font { - Font.system(size: UIFontMetrics.default.scaledValue(for: 13.0)).weight(.semibold) - } static var uiFootnote: Font { Font.footnote } diff --git a/Emitron/Emitron/UI/Shared/MainButtonView.swift b/Emitron/Emitron/UI/Shared/MainButtonView.swift index e2c8a56a..8309b435 100644 --- a/Emitron/Emitron/UI/Shared/MainButtonView.swift +++ b/Emitron/Emitron/UI/Shared/MainButtonView.swift @@ -76,7 +76,7 @@ struct MainButtonView: View { Spacer() Text(title) - .font(.uiButtonLabel) + .font(.uiButtonLabelLarge) .foregroundColor(.buttonText) .padding(15) .background(GeometryReader { proxy in From c794b653065e87c10cb2107054fee67a82fbdd49 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Mon, 7 Dec 2020 18:41:14 +0200 Subject: [PATCH 19/69] Added Padding and Changed font to match luke's design --- .../Content Detail/Child Listing/VideoOverlayButtonView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 c06b4be4..250ba27d 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/VideoOverlayButtonView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/Child Listing/VideoOverlayButtonView.swift @@ -50,12 +50,12 @@ struct VideoOverlayButtonView: View { if text != nil { Text(text!) .foregroundColor(.white) - .font(.uiButtonLabel) + .font(.uiButtonLabelMedium) .fixedSize() .padding([.trailing], 8) } } - .padding(10) + .padding(12.5) .background(GeometryReader { proxy in Color.clear.preference(key: SizeKey.self, value: proxy.size) }) From c1c99a7fd863ac1ddf4b1f6281e5876b1c3edd73 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 9 Dec 2020 02:55:16 +0200 Subject: [PATCH 20/69] Struct 'CustomToggleView' is unused --- Emitron/Emitron.xcodeproj/project.pbxproj | 4 - .../Emitron/UI/Shared/CustomToggleView.swift | 76 ------------------- 2 files changed, 80 deletions(-) delete mode 100644 Emitron/Emitron/UI/Shared/CustomToggleView.swift diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index a0ea10e7..928b5e22 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -258,7 +258,6 @@ 8BFCEF1F2341184A003FF72F /* PermissionsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BFCEF1E2341184A003FF72F /* PermissionsRequest.swift */; }; 95A4513522E224B500D24E56 /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A4513322E224B500D24E56 /* SessionController.swift */; }; B5678C62233668B100BCB8A1 /* CircleProgressBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5678C61233668B100BCB8A1 /* CircleProgressBarView.swift */; }; - B595654423130E4C00A3FF44 /* CustomToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B595654323130E4C00A3FF44 /* CustomToggleView.swift */; }; B60612B922CCE352007FC852 /* Parameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = B60612B822CCE352007FC852 /* Parameters.swift */; }; B60FF7BE23000ABD00F36B32 /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B60FF7BD23000ABD00F36B32 /* CardView.swift */; }; B621DD5122F5EEC500A1F27A /* DataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B621DD5022F5EEC500A1F27A /* DataManager.swift */; }; @@ -598,7 +597,6 @@ 8BFCEF1E2341184A003FF72F /* PermissionsRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsRequest.swift; sourceTree = ""; }; 95A4513322E224B500D24E56 /* SessionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionController.swift; sourceTree = ""; }; B5678C61233668B100BCB8A1 /* CircleProgressBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleProgressBarView.swift; sourceTree = ""; }; - B595654323130E4C00A3FF44 /* CustomToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomToggleView.swift; sourceTree = ""; }; B60612B822CCE352007FC852 /* Parameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parameters.swift; sourceTree = ""; }; B60FF7BD23000ABD00F36B32 /* CardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardView.swift; sourceTree = ""; }; B621DD3622F5CB9A00A1F27A /* Attachment_Downloads.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Attachment_Downloads.json; sourceTree = ""; }; @@ -1675,7 +1673,6 @@ isa = PBXGroup; children = ( B6C4F3D122E6ECA40087ED10 /* CheckmarkView.swift */, - B595654323130E4C00A3FF44 /* CustomToggleView.swift */, B66778AB2305D2D4003EEBAB /* MainButtonView.swift */, 8B283DEE23169A1E001F1B17 /* ProgressBarView.swift */, 229F0AC223BB27820004DD4F /* Content Detail */, @@ -2104,7 +2101,6 @@ B6D8EB2322CBA81200DE29AF /* DomainsService.swift in Sources */, 229935D023FE8C6F00D3D16A /* SettingsSelectable.swift in Sources */, B6D7DC4622C7AF60006DD325 /* UIFont+Extensions.swift in Sources */, - B595654423130E4C00A3FF44 /* CustomToggleView.swift in Sources */, 22D39FC823EC0AEA0058599D /* LoadingView.swift in Sources */, B6DF2FB622CA62E50081A3A3 /* JSONAPIDocument.swift in Sources */, 229F4A1623FE7ECB006E2DE6 /* SettingsToggleRow.swift in Sources */, diff --git a/Emitron/Emitron/UI/Shared/CustomToggleView.swift b/Emitron/Emitron/UI/Shared/CustomToggleView.swift deleted file mode 100644 index 94fd1269..00000000 --- a/Emitron/Emitron/UI/Shared/CustomToggleView.swift +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2019 Razeware LLC -// -// 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 - -struct CustomToggle: UIViewRepresentable { - - var isOn: Bool - var callback: (() -> Void)? - - func makeUIView(context: Context) -> UISwitch { - let uiView = UISwitch() - uiView.addTarget( - context.coordinator, - action: #selector(Coordinator.didChange(sender:)), - for: .valueChanged) - - return uiView - } - - func updateUIView(_ uiView: UISwitch, context: Context) { - // This is appGreen - uiView.onTintColor = UIColor(red: 21.0 / 255.0, green: 132.0 / 255.0, blue: 67.0 / 255.0, alpha: 1) - uiView.isOn = isOn - } - - func makeCoordinator() -> CustomToggle.Coordinator { - Coordinator(self) - } - - class Coordinator: NSObject { - var control: CustomToggle - - init(_ control: CustomToggle) { - self.control = control - } - - @objc func didChange(sender: UISwitch) { - control.isOn = sender.isOn - control.callback?() - } - } -} - -struct CustomToggleView: View { - var isOn: Bool - var callback: (() -> Void)? - var body: some View { - CustomToggle(isOn: isOn, callback: callback) - } -} From 0c3509be70347f3c7983a75fc88730d48911794a Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 9 Dec 2020 03:19:26 +0200 Subject: [PATCH 21/69] Cleaning up and fixing unused Property in Constants.swift --- Emitron/Emitron/Constants.swift | 7 +------ Emitron/Emitron/Models/SettingsOption.swift | 4 ++-- Emitron/Emitron/UI/Empty States/LoadingView.swift | 2 +- Emitron/Emitron/UI/Library/Filtering/FiltersView.swift | 2 +- .../UI/Shared/Content Detail/ContentSummaryView.swift | 2 +- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Emitron/Emitron/Constants.swift b/Emitron/Emitron/Constants.swift index ce933268..5dce7dd3 100644 --- a/Emitron/Emitron/Constants.swift +++ b/Emitron/Emitron/Constants.swift @@ -40,11 +40,10 @@ extension Int { } extension String { - static let clearAll = "Clear All" static let downloads = "Downloads" static let filters = "Filters" static let library = "Library" - static let loading = "Loading…" + static let loading = "Loading..." static let myTutorials = "My Tutorials" static let newest = "Newest" static let popularity = "Popularity" @@ -99,10 +98,6 @@ extension String { static let yes = "Yes" static let no = "No" // swiftlint:disable:this identifier_name - // MARK: Pull-to-refresh - static let pullToRefreshPullMessage = "Pull to refresh" - static let pullToRefreshLoadingMessage = "Loading…" - // MARK: Settings screens static let settingsPlaybackSpeedLabel = "Video Playback Speed" static let settingsWifiOnlyDownloadsLabel = "Downloads (WiFi only)" diff --git a/Emitron/Emitron/Models/SettingsOption.swift b/Emitron/Emitron/Models/SettingsOption.swift index 9b33941d..deb31bb2 100644 --- a/Emitron/Emitron/Models/SettingsOption.swift +++ b/Emitron/Emitron/Models/SettingsOption.swift @@ -67,11 +67,11 @@ enum SettingsOption: Int, Identifiable, CaseIterable { case .playbackSpeed: return PlaybackSpeed.allCases.map(\.display) case .wifiOnlyDownloads: - return ["Yes", "No"] + return [String.yes, String.no] case .downloadQuality: return Attachment.Kind.downloads.map(\.display) case .closedCaptionOn: - return ["Yes", "No"] + return [String.yes, String.no] } } diff --git a/Emitron/Emitron/UI/Empty States/LoadingView.swift b/Emitron/Emitron/UI/Empty States/LoadingView.swift index c26073ee..113b4922 100644 --- a/Emitron/Emitron/UI/Empty States/LoadingView.swift +++ b/Emitron/Emitron/UI/Empty States/LoadingView.swift @@ -33,7 +33,7 @@ struct LoadingView: View { VStack { ActivityIndicator() .padding([.bottom], 10) - Text("Loading...") + Text(String.loading) .font(.uiHeadline) } } diff --git a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift index 3bd27ea1..2db7c86f 100644 --- a/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/FiltersView.swift @@ -45,7 +45,7 @@ struct FiltersView: View { Spacer() - Text("Filters") + Text(String.filters) .font(.uiTitle5) .foregroundColor(.titleText) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift index baaa1f21..53468193 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift @@ -100,7 +100,7 @@ extension ContentSummaryView: View { .padding(.top, 15) .lineLimit(nil) - Text("By \(content.contributorString)") + Text("\(String.by) \(content.contributorString)") .font(.uiCaption) .foregroundColor(.contentText) .lineLimit(2) From 6ffd79d33b92f40b2db44e9bf76255e3f3c75bd8 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 9 Dec 2020 03:32:12 +0200 Subject: [PATCH 22/69] Clean up of unused properties in UIFont+Extensions.swift --- .../Styleguide/UIFont+Extensions.swift | 41 ------------------- 1 file changed, 41 deletions(-) diff --git a/Emitron/Emitron/Styleguide/UIFont+Extensions.swift b/Emitron/Emitron/Styleguide/UIFont+Extensions.swift index 3460c8ad..a44a730a 100644 --- a/Emitron/Emitron/Styleguide/UIFont+Extensions.swift +++ b/Emitron/Emitron/Styleguide/UIFont+Extensions.swift @@ -32,48 +32,7 @@ extension UIFont { static var uiLargeTitle: UIFont { UIFont(name: "Bitter-Bold", size: 34.0)! } - static var uiTitle1: UIFont { - UIFont(name: "Bitter-Bold", size: 28.0)! - } - static var uiTitle2: UIFont { - UIFont(name: "Bitter-Bold", size: 22.0)! - } - static var uiTitle3: UIFont { - UIFont(name: "Bitter-Bold", size: 20.0)! - } - static var uiTitle4: UIFont { - UIFont(name: "Bitter-Bold", size: 17.0)! - } static var uiHeadline: UIFont { UIFont(name: "Bitter-Regular", size: 17.0)! } - - static var uiNumberBox: UIFont { - UIFont(name: "Bitter-Bold", size: 13.0)! - } - - static var uiBodyAppleDefault: UIFont { - UIFont.systemFont(ofSize: 17.0, weight: .regular) - } - static var uiButtonLabel: UIFont { - UIFont.systemFont(ofSize: 15.0, weight: .bold) - } - static var uiBodyCustom: UIFont { - UIFont.systemFont(ofSize: 15.0, weight: .regular) - } - static var uiLabel: UIFont { - UIFont.systemFont(ofSize: 14.0, weight: .semibold) - } - static var uiButtonLabelSmall: UIFont { - UIFont.systemFont(ofSize: 13.0, weight: .semibold) - } - static var uiFootnote: UIFont { - UIFont.systemFont(ofSize: 13.0, weight: .regular) - } - static var uiCaption: UIFont { - UIFont.systemFont(ofSize: 12.0, weight: .regular) - } - static var uiUppercase: UIFont { - UIFont.systemFont(ofSize: 11.0, weight: .medium) - } } From 465d86c83162194c6b5ab7f9f5a8437c3ae9638a Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 9 Dec 2020 03:50:36 +0200 Subject: [PATCH 23/69] Removed unused Function 'openSettings()' --- .../UI/Shared/Content Detail/ContentDetailView.swift | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index fd82d806..6fdb64b3 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -96,14 +96,6 @@ private extension ContentDetailView { var imageRatio: CGFloat { 283 / 375 } var maxImageHeight: CGFloat { 384 } - - func openSettings() { - // open iPhone settings - if - let url = URL(string: UIApplication.openSettingsURLString), - UIApplication.shared.canOpenURL(url) - { UIApplication.shared.open(url) } - } var continueOrPlayButton: some View { Button(action: { From 55e5bf298c71a457bee1a65304565b517632c75f Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 9 Dec 2020 06:47:34 +0200 Subject: [PATCH 24/69] Remove unused Enum 'DownloadServiceError' --- Emitron/Emitron/Downloads/DownloadService.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Emitron/Emitron/Downloads/DownloadService.swift b/Emitron/Emitron/Downloads/DownloadService.swift index 5804e607..28817679 100644 --- a/Emitron/Emitron/Downloads/DownloadService.swift +++ b/Emitron/Emitron/Downloads/DownloadService.swift @@ -30,11 +30,6 @@ import Combine import Foundation import Network -enum DownloadServiceError: Error { - case unableToCancelDownload - case unableToDeleteDownload -} - final class DownloadService { enum Status { case active From 27aa747d9488f8dd22b085493547171e1da6c6bd Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 9 Dec 2020 06:52:49 +0200 Subject: [PATCH 25/69] Removed unused Enum 'RequestError' --- Emitron/Emitron/Networking/Requests/Request.swift | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Emitron/Emitron/Networking/Requests/Request.swift b/Emitron/Emitron/Networking/Requests/Request.swift index be99c7b2..350ce208 100644 --- a/Emitron/Emitron/Networking/Requests/Request.swift +++ b/Emitron/Emitron/Networking/Requests/Request.swift @@ -53,15 +53,3 @@ extension Request { var method: HTTPMethod { .GET } var body: Data? { nil } } - -enum RequestError: Error { - case responseMissingRequiredMeta(field: String?) - - var localizedDescription: String { - let prefix = "RequestError::" - switch self { - case .responseMissingRequiredMeta(field: let field): - return "\(prefix)ResponseMissingRequiredMeta: [Field: \(field ?? "UNKNOWN")]" - } - } -} From 5c49496ac0234db7a17bb2fc23a2836dda661954 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 9 Dec 2020 07:02:38 +0200 Subject: [PATCH 26/69] Removes the need to enter login details every time you build and run the app. The first time you build and run, you might still have to enter your email and password, but in subsequent build and runs, tapping Continue skips the login form --- Emitron/Emitron/Guardpost/Guardpost.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Emitron/Emitron/Guardpost/Guardpost.swift b/Emitron/Emitron/Guardpost/Guardpost.swift index c2f2fec2..486d32ba 100644 --- a/Emitron/Emitron/Guardpost/Guardpost.swift +++ b/Emitron/Emitron/Guardpost/Guardpost.swift @@ -127,7 +127,10 @@ public class Guardpost { authSession?.presentationContextProvider = presentationContextDelegate // This will prevent sharing cookies with Safari, which means no auto-login // However, it also means that you can actually log out, which is good, I guess. + #if (!DEBUG) authSession?.prefersEphemeralWebBrowserSession = true + #endif + authSession?.start() } From 161f27952db481e4c7e68a6f4b4c48489bdf3eb5 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 9 Dec 2020 09:14:47 +0200 Subject: [PATCH 27/69] Removed unused Typealias 'Provider' --- Emitron/Emitron/Networking/Services/WatchStatsService.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Emitron/Emitron/Networking/Services/WatchStatsService.swift b/Emitron/Emitron/Networking/Services/WatchStatsService.swift index 74fd4a7c..04107d0b 100644 --- a/Emitron/Emitron/Networking/Services/WatchStatsService.swift +++ b/Emitron/Emitron/Networking/Services/WatchStatsService.swift @@ -27,8 +27,6 @@ // THE SOFTWARE. class WatchStatsService: Service { - typealias Provider = (RWAPI) -> WatchStatsService - func update(watchStats: [WatchStat], completion: @escaping (_ response: Result) -> Void) { let request = WatchStatsUpdateRequest(watchStats: watchStats) From e618a1f5b79337fe845dc83ff35c3d764d9378a9 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 9 Dec 2020 09:22:12 +0200 Subject: [PATCH 28/69] Removed unused Initializer 'init(_:)' --- Emitron/Emitron/Models/ContentSubscriptionPlan.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Emitron/Emitron/Models/ContentSubscriptionPlan.swift b/Emitron/Emitron/Models/ContentSubscriptionPlan.swift index 98bc525c..74d565de 100644 --- a/Emitron/Emitron/Models/ContentSubscriptionPlan.swift +++ b/Emitron/Emitron/Models/ContentSubscriptionPlan.swift @@ -30,10 +30,6 @@ enum ContentSubscriptionPlan: Int, Codable { case beginner case professional - init(_ pro: Bool) { - self = pro ? .professional : .beginner - } - var displayString: String { switch self { case .beginner: From 471d2306ba93039bf436e09b7f74187e35e921a9 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 10 Dec 2020 02:00:10 +0200 Subject: [PATCH 29/69] Fix Truncated Text for content.name & content.contentSummary --- .../Emitron/UI/Shared/Content Detail/ContentSummaryView.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift index 939508fd..c43428b7 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift @@ -70,12 +70,14 @@ extension ContentSummaryView: View { Text(content.name) .font(.uiTitle1) .lineLimit(nil) + .fixedSize(horizontal: false, vertical: true) .foregroundColor(.titleText) Text(content.contentSummaryMetadataString) .font(.uiCaption) .foregroundColor(.contentText) .lineSpacing(3) + .fixedSize(horizontal: false, vertical: true) HStack(spacing: 30, content: { if canDownload { @@ -97,6 +99,7 @@ extension ContentSummaryView: View { .lineSpacing(3) .padding(.top, 15) .lineLimit(nil) + .fixedSize(horizontal: false, vertical: true) Text("By \(content.contributorString)") .font(.uiCaption) @@ -104,6 +107,7 @@ extension ContentSummaryView: View { .lineLimit(2) .padding(.top, 10) .lineSpacing(3) + .fixedSize(horizontal: false, vertical: true) } } } From 5757a8d2629e81893830245ccdaca2162d4f13d1 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 10 Dec 2020 02:01:22 +0200 Subject: [PATCH 30/69] Fix background Color for Content Summary --- Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index fd82d806..542eba7f 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -76,6 +76,7 @@ private extension ContentDetailView { ContentSummaryView(content: content, dynamicContentViewModel: dynamicContentViewModel) .padding([.leading, .trailing], 20) .padding(.bottom, 37) + .background(Color.backgroundColor) } .listRowInsets(EdgeInsets()) .listRowBackground(Color.backgroundColor) From c1a8cd118b1aaecfe1e86e270a02a19c74bbd798 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 10 Dec 2020 05:04:28 +0200 Subject: [PATCH 31/69] Replaced List with ScrollView / VStack to fix strange Animation issue. --- .../Content Detail/ContentDetailView.swift | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index 678f1113..85727844 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -65,8 +65,8 @@ extension ContentDetailView: View { private extension ContentDetailView { var contentView: some View { GeometryReader { geometry in - List { - Section { + ScrollView { + VStack { if content.professional && !canStreamPro { headerImageLockedProContent(for: geometry.size.width) } else { @@ -76,19 +76,17 @@ private extension ContentDetailView { ContentSummaryView(content: content, dynamicContentViewModel: dynamicContentViewModel) .padding([.leading, .trailing], 20) .background(Color.backgroundColor) - } - .listRowInsets(EdgeInsets()) - .listRowBackground(Color.backgroundColor) - - ChildContentListingView( - childContentsViewModel: childContentsViewModel, - currentlyDisplayedVideoPlaybackViewModel: $currentlyDisplayedVideoPlaybackViewModel - ) + + ChildContentListingView( + childContentsViewModel: childContentsViewModel, + currentlyDisplayedVideoPlaybackViewModel: $currentlyDisplayedVideoPlaybackViewModel + ) .background(Color.backgroundColor) + } } } - .navigationBarTitle(Text(""), displayMode: .inline) - .background(Color.backgroundColor) + .navigationBarTitle(Text(""), displayMode: .inline) + .background(Color.backgroundColor) } var canStreamPro: Bool { user.canStreamPro } From 7903b2ea9d003ecc33647bcb6770aeaa0e021025 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 10 Dec 2020 05:35:26 +0200 Subject: [PATCH 32/69] Removed unused functions for resumeDownload & pauseDownload --- .../Emitron/Downloads/DownloadProcessor.swift | 18 ------------------ .../Emitron/Downloads/DownloadService.swift | 8 -------- 2 files changed, 26 deletions(-) diff --git a/Emitron/Emitron/Downloads/DownloadProcessor.swift b/Emitron/Emitron/Downloads/DownloadProcessor.swift index 9402c102..8f0e5336 100644 --- a/Emitron/Emitron/Downloads/DownloadProcessor.swift +++ b/Emitron/Emitron/Downloads/DownloadProcessor.swift @@ -41,8 +41,6 @@ protocol DownloadProcessorDelegate: AnyObject { 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, didPauseDownloadWithId downloadId: UUID) - func downloadProcessor(_ processor: DownloadProcessor, didResumeDownloadWithId downloadId: UUID) func downloadProcessor(_ processor: DownloadProcessor, downloadWithId downloadId: UUID, didFailWithError error: Error) } @@ -99,22 +97,6 @@ extension DownloadProcessor { delegate.downloadProcessor(self, didStartDownloadWithId: download.id) } - func pauseDownload(_ download: DownloadProcessorModel) throws { - guard let downloadTask = currentDownloads.first(where: { $0.downloadId == download.id }) else { throw DownloadProcessorError.unknownDownload } - - downloadTask.suspend() - - delegate.downloadProcessor(self, didPauseDownloadWithId: download.id) - } - - func resumeDownload(_ download: DownloadProcessorModel) throws { - guard let downloadTask = currentDownloads.first(where: { $0.downloadId == download.id }) else { throw DownloadProcessorError.unknownDownload } - - downloadTask.resume() - - delegate.downloadProcessor(self, didResumeDownloadWithId: download.id) - } - func cancelDownload(_ download: DownloadProcessorModel) throws { guard let downloadTask = currentDownloads.first(where: { $0.downloadId == download.id }) else { throw DownloadProcessorError.unknownDownload } diff --git a/Emitron/Emitron/Downloads/DownloadService.swift b/Emitron/Emitron/Downloads/DownloadService.swift index 28817679..61bc5bbf 100644 --- a/Emitron/Emitron/Downloads/DownloadService.swift +++ b/Emitron/Emitron/Downloads/DownloadService.swift @@ -499,14 +499,6 @@ extension DownloadService: DownloadProcessorDelegate { } } - func downloadProcessor(_ processor: DownloadProcessor, didPauseDownloadWithId downloadId: UUID) { - transitionDownload(withID: downloadId, to: .paused) - } - - func downloadProcessor(_ processor: DownloadProcessor, didResumeDownloadWithId downloadId: UUID) { - transitionDownload(withID: downloadId, to: .inProgress) - } - func downloadProcessor(_ processor: DownloadProcessor, downloadWithId downloadId: UUID, didFailWithError error: Error) { transitionDownload(withID: downloadId, to: .error) Failure From d7a0db918660a8135d68b015b6968dae3655ad30 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 10 Dec 2020 05:42:18 +0200 Subject: [PATCH 33/69] Removed unused contentSummaries --- Emitron/Emitron/Downloads/DownloadService.swift | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Emitron/Emitron/Downloads/DownloadService.swift b/Emitron/Emitron/Downloads/DownloadService.swift index 61bc5bbf..dcf7ba41 100644 --- a/Emitron/Emitron/Downloads/DownloadService.swift +++ b/Emitron/Emitron/Downloads/DownloadService.swift @@ -524,18 +524,6 @@ extension DownloadService { .downloadList() .eraseToAnyPublisher() } - - func downloadedContentSummary(for contentId: Int) -> AnyPublisher { - persistenceStore - .downloadContentSummary(for: contentId) - .eraseToAnyPublisher() - } - - func contentSummaries(for contentIds: [Int]) -> AnyPublisher<[ContentSummaryState], Error> { - persistenceStore - .downloadContentSummary(for: contentIds) - .eraseToAnyPublisher() - } } // MARK: - Wifi Status Handling From 2e02626e889722288bb67692ba2b99d6fc3f9cf8 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 10 Dec 2020 05:52:01 +0200 Subject: [PATCH 34/69] Cleanup download functions downloadContentSummary downloads(for:) & deleteDownloads() --- .../PersistenceStore+Downloads.swift | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift index 57db7437..4c7bf65c 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift @@ -51,43 +51,6 @@ extension PersistenceStore { } extension PersistenceStore { - func downloadContentSummary(for contentId: Int) -> DatabasePublishers.Value { - ValueObservation.tracking { db -> ContentSummaryState? in - let request = Content - .filter(key: contentId) - .including(all: Content.domains) - .including(all: Content.categories) - .including(optional: Content.parentContent) - - return try ContentSummaryState.fetchOne(db, request) - } - .publisher(in: db) - } - - func downloadContentSummary(for contentIds: [Int]) -> DatabasePublishers.Value<[ContentSummaryState]> { - ValueObservation.tracking { db -> [ContentSummaryState] in - let request = Content - .filter(keys: contentIds) - .including(all: Content.domains) - .including(all: Content.categories) - .including(optional: Content.parentContent) - - return try ContentSummaryState.fetchAll(db, request) - } - .publisher(in: db) - } -} - -extension PersistenceStore { - func downloads(for contentIds: [Int]) -> DatabasePublishers.Value<[Download]> { - ValueObservation.tracking { db -> [Download] in - let request = Download - .filter(contentIds.contains(Download.Columns.contentId)) - return try Download.fetchAll(db, request) - } - .publisher(in: db) - } - func download(for contentId: Int) -> DatabasePublishers.Value { ValueObservation.tracking { db -> Download? in let request = Download @@ -374,13 +337,7 @@ extension PersistenceStore { } } } - - /// Delete all the downloads in the database - func deleteDownloads() throws { - _ = try db.write { db in - try Download.deleteAll(db) - } - } + /// Save the entire graph of models to supprt this ContentDeailsModel /// - Parameter contentPersistableState: The model to persist—from the DataCache. From 085fb09b4c7aeb400eb43a446cecb3af70b156ea Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 10 Dec 2020 08:50:57 +0200 Subject: [PATCH 35/69] Fixing Text("Course Episodes") that was in the center --- .../Content Detail/ChildContentListingView.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift index 93ef0197..6251b629 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift @@ -56,10 +56,13 @@ private extension ChildContentListingView { SwiftUI.Group { Section { if childContentsViewModel.contents.count > 1 { - Text("Course Episodes") - .font(.uiTitle2) - .foregroundColor(.titleText) - .padding([.top, .bottom]) + HStack { + Text("Course Episodes") + .font(.uiTitle2) + .foregroundColor(.titleText) + .padding([.top, .bottom]) + Spacer() + }.padding([.leading, .trailing], 20) } } .listRowBackground(Color.backgroundColor) From f55d3271a612beb9d8d074cf584706a25043547b Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 10 Dec 2020 09:09:32 +0200 Subject: [PATCH 36/69] fixing formatting --- .../UI/Shared/Content Detail/ChildContentListingView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift index 5f38740a..4a423a0e 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift @@ -59,15 +59,15 @@ private extension ChildContentListingView { HStack { Text("Course Episodes") .font(.uiTitle2) - .kerning(-0.5) + .kerning(-0.5) .foregroundColor(.titleText) .padding([.top, .bottom]) Spacer() }.padding([.leading, .trailing], 20) } } - .listRowBackground(Color.backgroundColor) - .accessibility(identifier: "childContentList") + .listRowBackground(Color.backgroundColor) + .accessibility(identifier: "childContentList") if childContentsViewModel.groups.count > 1 { ForEach(childContentsViewModel.groups, id: \.id) { group in From c2459999b6e09720afe405a9cdd424a386444047 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Fri, 11 Dec 2020 06:03:10 +0200 Subject: [PATCH 37/69] Fixed SettingsView_Previews with .environmentObject(SessionController.current) --- .../Emitron/UI/Settings/SettingsView.swift | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index dce9f223..643166a4 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -92,18 +92,19 @@ struct SettingsView: View { } } -//#if DEBUG -//struct SettingsView_Previews: PreviewProvider { -// static var previews: some View { -// SwiftUI.Group { -// settingsView.colorScheme(.dark) -// settingsView.colorScheme(.light) -// } -// } -// -// static var settingsView: some View { -// SettingsView() -// .background(Color.backgroundColor) -// } -//} -//#endif +#if DEBUG +struct SettingsView_Previews: PreviewProvider { + static var previews: some View { + SwiftUI.Group { + settingsView.colorScheme(.dark) + settingsView.colorScheme(.light) + } + } + + static var settingsView: some View { + SettingsView() + .background(Color.backgroundColor) + .environmentObject(SessionController.current) + } +} +#endif From af3faceafe84368243ec59f04991aa48fb0cc0d1 Mon Sep 17 00:00:00 2001 From: Brian Moakley Date: Fri, 26 Feb 2021 17:46:17 -0500 Subject: [PATCH 38/69] Updates code to replace mp4 downsloads and use hls streams instead --- Emitron/Emitron/AppDelegate.swift | 2 +- .../ViewModels/VideoPlaybackViewModel.swift | 5 +- .../Emitron/Downloads/DownloadProcessor.swift | 111 +++++++++++----- .../Emitron/Downloads/DownloadService.swift | 16 +-- .../Networking/Requests/VideosRequest.swift | 18 ++- .../Emitron/Networking/Services/Service.swift | 2 +- .../Networking/Services/VideosService.swift | 6 +- .../PersistenceStore+Downloads.swift | 1 - .../ChildContentListingView.swift | 5 + .../Downloads/DownloadServiceTest.swift | 121 +++++++++--------- .../Models/AttachmentTest+Mocks.swift | 10 +- .../Models/Mocks/Attachment_Downloads.json | 8 -- .../Services/Mock/VideosServiceMock.swift | 4 +- 13 files changed, 181 insertions(+), 128 deletions(-) diff --git a/Emitron/Emitron/AppDelegate.swift b/Emitron/Emitron/AppDelegate.swift index a83bbcdf..b9684e7d 100644 --- a/Emitron/Emitron/AppDelegate.swift +++ b/Emitron/Emitron/AppDelegate.swift @@ -76,7 +76,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { persistenceStore: persistenceStore, downloadService: downloadService ) - downloadService.startProcessing() + downloadService.startProcessing() } // MARK: UISceneSession Lifecycle diff --git a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift index 5dd367b2..a5490dc1 100644 --- a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift @@ -150,7 +150,7 @@ final class VideoPlaybackViewModel { } // If we're online then, that's all good if sessionController.sessionState == .online { - return + //return } // If we've got a download, then we might be ok @@ -380,7 +380,8 @@ private extension VideoPlaybackViewModel { if let download = state.download, download.state == .complete, let localURL = download.localURL { - let item = AVPlayerItem(url: localURL) + let asset = AVURLAsset(url: localURL) + let item = AVPlayerItem(asset: asset) self.addMetadata(from: state, to: item) self.addClosedCaptions(for: item) // Add it to the cache diff --git a/Emitron/Emitron/Downloads/DownloadProcessor.swift b/Emitron/Emitron/Downloads/DownloadProcessor.swift index 8f0e5336..b09f107d 100644 --- a/Emitron/Emitron/Downloads/DownloadProcessor.swift +++ b/Emitron/Emitron/Downloads/DownloadProcessor.swift @@ -28,6 +28,7 @@ import Foundation import Combine +import AVFoundation protocol DownloadProcessorModel { var id: UUID { get } @@ -56,6 +57,18 @@ private extension URLSessionDownloadTask { } } +private extension AVAssetDownloadTask { + var downloadId: UUID? { + get { + guard let taskDescription = taskDescription else { return .none } + return UUID(uuidString: taskDescription) + } + set { + taskDescription = newValue?.uuidString ?? "" + } + } +} + enum DownloadProcessorError: Error { case invalidArguments case unknownDownload @@ -64,17 +77,21 @@ enum DownloadProcessorError: Error { // Manage a list of files to download—either queued, in progresss, paused or failed. final class DownloadProcessor: NSObject { static let sessionIdentifier = "com.razeware.emitron.DownloadProcessor" + static let sdBitrate = 250_000 + private var downloadQuality: Attachment.Kind { + SettingsManager.current.downloadQuality + } - private lazy var session: URLSession = { + private lazy var session: AVAssetDownloadURLSession = { let config = URLSessionConfiguration.background(withIdentifier: DownloadProcessor.sessionIdentifier) // Uncommenting this causes the download task to fail with POSIX 22. But seemingly only with // Vimeo URLs. So that's handy. //config.isDiscretionary = true config.sessionSendsLaunchEvents = true - return URLSession(configuration: config, delegate: self, delegateQueue: .none) + return AVAssetDownloadURLSession(configuration: config, assetDownloadDelegate: self, delegateQueue: .none) }() var backgroundSessionCompletionHandler: (() -> Void)? - private var currentDownloads = [URLSessionDownloadTask]() + private var currentDownloads = [AVAssetDownloadTask]() private var throttleList = [UUID: Double]() weak var delegate: DownloadProcessorDelegate! @@ -87,8 +104,13 @@ final class DownloadProcessor: NSObject { extension DownloadProcessor { func add(download: DownloadProcessorModel) throws { guard let remoteURL = download.remoteURL else { throw DownloadProcessorError.invalidArguments } - - let downloadTask = session.downloadTask(with: remoteURL) + let hlsAsset = AVURLAsset(url: remoteURL) + var options: [String: Any]? + if downloadQuality == .sdVideoFile { + options = [AVAssetDownloadTaskMinimumRequiredMediaBitrateKey: DownloadProcessor.sdBitrate] + } + guard let downloadTask = session.makeAssetDownloadTask(asset: hlsAsset, assetTitle: "\(download.id))", assetArtworkData: nil, options: options) else { return } + downloadTask.downloadId = download.id downloadTask.resume() @@ -117,13 +139,15 @@ extension DownloadProcessor { } extension DownloadProcessor { - private func getDownloadTasksFromSession() -> [URLSessionDownloadTask] { - var tasks = [URLSessionDownloadTask]() + private func getDownloadTasksFromSession() -> [AVAssetDownloadTask] { + var tasks = [AVAssetDownloadTask]() // Use a semaphore to make an async call synchronous // --There's no point in trying to complete instantiating this class without this list. let semaphore = DispatchSemaphore(value: 0) - session.getTasksWithCompletionHandler { _, _, downloadTasks in - tasks = downloadTasks + session.getAllTasks { downloadTasks in + + let myTasks = downloadTasks as! [AVAssetDownloadTask] + tasks = myTasks semaphore.signal() } @@ -137,6 +161,47 @@ extension DownloadProcessor { } } +extension DownloadProcessor: AVAssetDownloadDelegate { + + func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didLoad timeRange: CMTimeRange, totalTimeRangesLoaded loadedTimeRanges: [NSValue], timeRangeExpectedToLoad: CMTimeRange) { + + guard let downloadId = assetDownloadTask.downloadId else { return } + + var percentComplete = 0.0 + for value in loadedTimeRanges { + let loadedTimeRange: CMTimeRange = value.timeRangeValue + percentComplete += CMTimeGetSeconds(loadedTimeRange.duration) / CMTimeGetSeconds(timeRangeExpectedToLoad.duration) + } + + 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) + } + + func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) { + + guard let downloadId = assetDownloadTask.downloadId, + let delegate = delegate else { return } + + let download = delegate.downloadProcessor(self, downloadModelForDownloadWithId: downloadId) + guard let localURL = download?.localURL else { return } + + let fileManager = FileManager.default + do { + if fileManager.fileExists(atPath: localURL.path) { + try fileManager.removeItem(at: localURL) + } + try fileManager.moveItem(at: location, to: localURL) + } catch { + delegate.downloadProcessor(self, downloadWithId: downloadId, didFailWithError: error) + } + } +} + extension DownloadProcessor: URLSessionDownloadDelegate { // When the background session has finished sending us events, we can tell the system we're done. func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { @@ -181,44 +246,30 @@ extension DownloadProcessor: URLSessionDownloadDelegate { // Use this to handle and client-side download errors func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - guard let downloadTask = task as? URLSessionDownloadTask, 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 if cancellationReason == NSURLErrorCancelledReasonUserForceQuitApplication || cancellationReason == NSURLErrorCancelledReasonBackgroundUpdatesDisabled { // The download was cancelled for technical reasons, but we might be able to restart it... - var newTask: URLSessionDownloadTask? - if let resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData] as? Data { - newTask = session.downloadTask(withResumeData: resumeData) - } else { - let download = delegate.downloadProcessor(self, downloadModelForDownloadWithId: downloadId) - if let remoteURL = download?.remoteURL { - newTask = session.downloadTask(with: remoteURL) - } - } - if let newTask = newTask { - newTask.downloadId = downloadId - newTask.resume() - currentDownloads.append(newTask) - delegate.downloadProcessor(self, didStartDownloadWithId: downloadId) - } - + currentDownloads.removeAll { $0 == downloadTask } } else if error.code == NSURLErrorCancelled { // User-requested cancellation currentDownloads.removeAll { $0 == downloadTask } - + delegate.downloadProcessor(self, didCancelDownloadWithId: downloadId) } else { // Unknown error currentDownloads.removeAll { $0 == downloadTask } - + delegate.downloadProcessor(self, downloadWithId: downloadId, didFailWithError: error) } } else { // Success! currentDownloads.removeAll { $0 == downloadTask } - + delegate.downloadProcessor(self, didFinishDownloadWithId: downloadId) } } diff --git a/Emitron/Emitron/Downloads/DownloadService.swift b/Emitron/Emitron/Downloads/DownloadService.swift index dcf7ba41..ee5bddaa 100644 --- a/Emitron/Emitron/Downloads/DownloadService.swift +++ b/Emitron/Emitron/Downloads/DownloadService.swift @@ -299,28 +299,29 @@ extension DownloadService { } // Use the video service to request the URLs - videosService.getVideoDownload(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 - + switch result { case .failure(let error): Failure .downloadService(from: "requestDownloadURL", reason: "Unable to obtain download URLs: \(error)") .log() - case .success(let attachments): - download.remoteURL = attachments.first { $0.kind == self.downloadQuality }?.url + case .success(let attachment): + print("remote url: \(attachment.url)") + download.remoteURL = attachment.url download.lastValidatedAt = Date() download.state = .readyForDownload } - + // Update the state if required if download.remoteURL == nil { download.state = .error } - + // Commit the changes do { try self.persistenceStore.update(download: download) @@ -332,7 +333,6 @@ extension DownloadService { self.transitionDownload(withID: download.id, to: .failed) } } - // Move it on through the state machine transitionDownload(withID: downloadQueueItem.download.id, to: .urlRequested) } @@ -356,7 +356,7 @@ extension DownloadService { } // Generate filename - let filename = "\(videoId).mp4" + let filename = "\(videoId).m3u8" // Save local URL and filename var download = downloadQueueItem.download diff --git a/Emitron/Emitron/Networking/Requests/VideosRequest.swift b/Emitron/Emitron/Networking/Requests/VideosRequest.swift index 51938daf..3d7455c5 100644 --- a/Emitron/Emitron/Networking/Requests/VideosRequest.swift +++ b/Emitron/Emitron/Networking/Requests/VideosRequest.swift @@ -56,13 +56,12 @@ struct StreamVideoRequest: Request { } } -struct DownloadVideoRequest: Request { - // It contains two Attachment objects, one for the HD file and one for the SD file. - typealias Response = [Attachment] +struct DownloadStreamVideoRequest: Request { + typealias Response = Attachment // MARK: - Properties var method: HTTPMethod { .GET } - var path: String { "/videos/\(id)/download" } + var path: String { "/videos/\(id)/stream" } var additionalHeaders: [String: String] = [:] var body: Data? { nil } @@ -70,9 +69,16 @@ struct DownloadVideoRequest: Request { let id: Int // MARK: - Internal - func handle(response: Data) throws -> [Attachment] { + func handle(response: Data) throws -> Attachment { let json = try JSON(data: response) let doc = JSONAPIDocument(json) - return try doc.data.map { try AttachmentAdapter.process(resource: $0) } + let attachments = try doc.data.map { try AttachmentAdapter.process(resource: $0) } + + guard let attachment = attachments.first, + attachments.count == 1 else { + throw RWAPIError.responseHasIncorrectNumberOfElements + } + + return attachment } } diff --git a/Emitron/Emitron/Networking/Services/Service.swift b/Emitron/Emitron/Networking/Services/Service.swift index b13df6cd..0d98604d 100644 --- a/Emitron/Emitron/Networking/Services/Service.swift +++ b/Emitron/Emitron/Networking/Services/Service.swift @@ -85,7 +85,7 @@ class Service { func prepare(request: R, parameters: [Parameter]?) -> URLRequest? { let pathURL = networkClient.environment.baseURL.appendingPathComponent(request.path) - + print("pathURL: \(pathURL)") var components = URLComponents(url: pathURL, resolvingAgainstBaseURL: false) diff --git a/Emitron/Emitron/Networking/Services/VideosService.swift b/Emitron/Emitron/Networking/Services/VideosService.swift index 25f223ed..37278619 100644 --- a/Emitron/Emitron/Networking/Services/VideosService.swift +++ b/Emitron/Emitron/Networking/Services/VideosService.swift @@ -37,9 +37,9 @@ class VideosService: Service { completion: completion) } - func getVideoDownload(for id: Int, - completion: @escaping (_ response: Result) -> Void) { - let request = DownloadVideoRequest(id: id) + func getVideoStreamDownload(for id: Int, + completion: @escaping (_ response: Result) -> Void) { + let request = DownloadStreamVideoRequest(id: id) makeAndProcessRequest(request: request, completion: completion) } diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift index 4c7bf65c..3ea6530a 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift @@ -338,7 +338,6 @@ extension PersistenceStore { } } - /// Save the entire graph of models to supprt this ContentDeailsModel /// - Parameter contentPersistableState: The model to persist—from the DataCache. func persistContentGraph(for contentPersistableState: ContentPersistableState, contentLookup: ContentLookup? = nil) -> Future { diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift index 4a423a0e..d0547084 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift @@ -62,6 +62,11 @@ private extension ChildContentListingView { .kerning(-0.5) .foregroundColor(.titleText) .padding([.top, .bottom]) + Button { + exit(0) + } label: { + Text("Close App0") + } Spacer() }.padding([.leading, .trailing], 20) } diff --git a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift index aaa80a64..9a546ca6 100644 --- a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift @@ -600,33 +600,33 @@ class DownloadServiceTest: XCTestCase { } } - func testRequestDownloadURLRespectsTheUserPreferencesOnQuality() throws { - let downloadQueueItem = try sampleDownloadQueueItem() - let attachment = AttachmentTest.Mocks.downloads.0.first { $0.kind == .sdVideoFile }! - - SettingsManager.current.downloadQuality = .sdVideoFile - - downloadService.requestDownloadURL(downloadQueueItem) - - try database.read { db in - let download = try Download.fetchOne(db, key: downloadQueueItem.download.id)! - XCTAssertNotNil(download.remoteURL) - XCTAssertEqual(attachment.url, download.remoteURL) - } - } - - func testRequestDownloadDefaultsToHDQuality() throws { - let downloadQueueItem = try sampleDownloadQueueItem() - let attachment = AttachmentTest.Mocks.downloads.0.first { $0.kind == .hdVideoFile }! - - downloadService.requestDownloadURL(downloadQueueItem) - - try database.read { db in - let download = try Download.fetchOne(db, key: downloadQueueItem.download.id)! - XCTAssertNotNil(download.remoteURL) - XCTAssertEqual(attachment.url, download.remoteURL) - } - } +// func testRequestDownloadURLRespectsTheUserPreferencesOnQuality() throws { +// let downloadQueueItem = try sampleDownloadQueueItem() +// let attachment = AttachmentTest.Mocks.download.0 { $0.kind == .sdVideoFile } +// +// SettingsManager.current.downloadQuality = .sdVideoFile +// +// downloadService.requestDownloadURL(downloadQueueItem) +// +// try database.read { db in +// let download = try Download.fetchOne(db, key: downloadQueueItem.download.id)! +// XCTAssertNotNil(download.remoteURL) +// XCTAssertEqual(attachment.url, download.remoteURL) +// } +// } + +// func testRequestDownloadDefaultsToHDQuality() throws { +// let downloadQueueItem = try sampleDownloadQueueItem() +// let attachment = AttachmentTest.Mocks.downloads.0.first { $0.kind == .hdVideoFile }! +// +// downloadService.requestDownloadURL(downloadQueueItem) +// +// try database.read { db in +// let download = try Download.fetchOne(db, key: downloadQueueItem.download.id)! +// XCTAssertNotNil(download.remoteURL) +// XCTAssertEqual(attachment.url, download.remoteURL) +// } +// } func testRequestDownloadUpdatesTheStateCorrectly() throws { let downloadQueueItem = try sampleDownloadQueueItem() @@ -660,40 +660,39 @@ class DownloadServiceTest: XCTestCase { } } - func testEnqueueUpdatesStateToCompletedIfItFindsDownload() throws { - let downloadQueueItem = try sampleDownloadQueueItem() - var download = downloadQueueItem.download - download.remoteURL = URL(string: "https://example.com/amazing.mp4") - download.fileName = "\(downloadQueueItem.content.videoIdentifier!).mp4" - download.state = .readyForDownload - try database.write { db in - try download.save(db) - } - - let fileManager = FileManager.default - let documentsDirectories = fileManager.urls(for: .documentDirectory, in: .userDomainMask) - let documentsDirectory = documentsDirectories.first - let downloadsDirectory = documentsDirectory!.appendingPathComponent("downloads", isDirectory: true) - - let sampleFile = downloadsDirectory.appendingPathComponent(download.fileName!) - - XCTAssert(!fileManager.fileExists(atPath: sampleFile.path)) - - fileManager.createFile(atPath: sampleFile.path, contents: nil) - - XCTAssert(fileManager.fileExists(atPath: sampleFile.path)) - - let newQueueItem = PersistenceStore.DownloadQueueItem(download: download, content: downloadQueueItem.content) - downloadService.enqueue(downloadQueueItem: newQueueItem) - - try database.read { db in - let refreshedDownload = try Download.fetchOne(db, key: download.id)! - XCTAssertEqual(Download.State.complete, refreshedDownload.state) - XCTAssertEqual(sampleFile, refreshedDownload.localURL) - } - - try fileManager.removeItem(at: sampleFile) - } +// func testEnqueueUpdatesStateToCompletedIfItFindsDownload() throws { +// let downloadQueueItem = try sampleDownloadQueueItem() +// var download = downloadQueueItem.download +// download.remoteURL = URL(string: "https://example.com/amazing.mp4") +// download.fileName = "\(downloadQueueItem.content.videoIdentifier!).mp4" +// download.state = .readyForDownload +// try database.write { db in +// try download.save(db) +// } +// +// let fileManager = FileManager.default +// let documentsDirectories = fileManager.urls(for: .documentDirectory, in: .userDomainMask) +// let documentsDirectory = documentsDirectories.first +// let downloadsDirectory = documentsDirectory!.appendingPathComponent("downloads", isDirectory: true) +// +// let sampleFile = downloadsDirectory.appendingPathComponent(download.fileName!) +// +// XCTAssert(!fileManager.fileExists(atPath: sampleFile.path)) +// +// fileManager.createFile(atPath: sampleFile.path, contents: nil) +// +// XCTAssert(fileManager.fileExists(atPath: sampleFile.path)) +// +// let newQueueItem = PersistenceStore.DownloadQueueItem(download: download, content: downloadQueueItem.content) +// downloadService.enqueue(downloadQueueItem: newQueueItem) +// +// try database.read { db in +// let refreshedDownload = try Download.fetchOne(db, key: download.id)! +// XCTAssertEqual(Download.State.complete, refreshedDownload.state) +// XCTAssertEqual(sampleFile, refreshedDownload.localURL) +// } +// try fileManager.removeItem(at: sampleFile) +// } func testEnqueueDoesNothingForADownloadWithoutARemoteURL() throws { let downloadQueueItem = try sampleDownloadQueueItem() diff --git a/Emitron/emitronTests/Models/AttachmentTest+Mocks.swift b/Emitron/emitronTests/Models/AttachmentTest+Mocks.swift index ac34fc59..7ff4de0a 100644 --- a/Emitron/emitronTests/Models/AttachmentTest+Mocks.swift +++ b/Emitron/emitronTests/Models/AttachmentTest+Mocks.swift @@ -32,16 +32,16 @@ import SwiftyJSON extension AttachmentTest { enum Mocks { - static var downloads: ([Attachment], DataCacheUpdate) { + static var download: (Attachment, DataCacheUpdate) { loadMockFrom(filename: "Attachment_Downloads") } static var stream: (Attachment, DataCacheUpdate) { - let (attachments, cacheUpdate) = loadMockFrom(filename: "Attachment_Stream") - return (attachments.first!, cacheUpdate) + let (attachment, cacheUpdate) = loadMockFrom(filename: "Attachment_Stream") + return (attachment, cacheUpdate) } - private static func loadMockFrom(filename: String) -> ([Attachment], DataCacheUpdate) { + private static func loadMockFrom(filename: String) -> (Attachment, DataCacheUpdate) { do { let bundle = Bundle(for: AttachmentTest.self) let fileURL = bundle.url(forResource: filename, withExtension: "json") @@ -53,7 +53,7 @@ extension AttachmentTest { try AttachmentAdapter.process(resource: resource) } let cacheUpdate = try DataCacheUpdate.loadFrom(document: document) - return (attachments, cacheUpdate) + return (attachments[0], cacheUpdate) } catch { preconditionFailure("Unable to load Attachment mock: \(error)") } diff --git a/Emitron/emitronTests/Models/Mocks/Attachment_Downloads.json b/Emitron/emitronTests/Models/Mocks/Attachment_Downloads.json index a7eb5ad6..046a5597 100644 --- a/Emitron/emitronTests/Models/Mocks/Attachment_Downloads.json +++ b/Emitron/emitronTests/Models/Mocks/Attachment_Downloads.json @@ -1,13 +1,5 @@ { "data":[ - { - "id":"30194", - "type":"attachments", - "attributes":{ - "url":"https://player.vimeo.com/external/332761683.sd.mp4?s=263eeb3fa49a8e8a5fea81c267bec4071d08bc78\u0026profile_id=164\u0026oauth2_token_id=897711146", - "kind":"sd_video_file" - } - }, { "id":"30195", "type":"attachments", diff --git a/Emitron/emitronTests/Services/Mock/VideosServiceMock.swift b/Emitron/emitronTests/Services/Mock/VideosServiceMock.swift index 63e5df33..0fa9aaef 100644 --- a/Emitron/emitronTests/Services/Mock/VideosServiceMock.swift +++ b/Emitron/emitronTests/Services/Mock/VideosServiceMock.swift @@ -47,8 +47,8 @@ class VideosServiceMock: VideosService { getVideoStreamCount += 1 } - override func getVideoDownload(for id: Int, completion: @escaping (Result) -> Void) { + override func getVideoStreamDownload(for id: Int, completion: @escaping (Result) -> Void) { getVideoDownloadCount += 1 - completion(Result.success(AttachmentTest.Mocks.downloads.0)) + completion(Result.success(AttachmentTest.Mocks.download.0)) } } From a328718ebe517fde107bfbb50a93b166bd116fef Mon Sep 17 00:00:00 2001 From: crispy8888 Date: Wed, 3 Mar 2021 20:32:49 -0400 Subject: [PATCH 39/69] Updates README.md with typo fix --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 7dadf147..47f36f3f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ If you find a __bug__, please create an issue. If you find a __security vulnerability__, please contact emitron@razeware.com as soon as possible. See [SECURITY.md](SECURITY.md) for further details. -There is more info about contributing in [CONTRIBUITNG.md](CONTRIBUTING.md). +There is more info about contributing in [CONTRIBUTING.md](CONTRIBUTING.md). ## Development @@ -44,7 +44,6 @@ If you are working on the download functionality and are having problems without The two `xcconfig` files are used to configure the project. To access the values specified, these files must be added to the __Info.plist__ file. -Use the `Configuration` struct to access these values from code. For more details on this approach, check out https://nshipster.com/xcconfig/ From bb2f3677285dd4bc2ad60eef4860937f21364d64 Mon Sep 17 00:00:00 2001 From: crispy8888 Date: Thu, 4 Mar 2021 09:09:05 -0400 Subject: [PATCH 40/69] Reverts dropped line --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 47f36f3f..02d996ce 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ If you are working on the download functionality and are having problems without The two `xcconfig` files are used to configure the project. To access the values specified, these files must be added to the __Info.plist__ file. +Use the `Configuration` struct to access these values from code. For more details on this approach, check out https://nshipster.com/xcconfig/ From 1170b575e72b47f68f44b593d2e81237f30f4272 Mon Sep 17 00:00:00 2001 From: Martin Rist Date: Tue, 16 Mar 2021 17:16:03 +0000 Subject: [PATCH 41/69] Remove 'Install SwiftLint' step SwiftLint is installed in GitHub's base virtual environment image since [`macos-10.14`](https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.14-Readme.md). Attempting to install SwiftLint using `brew` was causing this workflow to fail with the following errors, since `brew` was unable to overwrite the `swiftlint` symlink already installed in the image: ``` Error: The `brew link` step did not complete successfully ... Error: Process completed with exit code 1. ``` Note: this used to work because the workflow is using `macos-latest`. This used to resolve to [`macos-10.13`](https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.13-Readme.md) which didn't include SwiftLint in the base image. --- .github/workflows/swiftlint.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/swiftlint.yml b/.github/workflows/swiftlint.yml index ba5023f4..88a7de0d 100644 --- a/.github/workflows/swiftlint.yml +++ b/.github/workflows/swiftlint.yml @@ -12,8 +12,6 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v1 - - name: Install Swiftlint - run: brew install swiftlint - name: Run Swiftlint run: swiftlint --config Emitron/.swiftlint.yml From bd7ff33410033bff29ccfafdf2bc24654fa4ddec Mon Sep 17 00:00:00 2001 From: Martin Rist Date: Wed, 17 Mar 2021 16:32:24 +0000 Subject: [PATCH 42/69] Increase wait timeout to fix CI A number of tests have been failing on CI for recent PRs. This commit increases the timeout for tests that have been affected on the most recent PRs. --- .../Downloads/DownloadQueueManagerTest.swift | 2 +- .../Downloads/DownloadServiceTest.swift | 14 +++++++------- .../PersistenceStore+DownloadsTest.swift | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift b/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift index 0729c180..0d8be149 100644 --- a/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift @@ -107,7 +107,7 @@ class DownloadQueueManagerTest: XCTestCase { } .record() - let completion = try wait(for: recorder.completion, timeout: 5) + let completion = try wait(for: recorder.completion, timeout: 10) XCTAssert(completion == .finished) diff --git a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift index aaa80a64..56df50a2 100644 --- a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift @@ -86,7 +86,7 @@ class DownloadServiceTest: XCTestCase { } .record() - let completion = try wait(for: recorder.completion, timeout: 5) + let completion = try wait(for: recorder.completion, timeout: 10) XCTAssert(completion == .finished) let download = getAllDownloads().first! @@ -206,7 +206,7 @@ class DownloadServiceTest: XCTestCase { } .record() - let completion = try wait(for: recorder.completion, timeout: 5) + let completion = try wait(for: recorder.completion, timeout: 10) XCTAssert(completion == .finished) let allContentIds = fullState.childContents.map(\.id) + [collection.0.id] @@ -330,7 +330,7 @@ class DownloadServiceTest: XCTestCase { } .record() - let completion = try wait(for: recorder2.completion, timeout: 5) + let completion = try wait(for: recorder2.completion, timeout: 10) XCTAssert(completion == .finished) // Added the correct number of models @@ -354,7 +354,7 @@ class DownloadServiceTest: XCTestCase { } .record() - let completion = try wait(for: recorder.completion, timeout: 5) + let completion = try wait(for: recorder.completion, timeout: 10) XCTAssert(completion == .finished) XCTAssertEqual(2, getAllDownloads().count) @@ -381,9 +381,9 @@ class DownloadServiceTest: XCTestCase { } .record() - _ = try wait(for: recorder1.completion, timeout: 5) - _ = try wait(for: recorder2.completion, timeout: 5) - _ = try wait(for: recorder3.completion, timeout: 5) + _ = try wait(for: recorder1.completion, timeout: 10) + _ = try wait(for: recorder2.completion, timeout: 10) + _ = try wait(for: recorder3.completion, timeout: 10) XCTAssertEqual(4, getAllDownloads().count) } diff --git a/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift b/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift index 39097b3e..108ad5d3 100644 --- a/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift +++ b/Emitron/emitronTests/Persistence/PersistenceStore+DownloadsTest.swift @@ -67,7 +67,7 @@ class PersistenceStore_DownloadsTest: XCTestCase { } .record() - _ = try wait(for: recorder.completion, timeout: 5) + _ = try wait(for: recorder.completion, timeout: 10) return screencast.0 } @@ -80,7 +80,7 @@ class PersistenceStore_DownloadsTest: XCTestCase { } .record() - _ = try wait(for: recorder.completion, timeout: 5) + _ = try wait(for: recorder.completion, timeout: 10) return collection.0 } From d0c15211a2a54b3c895c4f1275745aa36cf2ff4f Mon Sep 17 00:00:00 2001 From: Derrick Plotsky Date: Thu, 18 Mar 2021 12:21:03 -0700 Subject: [PATCH 43/69] Fix redundant type warnings There was a couple spots where we had some unnecessary type information when we were creating some variables, and I was getting warnings in XCode about it (I assume from Swiftlint). --- Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift | 2 +- Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift | 2 +- Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift | 2 +- Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift | 2 +- Emitron/Emitron/UI/Library/LibraryView.swift | 2 +- Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift | 2 +- Emitron/Emitron/UI/Settings/SettingsView.swift | 2 +- .../UI/Video/FullScreenVideoPlayerViewController.swift | 2 +- Emitron/Emitron/Utilities/MessageBus.swift | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift index 8846dd4a..3ee0a27e 100644 --- a/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/DynamicContentViewModel.swift @@ -40,7 +40,7 @@ final class DynamicContentViewModel: ObservableObject, DynamicContentDisplayable var state: DataState = .initial @Published var viewProgress: ContentViewProgressDisplayable = .notStarted @Published var downloadProgress: DownloadProgressDisplayable = .notDownloadable - @Published var bookmarked: Bool = false + @Published var bookmarked = false private var subscriptions = Set() private var downloadActionSubscriptions = Set() diff --git a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift index 5dd367b2..15500e7e 100644 --- a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift @@ -74,7 +74,7 @@ extension VideoPlaybackViewModel { final class VideoPlaybackViewModel { // Allow control of appearance and dismissal of the video view - var shouldShow: Bool = false + var shouldShow = false private let initialContentId: Int private let repository: Repository diff --git a/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift b/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift index b17b615b..c1acd3f3 100644 --- a/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift +++ b/Emitron/Emitron/UI/App Root/PermissionsLoadingView.swift @@ -30,7 +30,7 @@ import SwiftUI struct PermissionsLoadingView: View { @EnvironmentObject var sessionController: SessionController - @State var showLogoutAlert: Bool = false + @State var showLogoutAlert = false var body: some View { LoadingView() diff --git a/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift b/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift index af576502..3507a101 100644 --- a/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift +++ b/Emitron/Emitron/UI/Library/Filtering/FiltersHeaderView.swift @@ -42,7 +42,7 @@ struct FiltersHeaderView: View { var filterGroup: FilterGroup @ObservedObject var filters: Filters - @State var isExpanded: Bool = false + @State var isExpanded = false var body: some View { VStack { diff --git a/Emitron/Emitron/UI/Library/LibraryView.swift b/Emitron/Emitron/UI/Library/LibraryView.swift index fdada262..1c371293 100644 --- a/Emitron/Emitron/UI/Library/LibraryView.swift +++ b/Emitron/Emitron/UI/Library/LibraryView.swift @@ -38,7 +38,7 @@ private extension CGFloat { struct LibraryView: View { @ObservedObject var filters: Filters @ObservedObject var libraryRepository: LibraryRepository - @State var filtersPresented: Bool = false + @State var filtersPresented = false var body: some View { contentView diff --git a/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift b/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift index cd72f7ec..829fc718 100644 --- a/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift +++ b/Emitron/Emitron/UI/Settings/Licenses/LicenseListView.swift @@ -66,7 +66,7 @@ struct LicenseListView: View { } struct LicenseListView_Previews: PreviewProvider { - @State static var visible: Bool = true + @State static var visible = true static var previews: some View { SwiftUI.Group { diff --git a/Emitron/Emitron/UI/Settings/SettingsView.swift b/Emitron/Emitron/UI/Settings/SettingsView.swift index 643166a4..12dfc051 100644 --- a/Emitron/Emitron/UI/Settings/SettingsView.swift +++ b/Emitron/Emitron/UI/Settings/SettingsView.swift @@ -38,7 +38,7 @@ struct SettingsView: View { @EnvironmentObject var sessionController: SessionController @EnvironmentObject var tabViewModel: TabViewModel @ObservedObject private var settingsManager = SettingsManager.current - @State private var licensesPresented: Bool = false + @State private var licensesPresented = false var body: some View { VStack { diff --git a/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift b/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift index 5702ca84..aa861d1b 100644 --- a/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift +++ b/Emitron/Emitron/UI/Video/FullScreenVideoPlayerViewController.swift @@ -32,7 +32,7 @@ import SwiftUI class FullScreenVideoPlayerViewController: UIViewController { @Binding var viewModel: VideoPlaybackViewModel? - private var isFullscreen: Bool = false + private var isFullscreen = false init(viewModel: Binding) { _viewModel = viewModel diff --git a/Emitron/Emitron/Utilities/MessageBus.swift b/Emitron/Emitron/Utilities/MessageBus.swift index 75f38692..d64c6de6 100644 --- a/Emitron/Emitron/Utilities/MessageBus.swift +++ b/Emitron/Emitron/Utilities/MessageBus.swift @@ -36,7 +36,7 @@ struct Message { let level: Level let message: String - var autoDismiss: Bool = true + var autoDismiss = true } extension Message { @@ -60,7 +60,7 @@ extension Message.Level { final class MessageBus: ObservableObject { @Published private(set) var currentMessage: Message? - @Published var messageVisible: Bool = false + @Published var messageVisible = false private var currentTimer: AnyCancellable? From 96f0f2e236ae864f7cc4f98ea420654b34706e39 Mon Sep 17 00:00:00 2001 From: Derrick Plotsky Date: Thu, 18 Mar 2021 12:30:00 -0700 Subject: [PATCH 44/69] Fix comment space warnings XCode is telling me there should always be a space after the double slash, and the rest of the comments are already this way. This just fixes those warnings. --- Emitron/Emitron/Downloads/DownloadProcessor.swift | 2 +- Emitron/Emitron/Networking/Requests/Parameters.swift | 2 +- Emitron/Emitron/UI/Shared/CheckmarkView.swift | 2 +- .../UI/Shared/Content Detail/ChildContentListingView.swift | 4 ++-- .../Emitron/UI/Shared/Content Detail/ContentSummaryView.swift | 4 ++-- Emitron/Emitron/UI/Shared/Content List/ContentListView.swift | 2 +- Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Emitron/Emitron/Downloads/DownloadProcessor.swift b/Emitron/Emitron/Downloads/DownloadProcessor.swift index 8f0e5336..0056f446 100644 --- a/Emitron/Emitron/Downloads/DownloadProcessor.swift +++ b/Emitron/Emitron/Downloads/DownloadProcessor.swift @@ -69,7 +69,7 @@ final class DownloadProcessor: NSObject { let config = URLSessionConfiguration.background(withIdentifier: DownloadProcessor.sessionIdentifier) // Uncommenting this causes the download task to fail with POSIX 22. But seemingly only with // Vimeo URLs. So that's handy. - //config.isDiscretionary = true + // config.isDiscretionary = true config.sessionSendsLaunchEvents = true return URLSession(configuration: config, delegate: self, delegateQueue: .none) }() diff --git a/Emitron/Emitron/Networking/Requests/Parameters.swift b/Emitron/Emitron/Networking/Requests/Parameters.swift index e68c88ca..ca975512 100644 --- a/Emitron/Emitron/Networking/Requests/Parameters.swift +++ b/Emitron/Emitron/Networking/Requests/Parameters.swift @@ -162,7 +162,7 @@ enum ParameterFilterValue { } } -//sort=-released_at; reversechronological order +// sort=-released_at; reversechronological order enum ParameterSortValue: String, Codable { case popularity = "popularity" case releasedAt = "released_at" diff --git a/Emitron/Emitron/UI/Shared/CheckmarkView.swift b/Emitron/Emitron/UI/Shared/CheckmarkView.swift index 56f27622..513b6f2a 100644 --- a/Emitron/Emitron/UI/Shared/CheckmarkView.swift +++ b/Emitron/Emitron/UI/Shared/CheckmarkView.swift @@ -28,7 +28,7 @@ import SwiftUI -//TODO: Refactor layout properties here +// TODO: Refactor layout properties here struct CheckmarkView: View { var isOn: Bool diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift index 4a423a0e..092585a3 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift @@ -166,8 +166,8 @@ private extension ChildContentListingView { } } -//struct ChildContentListingView_Previews: PreviewProvider { +// struct ChildContentListingView_Previews: PreviewProvider { // static var previews: some View { // ChildContentListingView() // } -//} +// } diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift index 674b5822..775a677f 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift @@ -130,8 +130,8 @@ private extension ContentSummaryView { } private var bookmarkButton: some View { - //ISSUE: Changing this from button to "onTapGesture" because the tap target between the download button and the - //bookmark button somehow wasn't... clearly defined, so they'd both get pressed when the bookmark button got pressed + // ISSUE: Changing this from button to "onTapGesture" because the tap target between the download button and the + // bookmark button somehow wasn't... clearly defined, so they'd both get pressed when the bookmark button got pressed Image.bookmark .resizable() .frame(width: Layout.buttonSize, height: Layout.buttonSize) diff --git a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift index da964ace..28969819 100644 --- a/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/ContentListView.swift @@ -105,7 +105,7 @@ private extension ContentListView { navLink(for: partialContent) .buttonStyle(PlainButtonStyle()) - //HACK: to remove navigation chevrons + // HACK: to remove navigation chevrons .padding(.trailing, -2 * .sidePadding) } } diff --git a/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift b/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift index 0729c180..44bbf566 100644 --- a/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadQueueManagerTest.swift @@ -167,8 +167,8 @@ class DownloadQueueManagerTest: XCTestCase { } // This shouldn't fire cos it doesn't affect the stream - //readyForDownload = try wait(for: recorder.next(), timeout: 5) - //XCTAssertEqual(download1, readyForDownload!!.download) + // readyForDownload = try wait(for: recorder.next(), timeout: 5) + // XCTAssertEqual(download1, readyForDownload!!.download) try database.write { db in download1.state = .enqueued From ff5115492b72b702c4f37536f566e3fcbf000242 Mon Sep 17 00:00:00 2001 From: Derrick Plotsky Date: Thu, 18 Mar 2021 12:31:43 -0700 Subject: [PATCH 45/69] Fix vertical whitespace warning Only one whitespace inside of the code instead of 2. --- Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift index 4c7bf65c..ffb2a5d5 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift @@ -337,7 +337,6 @@ extension PersistenceStore { } } } - /// Save the entire graph of models to supprt this ContentDeailsModel /// - Parameter contentPersistableState: The model to persist—from the DataCache. From 796d15e2ee618c41fbe3235770adacf5bab307b7 Mon Sep 17 00:00:00 2001 From: Brian Moakley Date: Fri, 19 Mar 2021 15:01:43 -0400 Subject: [PATCH 46/69] Removes deprecated tests --- .../Downloads/DownloadServiceTest.swift | 62 ------------------- 1 file changed, 62 deletions(-) diff --git a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift index 9a546ca6..74292444 100644 --- a/Emitron/emitronTests/Downloads/DownloadServiceTest.swift +++ b/Emitron/emitronTests/Downloads/DownloadServiceTest.swift @@ -600,34 +600,6 @@ class DownloadServiceTest: XCTestCase { } } -// func testRequestDownloadURLRespectsTheUserPreferencesOnQuality() throws { -// let downloadQueueItem = try sampleDownloadQueueItem() -// let attachment = AttachmentTest.Mocks.download.0 { $0.kind == .sdVideoFile } -// -// SettingsManager.current.downloadQuality = .sdVideoFile -// -// downloadService.requestDownloadURL(downloadQueueItem) -// -// try database.read { db in -// let download = try Download.fetchOne(db, key: downloadQueueItem.download.id)! -// XCTAssertNotNil(download.remoteURL) -// XCTAssertEqual(attachment.url, download.remoteURL) -// } -// } - -// func testRequestDownloadDefaultsToHDQuality() throws { -// let downloadQueueItem = try sampleDownloadQueueItem() -// let attachment = AttachmentTest.Mocks.downloads.0.first { $0.kind == .hdVideoFile }! -// -// downloadService.requestDownloadURL(downloadQueueItem) -// -// try database.read { db in -// let download = try Download.fetchOne(db, key: downloadQueueItem.download.id)! -// XCTAssertNotNil(download.remoteURL) -// XCTAssertEqual(attachment.url, download.remoteURL) -// } -// } - func testRequestDownloadUpdatesTheStateCorrectly() throws { let downloadQueueItem = try sampleDownloadQueueItem() @@ -660,40 +632,6 @@ class DownloadServiceTest: XCTestCase { } } -// func testEnqueueUpdatesStateToCompletedIfItFindsDownload() throws { -// let downloadQueueItem = try sampleDownloadQueueItem() -// var download = downloadQueueItem.download -// download.remoteURL = URL(string: "https://example.com/amazing.mp4") -// download.fileName = "\(downloadQueueItem.content.videoIdentifier!).mp4" -// download.state = .readyForDownload -// try database.write { db in -// try download.save(db) -// } -// -// let fileManager = FileManager.default -// let documentsDirectories = fileManager.urls(for: .documentDirectory, in: .userDomainMask) -// let documentsDirectory = documentsDirectories.first -// let downloadsDirectory = documentsDirectory!.appendingPathComponent("downloads", isDirectory: true) -// -// let sampleFile = downloadsDirectory.appendingPathComponent(download.fileName!) -// -// XCTAssert(!fileManager.fileExists(atPath: sampleFile.path)) -// -// fileManager.createFile(atPath: sampleFile.path, contents: nil) -// -// XCTAssert(fileManager.fileExists(atPath: sampleFile.path)) -// -// let newQueueItem = PersistenceStore.DownloadQueueItem(download: download, content: downloadQueueItem.content) -// downloadService.enqueue(downloadQueueItem: newQueueItem) -// -// try database.read { db in -// let refreshedDownload = try Download.fetchOne(db, key: download.id)! -// XCTAssertEqual(Download.State.complete, refreshedDownload.state) -// XCTAssertEqual(sampleFile, refreshedDownload.localURL) -// } -// try fileManager.removeItem(at: sampleFile) -// } - func testEnqueueDoesNothingForADownloadWithoutARemoteURL() throws { let downloadQueueItem = try sampleDownloadQueueItem() var download = downloadQueueItem.download From 22912ec87f1f6c050a650d648124580047245070 Mon Sep 17 00:00:00 2001 From: Tim Brooks Date: Sat, 20 Mar 2021 14:55:29 -0700 Subject: [PATCH 47/69] Update settings image --- .../TabBar/settings.imageset/Contents.json | 26 ++++++++++++++++++ .../TabBar/settings.imageset/gear.png | Bin 0 -> 2721 bytes .../TabBar/settings.imageset/gear@2x.png | Bin 0 -> 4940 bytes .../TabBar/settings.imageset/gear@3x.png | Bin 0 -> 7255 bytes Emitron/Emitron/UI/App Root/TabNavView.swift | 2 +- 5 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/Contents.json create mode 100644 Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/gear.png create mode 100644 Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/gear@2x.png create mode 100644 Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/gear@3x.png diff --git a/Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/Contents.json b/Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/Contents.json new file mode 100644 index 00000000..10ed1347 --- /dev/null +++ b/Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "gear.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "gear@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "gear@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/gear.png b/Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/gear.png new file mode 100644 index 0000000000000000000000000000000000000000..c8d07d469667b5b1ad08bc4dc722fdee3cd0ff27 GIT binary patch literal 2721 zcmZ{m2{@GN7r?*Cl68uvC}bMc*k;c%F^Iqx~=ci!{9-}#MfUPZ{0or1K2xJ2wr~m=Q8lv#cc7$Mo6`)TZ6b}e)$%cV8 z=P&~h2DKnR9u&c235sC7q|HMj7@>i1TRN2oH>R;Tp>Q3P4hj$8FgPp`gC}D3;W!)- zOCS>P03ehP0fd%lkk4Q7t+GPC(03b*0})!;C7=>#Te@=rKyr;hAV5|&*q)FT!`Y4J zW@|&Fv6v{TKg*Af3TLtf6krfe1WhKLM}>zoLqfU4a6`mW1`)IcZZrbEl)?)(M7Y`7 z!%bNnI@mA;WA*j*(HI;W zhtmc#w7C(XJZiXhC|Bikk*{^gbS{m66L+woxee z%c;Qgp~Idd#P8=^s$t9WX9Pr$sXRJKFdW(#yf#+f`Rj0e#+T(RqXowy+B3rGA#P*_ zlODd?jQ-+*}sYjzfM zQoUTpsxmK{j(9}r^?ESu&h%Dle)Q_Ywr5$&qvIc6G!La`_$d`;WGD3v++4H0^|tKD zAFU&H=R0_q$zd7(?{x)uN>a3k9^SCz!Rd=l>WzD{hx{}7^B;C-%ho`5NK&X`H*!K;))uv#Rp=zOC#el6`k1M#Zzb+(E%0OKUbaqD)sccjF4)WIT&UXZ zqXpA)30Jk38)h)TF(tMX?9cZ$6c8T!hH6qjc?w z6U`Wejnh^g3mX5Rt}8(qKxrzf#d#gu@^G_na`M3g{{DY3Sf1Qmd>sPF8RZQYPiXP$>K?mJ4BdCVtOn5*I4k45ca>4`t~EY$!HMd$@znVqqmF44eUkv( zX{|O5T5J`UfGk~rrukH{2~F5GlRj6eRliwi?6M!h^Pe7E9iueh>e7XQ{cgcAyyB<- zqH_1ZwG68nWJ#n^jAZF>_obLpBuv64uB|58DX*umy&dLoqJ9&^BLwzDtya3NPxQ4| zT?{qSG512^{rcZVQo6)j3cC-#8+uw!i>zEs7p)ueQICL1dzYHaL9h0u{#|m$LwTdh z?iG6;Uw@8052cX{P7fn_igF#uusN@d%;4Y}DTil;`DMfmvd$T4u0q$MmY%|4*>2Uh zX7cF-SbFh;qf=3O3*C2{@CT6}SiaNA5^#mJGV0ZO()pCzzWkBRaGd#Ec~bt(nwqKp z{?TnR4?XY0Eu9?vPE=yTsjlkLSAAmAgPc=66os3R6-LK9a);Yg-S^ITQ&PgVZe?We z?j(7qiJP3WqDY3x6<%r_G^RgI;MdnHyRF!K*8TLOM;eLg#2mdCC$jF4jo)fAdn9`Q zglDvA=i?k-FZ>aiF_?px0)c(jSwqA-qu}Msi+RX3cEp^&0s4_ND*VuFyi*Kbk%AX? zt(G-AaP5}Q{CK0%Imdr**VODopDC$F_7C`Sh2GRs8m+p;d!$a7yd1&aTs^y&Fg^78 z^{yM|1ETGp-6F3<4r!8n#0ZdB-Aswz!ioy~!azMbWi6#nGk#RgIr3fI`%ahGQZ_Vy zSStr`l>O_N`QqrC+2Q@qpJ(-*%&pcvT7NGUy6t(_)^U}AD;DjWHSI#)LE`!QJ3hf- z(#rDkZu#{0w+5<{Bt;fhIeIp-l}CcSd$FIQj*oT2`KnjtllhjJ^Tj=P6=O5Pcs!eP zj~7Yydr?DkJ69TydaCz!S+XWyH3Zo;y5-D0Y^Y?-+Cf{5j;$3}ja=OM_T&@m*vUT4 dnbMgB-R36U^Mo5$G6a97tjz4lr6#^H{{ew^EVcju literal 0 HcmV?d00001 diff --git a/Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/gear@2x.png b/Emitron/Emitron/Assets.xcassets/TabBar/settings.imageset/gear@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5befa3d445548eafc86009bc9ebc0945ceec34d1 GIT binary patch literal 4940 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½<1v7-^rVmW-kE1)Hab}{tBwAUtpSQI;y40mLdNS DC2|N| literal 0 HcmV?d00001 diff --git a/Emitron/Emitron/UI/App Root/TabNavView.swift b/Emitron/Emitron/UI/App Root/TabNavView.swift index 58b32736..7a6b05c7 100644 --- a/Emitron/Emitron/UI/App Root/TabNavView.swift +++ b/Emitron/Emitron/UI/App Root/TabNavView.swift @@ -76,7 +76,7 @@ struct TabNavView< NavigationView { settingsView } .tabItem { Text(String.settings) - Image(systemName: "gearshape.fill") + Image("settings") } .tag(MainTab.settings) .navigationViewStyle(StackNavigationViewStyle()) From a66e9a4c9afff30fb159a605be0f128c89e5e5ae Mon Sep 17 00:00:00 2001 From: Martin Rist Date: Tue, 16 Mar 2021 12:50:23 +0000 Subject: [PATCH 48/69] Add support for 0.75x and 1.25x playback speeds This commit adds new 0.75x and 1.25x playback speeds to the 'Settings' screen, to align the rates supported by the app with those available online. --- Emitron/Emitron/Models/PlaybackSpeed.swift | 10 ++++++++++ .../emitronTests/Settings/SettingsManagerTest.swift | 7 +++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Emitron/Emitron/Models/PlaybackSpeed.swift b/Emitron/Emitron/Models/PlaybackSpeed.swift index affd2193..8e451958 100644 --- a/Emitron/Emitron/Models/PlaybackSpeed.swift +++ b/Emitron/Emitron/Models/PlaybackSpeed.swift @@ -28,7 +28,9 @@ enum PlaybackSpeed: Int, CaseIterable, SettingsSelectable { case half + case threeQuarters case standard + case onePointTwoFive case onePointFive case double @@ -40,8 +42,12 @@ enum PlaybackSpeed: Int, CaseIterable, SettingsSelectable { switch self { case .half: return 0.5 + case .threeQuarters: + return 0.75 case .standard: return 1.0 + case .onePointTwoFive: + return 1.25 case .onePointFive: return 1.5 case .double: @@ -53,8 +59,12 @@ enum PlaybackSpeed: Int, CaseIterable, SettingsSelectable { switch self { case .half: return "0.5x" + case .threeQuarters: + return "0.75x" case .standard: return "1.0x" + case .onePointTwoFive: + return "1.25x" case .onePointFive: return "1.5x" case .double: diff --git a/Emitron/emitronTests/Settings/SettingsManagerTest.swift b/Emitron/emitronTests/Settings/SettingsManagerTest.swift index e19cd16e..01090e5c 100644 --- a/Emitron/emitronTests/Settings/SettingsManagerTest.swift +++ b/Emitron/emitronTests/Settings/SettingsManagerTest.swift @@ -114,10 +114,13 @@ class SettingsManagerTest: XCTestCase { settingsManager.playbackSpeed = .double settingsManager.playbackSpeed = .standard settingsManager.playbackSpeed = .onePointFive + settingsManager.playbackSpeed = .half + settingsManager.playbackSpeed = .threeQuarters + settingsManager.playbackSpeed = .onePointTwoFive - let stream = try wait(for: recorder.next(3), timeout: 5) + let stream = try wait(for: recorder.next(6), timeout: 5) - XCTAssertEqual([.double, .standard, .onePointFive], stream) + XCTAssertEqual([.double, .standard, .onePointFive, .half, .threeQuarters, .onePointTwoFive], stream) } func testClosedCaptionOnSuccessfullyPersisted() { From 6fc80d0cdba9b4d010898505c8bbb9f1830dc623 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 7 Apr 2021 03:58:43 +0300 Subject: [PATCH 49/69] Fixed Lesson list top/bottom padding issue for Download Icon --- .../Emitron/UI/Shared/Content Detail/TextListItemView.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift index 3c15ba33..535a295b 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift @@ -60,6 +60,7 @@ struct TextListItemView: View { .kerning(-0.5) .lineSpacing(3) .foregroundColor(.titleText) + .fixedSize(horizontal: false, vertical: true) Text(content.duration.minuteSecondTimeFromSeconds) .font(.uiFootnote) @@ -69,11 +70,16 @@ struct TextListItemView: View { Spacer() if canDownload { + VStack { + Spacer() DownloadIcon(downloadProgress: dynamicContentViewModel.downloadProgress) .onTapGesture { download() } .alert(item: $deletionConfirmation, content: \.alert) + .padding(.bottom, 5) + Spacer() + } } } progressBar From 24d41d9372d658ec64c54031f9c14235ac68a4f8 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 8 Apr 2021 16:47:20 +0300 Subject: [PATCH 50/69] Added Spacer() so the space from the top of the title to the platform information should match the space from the bottom of the title to the Date. --- .../UI/Shared/Content Detail/ContentSummaryView.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift index 775a677f..b553fca3 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift @@ -37,7 +37,7 @@ struct ContentSummaryView { private let content: ContentListDisplayable @ObservedObject private var dynamicContentViewModel: DynamicContentViewModel @State private var deletionConfirmation: DownloadDeletionConfirmation? - + init( content: ContentListDisplayable, dynamicContentViewModel: DynamicContentViewModel @@ -74,6 +74,8 @@ extension ContentSummaryView: View { .fixedSize(horizontal: false, vertical: true) .foregroundColor(.titleText) + Spacer() + Text(content.contentSummaryMetadataString) .font(.uiCaption) .foregroundColor(.contentText) @@ -121,7 +123,7 @@ private extension ContentSummaryView { var canDownload: Bool { sessionController.user?.canDownload ?? false } - + var completedTag: CompletedTag? { if case .completed = dynamicContentViewModel.viewProgress { return CompletedTag() From 19251376c7eb2d220e03052543505d0f20acd5c5 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Thu, 8 Apr 2021 18:22:30 +0300 Subject: [PATCH 51/69] Fixing Formatting --- .../Shared/Content Detail/TextListItemView.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift index 535a295b..6ec9dc18 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/TextListItemView.swift @@ -66,18 +66,18 @@ struct TextListItemView: View { .font(.uiFootnote) .foregroundColor(.contentText) } - + Spacer() - + if canDownload { VStack { Spacer() - DownloadIcon(downloadProgress: dynamicContentViewModel.downloadProgress) - .onTapGesture { - download() - } - .alert(item: $deletionConfirmation, content: \.alert) - .padding(.bottom, 5) + DownloadIcon(downloadProgress: dynamicContentViewModel.downloadProgress) + .onTapGesture { + download() + } + .alert(item: $deletionConfirmation, content: \.alert) + .padding(.bottom, 5) Spacer() } } From 2a88adf40d9fe41a0571456f9b75156a0e4b6663 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Fri, 9 Apr 2021 06:45:18 +0300 Subject: [PATCH 52/69] Fixed swiftlint warning: valid rule identifier Fixed swiftlint warning: 'unused_private_declaration' is not a valid rule identifier Signed-off-by: Franklin Byaruhanga --- Emitron/.swiftlint.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/Emitron/.swiftlint.yml b/Emitron/.swiftlint.yml index f5df941c..17b90ea6 100644 --- a/Emitron/.swiftlint.yml +++ b/Emitron/.swiftlint.yml @@ -51,7 +51,6 @@ opt_in_rules: - unowned_variable_capture - untyped_error_in_catch - unused_import - - unused_private_declaration - vertical_parameter_alignment_on_call - vertical_whitespace_closing_braces - xct_specific_matcher From a25ff4eb35bde90c4116d0d93e966c35aff830a1 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Fri, 9 Apr 2021 06:46:45 +0300 Subject: [PATCH 53/69] Fixed swiftlint warning: configuration for on 'disabled_rules'. Fixed swiftlint warning: Found a configuration for 'line_length' rule, but it is disabled on 'disabled_rules'. Signed-off-by: Franklin Byaruhanga --- Emitron/.swiftlint.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Emitron/.swiftlint.yml b/Emitron/.swiftlint.yml index 17b90ea6..4d1ed600 100644 --- a/Emitron/.swiftlint.yml +++ b/Emitron/.swiftlint.yml @@ -79,10 +79,6 @@ large_tuple: - 3 # warning - 4 # error -line_length: - - 150 # warning - - 200 # error - file_length: - 1200 # warning - 1500 # error From ef2d9bc53f36a2a363303e421052c970d0b238c7 Mon Sep 17 00:00:00 2001 From: Brian Moakley Date: Fri, 9 Apr 2021 10:36:09 -0400 Subject: [PATCH 54/69] Removes print statements --- Emitron/Emitron/Downloads/DownloadService.swift | 1 - Emitron/Emitron/Networking/Services/Service.swift | 1 - Emitron/Emitron/Sessions/SessionController.swift | 2 -- 3 files changed, 4 deletions(-) diff --git a/Emitron/Emitron/Downloads/DownloadService.swift b/Emitron/Emitron/Downloads/DownloadService.swift index ee5bddaa..8d01c6bc 100644 --- a/Emitron/Emitron/Downloads/DownloadService.swift +++ b/Emitron/Emitron/Downloads/DownloadService.swift @@ -311,7 +311,6 @@ extension DownloadService { reason: "Unable to obtain download URLs: \(error)") .log() case .success(let attachment): - print("remote url: \(attachment.url)") download.remoteURL = attachment.url download.lastValidatedAt = Date() download.state = .readyForDownload diff --git a/Emitron/Emitron/Networking/Services/Service.swift b/Emitron/Emitron/Networking/Services/Service.swift index 0d98604d..3c21003a 100644 --- a/Emitron/Emitron/Networking/Services/Service.swift +++ b/Emitron/Emitron/Networking/Services/Service.swift @@ -85,7 +85,6 @@ class Service { func prepare(request: R, parameters: [Parameter]?) -> URLRequest? { let pathURL = networkClient.environment.baseURL.appendingPathComponent(request.path) - print("pathURL: \(pathURL)") var components = URLComponents(url: pathURL, resolvingAgainstBaseURL: false) diff --git a/Emitron/Emitron/Sessions/SessionController.swift b/Emitron/Emitron/Sessions/SessionController.swift index 6130f0ba..ade8a452 100644 --- a/Emitron/Emitron/Sessions/SessionController.swift +++ b/Emitron/Emitron/Sessions/SessionController.swift @@ -144,8 +144,6 @@ class SessionController: NSObject, UserModelController, ObservablePrePostFactoOb .log() case .success(let user): self.user = user - print(user) - Event .login(from: "SessionController") .log() From cf316b5d77145f2bb69142b5b83f0da7d3f810fe Mon Sep 17 00:00:00 2001 From: Brian Moakley Date: Fri, 9 Apr 2021 21:40:51 -0400 Subject: [PATCH 55/69] Adds the initial code for review prompt --- .../Assets.xcassets/Artwork/Contents.json | 6 +- .../razefaceAnnoyed.imageset/Contents.json | 23 ++++++ .../basic-annoyed-1.png | Bin 0 -> 4839 bytes .../basic-annoyed-1@2x.png | Bin 0 -> 10259 bytes .../basic-annoyed-1@3x.png | Bin 0 -> 16151 bytes .../razefaceHappy.imageset/Contents.json | 23 ++++++ .../razefaceHappy.imageset/basic-happy-5@.png | Bin 0 -> 4543 bytes .../basic-happy-5@2x.png | Bin 0 -> 9424 bytes .../basic-happy-5@3x.png | Bin 0 -> 14385 bytes Emitron/Emitron/UI/App Root/MainView.swift | 77 +++++++++++++++++- 10 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/Contents.json create mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/basic-annoyed-1.png create mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/basic-annoyed-1@2x.png create mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/basic-annoyed-1@3x.png create mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/Contents.json create mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@.png create mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@2x.png create mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@3x.png diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/Contents.json b/Emitron/Emitron/Assets.xcassets/Artwork/Contents.json index da4a164c..73c00596 100644 --- a/Emitron/Emitron/Assets.xcassets/Artwork/Contents.json +++ b/Emitron/Emitron/Assets.xcassets/Artwork/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/Contents.json b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/Contents.json new file mode 100644 index 00000000..fb4a3bdd --- /dev/null +++ b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "basic-annoyed-1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "basic-annoyed-1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "basic-annoyed-1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/basic-annoyed-1.png b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/basic-annoyed-1.png new file mode 100644 index 0000000000000000000000000000000000000000..aaae2d14f25e8f3ebec25c7389ce5fd508a6de96 GIT binary patch literal 4839 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 z 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 JRl){8{}=c6#2x?u literal 0 HcmV?d00001 diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/Contents.json b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/Contents.json new file mode 100644 index 00000000..8d1988c1 --- /dev/null +++ b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "basic-happy-5@.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "basic-happy-5@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "basic-happy-5@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@.png b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@.png new file mode 100644 index 0000000000000000000000000000000000000000..5cc6062df5e79ceca7953a8cbb6f965140517154 GIT binary patch literal 4543 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 literal 0 HcmV?d00001 diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@3x.png b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f56dbe496838925149e98154fa4b18c1bfa394d2 GIT binary patch literal 14385 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 I%;eku0zQn+djJ3c literal 0 HcmV?d00001 diff --git a/Emitron/Emitron/UI/App Root/MainView.swift b/Emitron/Emitron/UI/App Root/MainView.swift index 4220c0c8..a10a9d00 100644 --- a/Emitron/Emitron/UI/App Root/MainView.swift +++ b/Emitron/Emitron/UI/App Root/MainView.swift @@ -34,11 +34,82 @@ struct MainView: View { private let tabViewModel = TabViewModel() var body: some View { - contentView - .background(Color.backgroundColor) - .overlay(MessageBarView(messageBus: MessageBus.current), alignment: .bottom) + ZStack { + contentView + .background(Color.backgroundColor) + .overlay(MessageBarView(messageBus: MessageBus.current), alignment: .bottom) + // swiftlint:disable all + Color.black + .edgesIgnoringSafeArea(.all) + .opacity(0.6) + VStack { + HStack { + Image("razefaceAnnoyed") + ZStack { + Rectangle() + .foregroundColor(.black) + .frame(height: 5) + HStack { + Spacer() + .frame(width: 50) + Circle() + .foregroundColor(.black) + .frame(height: 10) + } + } + Image("razefaceHappy") + } + Text("Congratulations on completing this course!") + .foregroundColor(.black) + .font(.headline) + .multilineTextAlignment(.center) + .padding(.bottom) + + Text("Next, take a moment and rate your experience with the app.") + .foregroundColor(.black) + .fontWeight(.light) + .multilineTextAlignment(.center) + Button { + // Action Goes Here + } label: { + HStack { + Spacer() + Text("Leave a review") + .foregroundColor(.white) + .padding(.top, 5.0) + .padding(.bottom, 7.0) + Spacer() + } + .background(Color(.accent)) + .cornerRadius(10.0) + .shadow(radius: 10, x: 5, y: 5) + } + Button { + // Action Goes Here + } label: { + HStack { + Spacer() + Text("Not now") + .foregroundColor(.black) + .padding(.top, 5.0) + .padding(.bottom, 7.0) + Spacer() + } + .background(Color(.white)) + .cornerRadius(10.0) + .shadow(radius: 10, x: 5, y: 5) + } + } + .padding() + .frame(maxWidth: 250) + .background(Color.white) + .cornerRadius(21.0) + .shadow(radius: 10, x: 5, y: 5) + + } } } +// swiftlint:disable all // MARK: - private private extension MainView { From 63b6e004e9c0f0a3120bb46e17de2ef9d31d2dfe Mon Sep 17 00:00:00 2001 From: Brian Moakley Date: Sat, 17 Apr 2021 12:11:29 -0400 Subject: [PATCH 56/69] Add code and ui for app review request --- Emitron/Emitron.xcodeproj/project.pbxproj | 4 + Emitron/Emitron/Constants.swift | 4 + .../ViewModels/VideoPlaybackViewModel.swift | 9 +- .../PersistenceStore+Downloads.swift | 1 - Emitron/Emitron/UI/App Root/MainView.swift | 75 ++---------- .../UI/App Root/RequestReviewView.swift | 109 ++++++++++++++++++ Emitron/Emitron/UI/SceneDelegate.swift | 5 +- .../Content Detail/ContentDetailView.swift | 11 ++ 8 files changed, 149 insertions(+), 69 deletions(-) create mode 100644 Emitron/Emitron/UI/App Root/RequestReviewView.swift diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index 928b5e22..ab653de4 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -333,6 +333,7 @@ B6FC15C722CB55DB0078CEDB /* JSONAPIErrorSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FC15C622CB55DB0078CEDB /* JSONAPIErrorSource.swift */; }; B6FC15D522CB68E20078CEDB /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FC15D422CB68E20078CEDB /* Service.swift */; }; B6FC15D722CB817C0078CEDB /* BookmarksService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FC15D622CB817C0078CEDB /* BookmarksService.swift */; }; + D341C8DA2629EFB9001E2AFD /* RequestReviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D341C8D92629EFB9001E2AFD /* RequestReviewView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -682,6 +683,7 @@ B6FC15C622CB55DB0078CEDB /* JSONAPIErrorSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONAPIErrorSource.swift; sourceTree = ""; }; B6FC15D422CB68E20078CEDB /* Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = ""; }; B6FC15D622CB817C0078CEDB /* BookmarksService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksService.swift; sourceTree = ""; }; + D341C8D92629EFB9001E2AFD /* RequestReviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestReviewView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1392,6 +1394,7 @@ 22BFE75423D9905500495BA9 /* SnackbarView.swift */, 22BFE75B23D9AF4100495BA9 /* MessageBarView.swift */, 22BE655223F2D0CB00717369 /* PermissionsLoadingView.swift */, + D341C8D92629EFB9001E2AFD /* RequestReviewView.swift */, ); path = "App Root"; sourceTree = ""; @@ -2041,6 +2044,7 @@ B6D8EB2922CBA93D00DE29AF /* VideosService.swift in Sources */, 222EEF7023CAD65200B025A4 /* ChildContentsState+Fetchable.swift in Sources */, B6FC15C722CB55DB0078CEDB /* JSONAPIErrorSource.swift in Sources */, + D341C8DA2629EFB9001E2AFD /* RequestReviewView.swift in Sources */, 223DECF0240926B90017F7CA /* DownloadDeletionConfirmation+Alert.swift in Sources */, B6C0F0DB22D5E65900012839 /* UIViewController+Extensions.swift in Sources */, 2278AE48240A4BBC00855221 /* IconView.swift in Sources */, diff --git a/Emitron/Emitron/Constants.swift b/Emitron/Emitron/Constants.swift index 5dce7dd3..63e0c9cb 100644 --- a/Emitron/Emitron/Constants.swift +++ b/Emitron/Emitron/Constants.swift @@ -112,3 +112,7 @@ extension TimeInterval { // MARK: Video playback static let videoPlaybackOfflinePermissionsCheckPeriod: Self = 7 * 24 * 60 * 60 } + +enum LookupKey { + static let requestReview = "request" +} diff --git a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift index 5dd367b2..006085bb 100644 --- a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift @@ -72,6 +72,10 @@ extension VideoPlaybackViewModel { } } +extension Notification.Name { + static let requestReview = Notification.Name("requestReview") +} + final class VideoPlaybackViewModel { // Allow control of appearance and dismissal of the video view var shouldShow: Bool = false @@ -316,13 +320,16 @@ private extension VideoPlaybackViewModel { // Don't load the next one if we've already got another one ready to play guard player.items().last == currentItem else { return } // Preload the next video 10s from the end + if (currentItem.duration - time).seconds < 10 { enqueueNext() } } func enqueueNext() { - guard nextContentToEnqueueIndex < contentList.endIndex else { return } + guard nextContentToEnqueueIndex < contentList.endIndex else { + return + } enqueue(index: nextContentToEnqueueIndex) } diff --git a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift index 4c7bf65c..3ea6530a 100644 --- a/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift +++ b/Emitron/Emitron/Persistence/PersistenceStore+Downloads.swift @@ -338,7 +338,6 @@ extension PersistenceStore { } } - /// Save the entire graph of models to supprt this ContentDeailsModel /// - Parameter contentPersistableState: The model to persist—from the DataCache. func persistContentGraph(for contentPersistableState: ContentPersistableState, contentLookup: ContentLookup? = nil) -> Future { diff --git a/Emitron/Emitron/UI/App Root/MainView.swift b/Emitron/Emitron/UI/App Root/MainView.swift index a10a9d00..6263e377 100644 --- a/Emitron/Emitron/UI/App Root/MainView.swift +++ b/Emitron/Emitron/UI/App Root/MainView.swift @@ -27,89 +27,32 @@ // THE SOFTWARE. import SwiftUI +import StoreKit struct MainView: View { @EnvironmentObject var sessionController: SessionController @EnvironmentObject var dataManager: DataManager + @State private var requestReview = false private let tabViewModel = TabViewModel() + private let notification = NotificationCenter.default.publisher(for: .requestReview) var body: some View { ZStack { contentView .background(Color.backgroundColor) .overlay(MessageBarView(messageBus: MessageBus.current), alignment: .bottom) - // swiftlint:disable all - Color.black - .edgesIgnoringSafeArea(.all) - .opacity(0.6) - VStack { - HStack { - Image("razefaceAnnoyed") - ZStack { - Rectangle() - .foregroundColor(.black) - .frame(height: 5) - HStack { - Spacer() - .frame(width: 50) - Circle() - .foregroundColor(.black) - .frame(height: 10) - } + .onReceive(notification) { _ in + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + //requestReview.toggle() + SKStoreReviewController.requestReview() } - Image("razefaceHappy") - } - Text("Congratulations on completing this course!") - .foregroundColor(.black) - .font(.headline) - .multilineTextAlignment(.center) - .padding(.bottom) - - Text("Next, take a moment and rate your experience with the app.") - .foregroundColor(.black) - .fontWeight(.light) - .multilineTextAlignment(.center) - Button { - // Action Goes Here - } label: { - HStack { - Spacer() - Text("Leave a review") - .foregroundColor(.white) - .padding(.top, 5.0) - .padding(.bottom, 7.0) - Spacer() - } - .background(Color(.accent)) - .cornerRadius(10.0) - .shadow(radius: 10, x: 5, y: 5) - } - Button { - // Action Goes Here - } label: { - HStack { - Spacer() - Text("Not now") - .foregroundColor(.black) - .padding(.top, 5.0) - .padding(.bottom, 7.0) - Spacer() - } - .background(Color(.white)) - .cornerRadius(10.0) - .shadow(radius: 10, x: 5, y: 5) } + if requestReview { + RequestReviewView() } - .padding() - .frame(maxWidth: 250) - .background(Color.white) - .cornerRadius(21.0) - .shadow(radius: 10, x: 5, y: 5) - } } } -// swiftlint:disable all // MARK: - private private extension MainView { diff --git a/Emitron/Emitron/UI/App Root/RequestReviewView.swift b/Emitron/Emitron/UI/App Root/RequestReviewView.swift new file mode 100644 index 00000000..2c355c90 --- /dev/null +++ b/Emitron/Emitron/UI/App Root/RequestReviewView.swift @@ -0,0 +1,109 @@ +// 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 SwiftUI + +struct RequestReviewView: View { + + var body: some View { + ZStack { + Color.black + .edgesIgnoringSafeArea(.all) + .opacity(0.6) + VStack { + HStack { + Image("razefaceAnnoyed") + ZStack { + Rectangle() + .foregroundColor(.black) + .frame(height: 5) + HStack { + Spacer() + .frame(width: 50) + Circle() + .foregroundColor(.black) + .frame(height: 10) + } + } + Image("razefaceHappy") + } + Text("Congratulations on completing this course!") + .foregroundColor(.black) + .font(.headline) + .multilineTextAlignment(.center) + .padding(.bottom) + + Text("Next, take a moment and rate your experience with the app.") + .foregroundColor(.black) + .fontWeight(.light) + .multilineTextAlignment(.center) + Button { + // Action Goes Here + } label: { + HStack { + Spacer() + Text("Leave a review") + .foregroundColor(.white) + .padding(.top, 5.0) + .padding(.bottom, 7.0) + Spacer() + } + .background(Color(.accent)) + .cornerRadius(10.0) + .shadow(radius: 10, x: 5, y: 5) + } + Button { + NotificationCenter.default.post(name: .requestReview, object: nil) + } label: { + HStack { + Spacer() + Text("Not now") + .foregroundColor(.black) + .padding(.top, 5.0) + .padding(.bottom, 7.0) + Spacer() + } + .background(Color(.white)) + .cornerRadius(10.0) + .shadow(radius: 10, x: 5, y: 5) + } + } + .padding() + .frame(maxWidth: 250) + .background(Color.white) + .cornerRadius(21.0) + .shadow(radius: 10, x: 5, y: 5) + } + } +} + +struct RequestReviewView_Previews: PreviewProvider { + static var previews: some View { + RequestReviewView() + } +} diff --git a/Emitron/Emitron/UI/SceneDelegate.swift b/Emitron/Emitron/UI/SceneDelegate.swift index 9b7f6cf2..f2802a5a 100644 --- a/Emitron/Emitron/UI/SceneDelegate.swift +++ b/Emitron/Emitron/UI/SceneDelegate.swift @@ -70,7 +70,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let mainView = MainView() .environmentObject(sessionController) .environmentObject(dataManager) - + if NSUbiquitousKeyValueStore.default.object(forKey: LookupKey.requestReview) == nil { + NSUbiquitousKeyValueStore.default.set(Date().timeIntervalSince1970, forKey: LookupKey.requestReview) + } + window.rootViewController = PortraitHostingController(rootView: mainView) self.window = window window.rootViewController?.view.backgroundColor = .backgroundColor diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index a70e769f..f15f4268 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -40,12 +40,14 @@ struct ContentDetailView { } private let content: ContentListDisplayable + @State private var checkReviewRequest = false @ObservedObject private var childContentsViewModel: ChildContentsViewModel @ObservedObject private var dynamicContentViewModel: DynamicContentViewModel @EnvironmentObject private var sessionController: SessionController @State private var currentlyDisplayedVideoPlaybackViewModel: VideoPlaybackViewModel? + private let videoCompletedNotification = NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime) } // MARK: - View @@ -87,6 +89,15 @@ private extension ContentDetailView { } .navigationBarTitle(Text(""), displayMode: .inline) .background(Color.backgroundColor) + .onReceive(videoCompletedNotification) { _ in + checkReviewRequest = true + } + .onAppear { + if checkReviewRequest { + print("course progress: \(dynamicContentViewModel.viewProgress)") + NotificationCenter.default.post(name: .requestReview, object: nil) + } + } } var canStreamPro: Bool { user.canStreamPro } From e5c7e495ea042b93d7877f56c8bbeb8f580c5f6f Mon Sep 17 00:00:00 2001 From: Brian Moakley Date: Sat, 17 Apr 2021 12:23:03 -0400 Subject: [PATCH 57/69] Comments the return statement back in --- Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift index a5490dc1..a12c4494 100644 --- a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift @@ -150,7 +150,7 @@ final class VideoPlaybackViewModel { } // If we're online then, that's all good if sessionController.sessionState == .online { - //return + return } // If we've got a download, then we might be ok From 21e813c9d6d668e82de241e6f4f780dc514dba5d Mon Sep 17 00:00:00 2001 From: Brian Moakley Date: Fri, 23 Apr 2021 17:02:02 -0400 Subject: [PATCH 58/69] Adds time logic before review request. --- Emitron/Emitron.xcodeproj/project.pbxproj | 4 - .../xcshareddata/swiftpm/Package.resolved | 2 +- .../razefaceAnnoyed.imageset/Contents.json | 23 ---- .../basic-annoyed-1.png | Bin 4839 -> 0 bytes .../basic-annoyed-1@2x.png | Bin 10259 -> 0 bytes .../basic-annoyed-1@3x.png | Bin 16151 -> 0 bytes .../razefaceHappy.imageset/Contents.json | 23 ---- .../razefaceHappy.imageset/basic-happy-5@.png | Bin 4543 -> 0 bytes .../basic-happy-5@2x.png | Bin 9424 -> 0 bytes .../basic-happy-5@3x.png | Bin 14385 -> 0 bytes Emitron/Emitron/UI/App Root/MainView.swift | 17 ++- .../UI/App Root/RequestReviewView.swift | 109 ------------------ Emitron/Emitron/UI/SceneDelegate.swift | 1 + .../Content Detail/ContentDetailView.swift | 16 ++- 14 files changed, 27 insertions(+), 168 deletions(-) delete mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/Contents.json delete mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/basic-annoyed-1.png delete mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/basic-annoyed-1@2x.png delete mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/basic-annoyed-1@3x.png delete mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/Contents.json delete mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@.png delete mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@2x.png delete mode 100644 Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@3x.png delete mode 100644 Emitron/Emitron/UI/App Root/RequestReviewView.swift diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index ab653de4..928b5e22 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -333,7 +333,6 @@ B6FC15C722CB55DB0078CEDB /* JSONAPIErrorSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FC15C622CB55DB0078CEDB /* JSONAPIErrorSource.swift */; }; B6FC15D522CB68E20078CEDB /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FC15D422CB68E20078CEDB /* Service.swift */; }; B6FC15D722CB817C0078CEDB /* BookmarksService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FC15D622CB817C0078CEDB /* BookmarksService.swift */; }; - D341C8DA2629EFB9001E2AFD /* RequestReviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D341C8D92629EFB9001E2AFD /* RequestReviewView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -683,7 +682,6 @@ B6FC15C622CB55DB0078CEDB /* JSONAPIErrorSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONAPIErrorSource.swift; sourceTree = ""; }; B6FC15D422CB68E20078CEDB /* Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = ""; }; B6FC15D622CB817C0078CEDB /* BookmarksService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksService.swift; sourceTree = ""; }; - D341C8D92629EFB9001E2AFD /* RequestReviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestReviewView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1394,7 +1392,6 @@ 22BFE75423D9905500495BA9 /* SnackbarView.swift */, 22BFE75B23D9AF4100495BA9 /* MessageBarView.swift */, 22BE655223F2D0CB00717369 /* PermissionsLoadingView.swift */, - D341C8D92629EFB9001E2AFD /* RequestReviewView.swift */, ); path = "App Root"; sourceTree = ""; @@ -2044,7 +2041,6 @@ B6D8EB2922CBA93D00DE29AF /* VideosService.swift in Sources */, 222EEF7023CAD65200B025A4 /* ChildContentsState+Fetchable.swift in Sources */, B6FC15C722CB55DB0078CEDB /* JSONAPIErrorSource.swift in Sources */, - D341C8DA2629EFB9001E2AFD /* RequestReviewView.swift in Sources */, 223DECF0240926B90017F7CA /* DownloadDeletionConfirmation+Alert.swift in Sources */, B6C0F0DB22D5E65900012839 /* UIViewController+Extensions.swift in Sources */, 2278AE48240A4BBC00855221 /* IconView.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 c6cf4204..8c232c2f 100644 --- a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -42,7 +42,7 @@ "repositoryURL": "https://github.com/onevcat/Kingfisher", "state": { "branch": null, - "revision": "2a10bf41da75599a9f8e872dbd44fe0155a2e00c", + "revision": "1a0c2df04b31ed7aa318354f3583faea24f006fc", "version": "5.15.8" } }, diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/Contents.json b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/Contents.json deleted file mode 100644 index fb4a3bdd..00000000 --- a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "basic-annoyed-1.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "basic-annoyed-1@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "basic-annoyed-1@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/basic-annoyed-1.png b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceAnnoyed.imageset/basic-annoyed-1.png deleted file mode 100644 index aaae2d14f25e8f3ebec25c7389ce5fd508a6de96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4839 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 z 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 JRl){8{}=c6#2x?u diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/Contents.json b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/Contents.json deleted file mode 100644 index 8d1988c1..00000000 --- a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "basic-happy-5@.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "basic-happy-5@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "basic-happy-5@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@.png b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@.png deleted file mode 100644 index 5cc6062df5e79ceca7953a8cbb6f965140517154..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4543 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 diff --git a/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@3x.png b/Emitron/Emitron/Assets.xcassets/Artwork/razefaceHappy.imageset/basic-happy-5@3x.png deleted file mode 100644 index f56dbe496838925149e98154fa4b18c1bfa394d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14385 zcmb8W1yo#5voAV>1$TFM_u%gC?gV#t5(bCh9z56(T!Om=3GVLh?w9{}&OPUQ=f1nv zdwW(-)vl^vS69oPJuQ(cO43O1`0xM#07+IxLhXa5{*z!~KE4HjE?XZ2%1TsG6ac7+ zLwGTT{_{?Vc;@S#Yx)RFz7 zs0g6{;9&vKKy(1)2M7H40D$;_e{%ca0CGTr|KinvYygN48QMqE3%3D4{ilun2mR-g z`AGj@{zXMCoXjjqWgSedEY&PcEqp%Yka@uW_K^qiU-;BK$p7X)>;Q==At(KpiMyR3xsIX=skoD?B`G&ED>Ey(5IiXBEA_&BxK*)SJoCjp9Et`QQ0S zSh|_J+Bm!0I60F3ldq|nlZU$?Ir%@1{_FaWa$0)Z{I@4ZxBm?5BS4maG%Re)tStX; zu#aH>t9<_tl((t#{}tqJ^X32I>mSX3!2VSk|4~MPe@eioV&iRTuPb5WVCm@g5swfD zE33f2%JzR~{yVyVL+bork^iRopGa{hdnZ?QXH#=aA-4Z?_HUwpv;L?CK5=N)Og&Se}&SYa7%R`q2MZ=0rA6_U_x$5-2%YR&xJ%x?`s5HT~|o z*e3&XrI;GnA@~Pip54R^umRtj4)uZ+y;WsmFV2gh7&}rF_I0Q$8GH2)em8C@ZQX5o znw>RZUOSd~H+ZKssk9liiM2@~rqdTdtd7DsGr;rHoCmCa-NgqXDv#fMZ{d*;P!P~) z%V^4NN^8o(72(41!;G)d7WMQ5R1v9QC?brsnK^?h{(TR7>Nt7h#PwsZB_XjaOG`WP#TF0KcJI{|cD6xSbx=)UI}H^y=hiKKiVWEquy(LN zE~1P(d%_IayK}zudn}&LVepc4qOh!gQBF1l~L7Gy>8vNi5l~giCRqt$tlWTx^>ja$(Z4@@xo~KKpS{2Kn ztu0N{L-doQDz$y)Rk1kYcCiwrY_a*uRq;Wk?7}A%)8q;!1bJ*0MEO)^WcgG^WVgLh z_;Toxp-M#j@{+$$5cbAnNn(-po$3M>Tm}t$_r$}(&Q|@x z99LVyk*9jHYSRkkTG*jQBdKBi(b!>+QQOnX)M1y~ z#t3y*W3)KN=WDNGWow2V{x-CRA@koT37V-X;5#rf={s~^cw7<|g09c!D^{pRf>#uc z=q>-C`^BHViM9E?lq2wQgFuX5^WF9<`LJmV_kThx28L(%9!##o_BO8YA>#7NER{-I zFE=_^uN8!zloo>Te1pF3 zxv^320l|@kmE`(!j%G*mkduo@rN9kUPogJR_IFG;%}1O^=89tbbC`_k4ttnTmA8{6 zZh34Kn%j^Yu=IPD7uBodXqwAw^qX#r_?ZJ2-LLz|GOPGs^ za9H&6z_kVrX~@;{H$9eH+Nb@5Rm3CmY)UTQ2@~~>%!&@-*>aqrlx!4jO6Mw;!<4f5 z!<%cpelWK^M6ue=7@1ydjEO%Z6DiciyI5n2Yy@8xcBWMn0+Ad_0KiP`vU=Jy7JY1L z*Wt7>(Vqs69g$28InSaG4ljsp7aSTXuXkPv_yG$8Gn$PZ8u*)}&0AMZ>AgFFAa%XJe9y5Ysv{ zZCwryaz)aS1!gle8?ZQ1!Kl1^IlP?`WNFmB!wV4MJ$p#%z2iUmnQDMDf{JK2KGZTL zC#M`cBpOEDJJxN*lq`6+msJ(2|Mv3C>M@K1uI1OmYPKd5l-#A&ZE~DpNsF?9Q`s1v zEoyOO9i}pAHsO=O55giLkf)-Pfkd7iHsg8>cWQ9py&p^i8G5d+`pX7K9wC$N;-}M( zn2Jb^8L8yKNYoXZZuUTL?y|9fMuI$Z+NWBTJFCqeYpd0m#-UUN#jNkW`qy!Sm_6~1 z>P_jigC%Q#uMGm$!-ny2Q!J4E8$0#8sR4bxNQIu;ZeC* z_gJl6hz~@X?VI`qk8g&HDxIC1ET-q}_i2M|DSAQW6~l#kHjH|5>-Si$Owq?*#iokn z=c~7s3TMEp+HchvSFwt)0Z(@*c0W@_xa)}%h#EBs*zj|TFYL^QfY^q!*Xv~sTXn9Le)W3Imb@}k6GIBaN z7&X}@x8oHCESHXrEO1cm{HDx|ARdNXmx?L&NhJ#+zq^$9kTB>zo2U1)FdrSGVT;U? zUGV5PJXthS`JM1uZ;=yjTXgRhL*W@zdCw zNlC4|yieQ^^Hb$gOIQ5+rea8ZjG=?O1BfJJ?!;(~YK%Lbs|bHA#x=d@>KzY1Z6u%z zY1h#d)gU@}6p)25;+oRs^e=blL8kCRMp+B0vhjG5J4}oD)7{We`5c*xNpb88)D8e5 zsBEh7iuqS9{1K{U$WtT1LmuZp_f?U4!iE9M$j?R-3JAt-!8Tc=slPgOJWf=XKS_`@ zy`N7Vjg9In&*EY{Mz46Gy9r4t7^##VCm&JFKag_HHg36WHJZ(9l}*CxBQDdg+|37t zy}f=~a!&N0rJ>UdVa3qDzKtU70oy3Mvo>B0ON!-K9fg1H%h#E0mCAXhmkJsNPFH*^T zR5Cc(-$Oj5#ZUQc$1mnPSFOH4i#wCEuZjw(=S^xfk5|STyz?|&Hrmf@P^bx? zR{KKwQKFlrxiUP!3?|K)tEzOxWh0+d+7wq(Cg4KL~~14g%cxI68!=SvH;zo#-+vlg~ zCJ8jB{XvcV9v3vrU9OtZzwfPFe33N#S=GUL2rO^S9*U&F0;OhjmX{_2ZBLq)pVpqz zf~;d+6DP{G*DVk=f`X4tE>ZetSXV~yn?3(%1t-J64+(DVmFaq!Sb@gf0Ixqv~|dSz}O&?UjAii%zn)WfXzG z>ZB;9^uFd+fi}}7daEex5_Yvzlg>UW#q$s}R8ef%r;xLlJ6sNDO z+F5tZGJ!AcF7q!qi+ ztq4aT+vPXAa*`wBA5&U_KP%vO4Lk36f@)=dmhQHB8KQ%&T*Y&++;w80K8VNGcU$d` zU!F;W2V%R5hT8lZLK&<6t6J8KqS4W{ir>U&cMIkucKJJ}U>z|e=RCf?`1iR4Y5f!2 z%g7gJ=xf1OUI_!c3Qc_zpKr*TX3k+#^!2Q*?nUx)Fwckkwwh^MG{; zjgQ!yzv_a_II46LXsGA)(m81Q?`=b4_1b?yH6?i2{=jV=Xf%6z&g&MpPj&6U!j&)i zigM%1nO4kqA#xM^w8EK2Dq2bTeZF1E%$6LWO#w!#_QpYKNO|bu^+bp>9hmr2@}sDK z)ECs?gW|`S6DF9~pfj9UbG6QYC!x=eNMf!K96|3K%qwQJf`#**GnwDmMP7Rz#rA}_ zIhT!ErP$9dRy;Gq{zu(W2B447xh1+Tu!26LC2yrA@{Y4E?lKc$3&|KP5q(k0`vsvq zL@JbdS|LkanO}RAR}Y`9s=_vz&Epmw=6PwV!L)pHc?pCy7h+u*rp1opA*9z9%4^AS zEI8ObIT_al_ zK+$0{O>MgKebh>lA(^0M$5Xz!ZH!YDIbLBMa<(mtbt$W~uDLKYr0So&r z-G5cC3oNeaZ+XCOzO8dpVL4JNtj)$x?K%5M%GQt&qF6FZf%4N=Sz7s#zt>$B2k#+XVo{L zI3X%ottx+|Lr%2q%=;vmxXL53B#}g2b3Ye;P8iTOr*odzz)T1wf`PZU!h6b>)W=h7 zRd^oTDR}uR>oarXXJB_b4Q2Y|?qXd?Xy$65LgYG~9R9A8UHC~Q+sbEPmNJ12@{=0w zamcfHBLUkKxUU{7sF0@oalX56aGPl0yhlI+yEm`1JT#1#<2mh3X26wy1B~bYS9mWJ z)(ztNo0Fx^?|@c|jjIj(tw#kqh&es|*4vE`l)8C}U6Z}!*GWYK3Pa0Gjf9% zz!em~I|L*q_GtObjwjdytKJA&v^^fua4aafX2p0ZfI&q z{{s3vp%e4XMO%estV>egGP4;LD?cwo2mfl-YaO2D?fi3=w+&GH$|_Fq9x0sARbs&* zt{u7_SY{c=9^09?aeT@R4K8{KWK_TT^Am2fVSd2+Q}kf(&qxxjOc?mNmw%=bU~FN#CtNao+;z9s2 z^_#`DzQf<0tyF|REvd@rX}dF$jZg|Z;`~vdN{f|q@rivma?4ifo7NSxq#HC+#>Ge& zZAYBV?a3?}y=y#cBD7OKZw;CE(3NBpHO>f$`nTE%_mswX!>F4VYgr4KbY_bY%tD=5 zNUcRah}&JLvR&hf*fkulC~#>}hFUl9_G9F?rOeQEEqN{zDQsU%&d%Z*so^lESM^`N zqrL^KuVORX+1HxDZdOh;+RKXid;t*Jqu!0vsmiwBY}Ikyc>436f6HC+bpC~vGO}^y zRFD@Fu-or^^e2Jl$KptE#H-hWYK-lg<=EtUVX;Xs)ZHiFt;Z*GJjjvt6@z?}QBy1Dakjka;WA%7x8CP9K3vRoTe+`p?^xSlv_z|y zuz7kRHGL3UBOX$m$5-)!$zB;dJ0dV6#qFSF?BbyHZ>w)ldkv0 zSHIrzAkVK%TVdG09QEjxOjp;6E-s0bZLC;D`eh*Rcz=e5*>rm{?U-Bil>-Z%oaofM z!&?_R1nB)atd#1|@&$cwkgc*^{Jr0rX%x#hRjZ;RNbt-G9V#-WQIQJL33XmQRu`SL z?$hC_1<1|LaHRA6RA>9R?2-z}z)2YbA;H*X5^pz&ZVe1`%lW@!Ch<6mGesV)!J=n1 zfBETi!Ert=3uqPfZ0aPh1L!iSMZQlrbeWv2tTA}kZQ$wS8-JIEY4fi$+ay!+rap)L z`@NFFpVj%3kk~7taR?!t8KwIq)a}b=%cD=1CpQ@37Tf)*x+JjR+53ufMl4R$o5tTS z>GW8RJ<5%MN}3jjD&{s_!J>D#p==e6rosN}yoiG${<@E3)8P2{gPIr^gF}#n%DcGx zb{}oxan;)Kh*1cmD+1D7o~S=FtZRPEGaN|}s@6bf zjRgwRKneo_7lvhvO=URoi}ZrczUrooM}2M3hSV`sbWz}2da>3>oFeeA&@q&Lp66TJ zXBb>~>O4_%s{6@BQ9mYj`m`Q)ho!VKB0d>5^_GPljMK5oD*t0y8h@G?yyuRJNaI;p z40v8mdO2W0^hUNG=8<0oU7!2V{M!i(+IWq68mb)pqt}%CTR+K`PTk? z!-W$~aB5uvO>e;X;Bue+8*c)D6J3!%IeCQ$<9(O$4#L>Z_>8Se6||s{ouPqhBaeFY zouE>NJF$*n?N{Ug#8TU*%#?Ba8{CS{zd0k94nz!O?UGxQ%=fEaae@*aGOORo;7h;1 z+|_J)pYCyKP*6@71Y_Y0_?}`a|9tS#L2};M7^~;JHQLmdCM0rv+IF({<2FF9v&_9j zycsw)@AZoNy!O{^Gj5sl{7?4P4hD*@G%I%^4*Zn&Ef)`1BtZO5OL>~wzg5#l<5MC} zYfr5MF@|Qk?svu8V+Iy3l|~$1AXZn~S!V9?9>T8#fcf_J8hpCc?}KJZo?str2Eb0m5|TQpBcvKwwK-bT8r9KZ{{`uAnGQf+Ljm z-WjJ)Y2AtHA89ykwxnVHf;))XYT@`nkocAfQmTR(o^{|6g%~fC5(j_ue3{6uYKcgj z5U-S9;O^bdz`I2nY=vcRhp{1v_V?G^I9dGw0m<{p5xLBcusz;=rQmaK` z&uZS|6NCz2S3Ih}NEHL*r4i&567+;o`Mt0O^OA3KW90KX!nhp4?*{rD)gLjU?_;+- z;rZg>1l9(<$?t*X z*C^WZ2xwB4-%1boy|I~8CfZ1U_{=@lGW<hZGrx%NgUka*$o#Ony<)1ci92*?9R#Ki;S8 z3yvGI`0swbA=qVXxFMOY1d=(gX)07vC6tPO6)qdK2TeYcV7GW4<{6?BhWHks_mG}4 z$4)h?&tFWBRMoy_=_&)pT|GiG5i`9?sjFuj*K=&^b-mx)e>YTZo>y>c<6QhBrAU7CPa^*Np>hZ4d}QT7UJVDvE5kLqD&tyb4t{=*PE5CyHdyis>2#3TTwg$KCi z$(1v1b3`Ve|I}lkr?0Jc3G{8yQ{@#r zGFEWHc48PJQkBp8CRKA&)4mI4iNz)*M`hi33%u!95+)tWv6si9he1Ek#GhO4(=aP|RJa$Jq~`{dP&)G{H!}mT zP^FnL2~H1XsmD1A6oPkw(<8EW-4PN#DEyw-b%I)OIz_II4pW*52(*K7U*i5ye>uU? z0DlEW`n%0;bh+-1mBE-OQ^)lQg|uLID4iG9 zSno%+0iRHT%YMRn;xhqNkV_#7HF4KK6yFwO!4TWvxq^3|hp1qC0V;~`ZpSYP@5gDF zL;(EPwjMIMTyjh%U%RMp!Uu4AAh$j|RqC#*2;4c`?&aWyH5H{_(^|+y zCFtIMIQ2JUDo*9_;ZZy~0%YKqMv1|pd-YbgNKMndfzoCEjUDc{^z>G1Yt!6WIpVgRE)?QAxeShu(oy+TT@#3%qqt_O%VoI|?2_FgDUS6t=oih{Yr%*knWo%M`kq#T28G6PU zCL><1Wy?<_L<*4xX{YugmklvOkySe6%+Csl@l#|4p{CJ8@N5(XZud1Ru0AqseZSDK1XXRxQVXSQM93C^-UOr*jmxcivF z3|p|D&;h_jso?CoxtHJvam?x<{|a>hJs;o#EW*>9A*h}QL%wJTH(4~sIuDqP%JD6Z}G~yIThUFSwOztlpcj8jI z7EP@O9W3;JX>DbNS&ne^)P?Lpa#vSiX?}&~hTMaT<+1X`s|&8FdSJ?e3dU=OSNw62 zP~1&oZ!+?QumLW50B&8-W@-={u{Q^UT?7}CtGG}Z!%Cs@jNNC_ZR7TV3L!+|(RqQT z^FhOw(H9X(lX|Sd+s3ru=`Ur=iEy+CzqJgK`9N7E2E>?|#nT(^FaMy`KGnLNL~H}G zP*)DtDm1lFQY?DRxJJ~=t!9o7zc8sLSOocF&4?4RXd@L)(amy%_qlA_M2{E}Fd zDXL~QJ3i2f`?a7Zy$R@Xu)sz)6Z=V}f+jOvP!o52hPW8Z#I;CLhB&`Mr)WR_)0?Sr zZ_5kZl|pY=4t?51q@`{T-x zvpZK1pZH3@s<~)*q65ok2-Ag|5BBeHTa!#7Eu&jJj&1B@>JP?n+2P&=)&9Hl!p zk?kdHUEZ{@T1=LN&{sfG)qW?{bU0xAX=<3B3yxe^_JCnaIKoZ?>8L%qqR*#zXLeKF zH^CH(ZdRkfbl9-v4&K~2kXqsKN*(y|z_BNYII7Acy>i&(b&&CkroX&!A|eV5>NU2H zr7Pa5vnNMLZK^nsmiJ#Gd5=w{vMZ>?N4C?i(Hq@X{yv%9R56N|g^*V;k(aVRn&NOf z0eMGdT`5QFOFBF)M_l&g>A;CicHP03Zr}cE)&1tLT$Z;SLrq?u%@?&Kx zCDjSnUx8HtV}%YW2st6mvFjy>5lyO=cMX?%K(G=3t8q@6 zV1OiqcB`kJtzKh#b!<|QX3KV5eXdSLpa)n@t5>FmvD5oBnU(rs2O$Y+1u{Vnw%etN=m!#=az~Sh|=$f z3tv33gJ=ilhT%+33qfs+QUS$)1D{)mr2#LJlr!r|1VM99SWkS3+{t`N z^rAt!`E6OV0Fwn2ArO&AmJ?J|c*LNdkQ_j&l)c#jFwVGvQ}#*Z{tw zZHAy#Mp#r8?7Xp;i}(8~4`;btx!f|msDKT~Oaw2F@cZ}iW^Hr!F}3aAg*XY^2RYPh zl5L^s61p7LNb2WY^e$knl*NGYSFYRE3QLh%xxO{C0=qv)QxitBW)>6k!cFA*^jC5m zT%8J#xHT0z`rq97>xjj6a-+GK04i^l9j)DeI8*fUi|}>X-R26ajn-N3QmlHn<5ujJ zz@-y3tI6{8!y5{hh-^MMDdgERpjsf~)-7ms=26xR9gd)s6?1%W|Fg}SKmb@4Et{k zcqx?XtFGd#4)%K|RP5;^3MqpP%2$)V!d@4Yakn`Uwh{+<10(mqy4shNM(ngp?y@2m#5BX0Hb(ku-S zF3KqsSJ5w@9M*9-ND<ivnZ4`iiIk5!Li+=X}mMX^V_OmFxzK5$q^GBW6 zx{LV3#j6XqP~|szR6v6Ad6Y7CA?n62RYL#*X~MyuE#fW1{+V4E<`b&9kUop@P@Rg(lDsGN>OE50adaY%>Ur%vqJ2?}U4Sn(4SVpS`Obb3=ZPhQcc)p;m zNx$Agl=TX+W$EnEYmZJsASv-)gt%WZE@8ZrZea@mJJRhYZnjxFvhrj3F@`gn-zu}h zwe}Uc5Gqw`N+N}20jA0Mry9HJDX#_^^|v+5Ap|s6omNl7^iC5Wo%$vjO_JAi)lQm$ zr*xcL_@L%pdTXjvf4fv|SDMJ}&9}K^aJ? zeT>1S&<9HR!e_e|S$52v^M4CM-A;bj3(pV&A|Zkj)7wgu3Ut%D$h-af6fWT}yUkdb z_)?U`1_Y>%+r5lt=eZ`aYx(;&zy@xYMsj{-vk~&&juPIac6O+;_B)Vw@FTef_uy0z zxwdj3kyP?e*8}!%op-UUY)U<}xx7TUn=n7Sac!)6a!M= zUK|8=phM<}-!BfCbeaShH5#O`(r48Cu~rq&%iSKMa4{ zi{YgFvNgWK&(o%K2Noj7A$ka^2#EY(-+R<5j=eo%a*p+reJf|TsXQ(Ey5QXE8nQMW zsSk0(`aOrM88>^_*&47H@I6QOO^KOUFxHyfqE_y>X-iC}sYyl8r=rPY|ig`U`sF z(Q#OHs~Qjn__d{nYP>4KFD5^z&HZg!4Xe*d0}SHRbXrSy;vnrCf_IZFMl=+;Q>bYt zU?)m-J2oxHSCG7p&KU7o3CBse2%6WeoZ#vI~sq}FtDvoN|e#?TP$AB zEzb*uebE`oN!ZX?L6^gim)?uDZ567d1ANT#8AX0D(%JB zkbMgKU>+5^N*(+kvn*8Iw?7KbXp1|<3yVsd5xpDhiXd;hLOKhP$dzZ;CLxpQri|FH z7U>w(UE>ortvR~H_tYfKE`CqUxi_&`12$;3JFEhsTCUjiP@l{J=+2W;8tsU+`V@s( z1ATfrhr7C4JXym^^e`A~sKlV4sMk-%leD42>+VIX$CUe5wU)cZ4vVi3aUr%bl_*#o zQy~2Ozo+#52?b5|MU}_becfDQ3GBy>zZXK)n2ChiGP2SPT?(nKPRi)?9FkpB5lZ96#n z(5@Ba^21Xmg4CzgXnZyA{FlyQt1F724TJW44W33?rJ%hl4Bg8n9Zp#L-Dj0&x%&pS zzsZcW1c18WqfKRv-9OGe{NUz>7~!XjQ|EC*o={zmju|z8;0_;MBZQ``Ya%sNAb7oh zoC~)!LKr7lk}vV-$=A?$R8dvUdXnhXVY)G7k$5CkYWsJP`x%z$zTsVJc|lrWQBosk zrVkTWVmWCvpa^B26({EUV{K-PcQE$iuYT3SZ+7iq)72DIdd$bXTec=8%x%!e=18rS zsM&E3`DO^l4tCw1@3XfRX7%aIaK^F>^6Kmz(R8m9hgPo|{7dIHN#=M@G5a}xN{4Xu zX*7)&r*Gc}8!(b2Z9>%)*+ZVDWl6&Ja|0;bh#;f>KJK#rr6Wy=e}0dLW9r1{Q}3^Q z^;oLDNg1_W*hzM;7Q7d{jMT|>?||s?!F?feyRNx6u!#%{)q{ywC&cv z;crz0$satGgq)TX`~99OCIP>)gwZ>(-y;N$`Y-VYe@}{6LPUco7+vAk)cgx^dx^pI zz7iA@BkO8H)&0@?lFG;D7Zp-rhF%0jFizZzH-{V|;-37=VD|bhQay{CUdfIMBxb1R z$wbs_su6ORB3x8i4hTQ3Ia6;|Lk&h7iTjywl9)7>BO4Tn(7fp4-&hN%IQ2GI$&3oT zd^Ms~Slf1FmoH~U;w8sA=)F5X zJeBo`{x=NdfzC@~(HvhqauT~4RFB>@h~vm3G_~J=HkQ+Fk5j+9+CK$1RA~e`z>M3Z z?Ct~&mq+X{*w=8}m?@Askj1J&*X&7YFd%7~#{r+O0Evy&J@9Q8IQ94bb9+7p_&t1^z{r|AN&zL0EG`AU zlMJIFp-HyxH=$2`>x0Cl>{dmeQf0q)N8Ia-7ereygg-;g*-!9VIz&e&9B4Ymf&2GY zgTBckd|eUv+r&Kmvxg#5s%MuH-{YJ!5L0>jDU{G%dGU&Yc55#SU*jLP$6ce{I;oMI`G8-j zFCp%5`px@;N8nh|q}Wg2^b7g5sOzQf$!y^6MsMi{gNmcA{VxA*C2UpnhxFBR*2(s@ z>LA8De_?0XbGR(o;xeO|k|l1MyLpa%d+>nPVmBwWurO-j&V$53;+7Dg-Rt0+B0ZoV zfSZ$AQCks4N6kbO;JlA>4>aTIu_D{7G{D|a=_> zKJyu@JfB^=9bHL`#!xE1`-2g>Y^5@S82|sA+sRXv-*^YcO$!Xm!162obL>r4Qc0px I%;eku0zQn+djJ3c diff --git a/Emitron/Emitron/UI/App Root/MainView.swift b/Emitron/Emitron/UI/App Root/MainView.swift index 6263e377..cf69a5d2 100644 --- a/Emitron/Emitron/UI/App Root/MainView.swift +++ b/Emitron/Emitron/UI/App Root/MainView.swift @@ -32,10 +32,9 @@ import StoreKit struct MainView: View { @EnvironmentObject var sessionController: SessionController @EnvironmentObject var dataManager: DataManager - @State private var requestReview = false private let tabViewModel = TabViewModel() private let notification = NotificationCenter.default.publisher(for: .requestReview) - + var body: some View { ZStack { contentView @@ -43,13 +42,19 @@ struct MainView: View { .overlay(MessageBarView(messageBus: MessageBus.current), alignment: .bottom) .onReceive(notification) { _ in DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { - //requestReview.toggle() - SKStoreReviewController.requestReview() + makeReviewRequest() } } - if requestReview { - RequestReviewView() + } + } + + private func makeReviewRequest() { + if #available(iOS 14.0, *) { + if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene { + SKStoreReviewController.requestReview(in: scene) } + } else { + SKStoreReviewController.requestReview() } } } diff --git a/Emitron/Emitron/UI/App Root/RequestReviewView.swift b/Emitron/Emitron/UI/App Root/RequestReviewView.swift deleted file mode 100644 index 2c355c90..00000000 --- a/Emitron/Emitron/UI/App Root/RequestReviewView.swift +++ /dev/null @@ -1,109 +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 SwiftUI - -struct RequestReviewView: View { - - var body: some View { - ZStack { - Color.black - .edgesIgnoringSafeArea(.all) - .opacity(0.6) - VStack { - HStack { - Image("razefaceAnnoyed") - ZStack { - Rectangle() - .foregroundColor(.black) - .frame(height: 5) - HStack { - Spacer() - .frame(width: 50) - Circle() - .foregroundColor(.black) - .frame(height: 10) - } - } - Image("razefaceHappy") - } - Text("Congratulations on completing this course!") - .foregroundColor(.black) - .font(.headline) - .multilineTextAlignment(.center) - .padding(.bottom) - - Text("Next, take a moment and rate your experience with the app.") - .foregroundColor(.black) - .fontWeight(.light) - .multilineTextAlignment(.center) - Button { - // Action Goes Here - } label: { - HStack { - Spacer() - Text("Leave a review") - .foregroundColor(.white) - .padding(.top, 5.0) - .padding(.bottom, 7.0) - Spacer() - } - .background(Color(.accent)) - .cornerRadius(10.0) - .shadow(radius: 10, x: 5, y: 5) - } - Button { - NotificationCenter.default.post(name: .requestReview, object: nil) - } label: { - HStack { - Spacer() - Text("Not now") - .foregroundColor(.black) - .padding(.top, 5.0) - .padding(.bottom, 7.0) - Spacer() - } - .background(Color(.white)) - .cornerRadius(10.0) - .shadow(radius: 10, x: 5, y: 5) - } - } - .padding() - .frame(maxWidth: 250) - .background(Color.white) - .cornerRadius(21.0) - .shadow(radius: 10, x: 5, y: 5) - } - } -} - -struct RequestReviewView_Previews: PreviewProvider { - static var previews: some View { - RequestReviewView() - } -} diff --git a/Emitron/Emitron/UI/SceneDelegate.swift b/Emitron/Emitron/UI/SceneDelegate.swift index f2802a5a..4dee968c 100644 --- a/Emitron/Emitron/UI/SceneDelegate.swift +++ b/Emitron/Emitron/UI/SceneDelegate.swift @@ -70,6 +70,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let mainView = MainView() .environmentObject(sessionController) .environmentObject(dataManager) + let date = Calendar.current.date(byAdding: .day, value: -20, to: Date()) if NSUbiquitousKeyValueStore.default.object(forKey: LookupKey.requestReview) == nil { NSUbiquitousKeyValueStore.default.set(Date().timeIntervalSince1970, forKey: LookupKey.requestReview) } diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift index f15f4268..863cb3c8 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentDetailView.swift @@ -94,8 +94,15 @@ private extension ContentDetailView { } .onAppear { if checkReviewRequest { - print("course progress: \(dynamicContentViewModel.viewProgress)") - NotificationCenter.default.post(name: .requestReview, object: nil) + 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) + } + } } } } @@ -131,6 +138,11 @@ private extension ContentDetailView { } } + private func isPastTwoWeeks(_ currentWeek: Date, from lastWeek: Date) -> Bool { + let components = Calendar.current.dateComponents([.weekOfYear], from: lastWeek, to: currentWeek) + return components.weekOfYear ?? 0 >= 2 + } + func headerImagePlayableContent(for width: CGFloat) -> some View { VStack(spacing: 0, content: { ZStack(alignment: .center) { From 2512ad8f1888570dca750983f04ab39b387bd95a Mon Sep 17 00:00:00 2001 From: Brian Moakley Date: Fri, 23 Apr 2021 17:12:13 -0400 Subject: [PATCH 59/69] Adjusted spacing to match repo file --- Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift index 006085bb..b285a175 100644 --- a/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift +++ b/Emitron/Emitron/Data/ViewModels/VideoPlaybackViewModel.swift @@ -327,10 +327,7 @@ private extension VideoPlaybackViewModel { } func enqueueNext() { - guard nextContentToEnqueueIndex < contentList.endIndex else { - return - } - + guard nextContentToEnqueueIndex < contentList.endIndex else { return } enqueue(index: nextContentToEnqueueIndex) } From 29498655679bd0c14fb6ff93f98e419edefeb6a5 Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Tue, 27 Apr 2021 19:54:15 +0100 Subject: [PATCH 60/69] #563: Upgrading fastlane --- Emitron/.ruby-version | 1 + Emitron/Gemfile.lock | 100 +++++++++++++++++++++++++++--------------- 2 files changed, 65 insertions(+), 36 deletions(-) create mode 100644 Emitron/.ruby-version diff --git a/Emitron/.ruby-version b/Emitron/.ruby-version new file mode 100644 index 00000000..2c9b4ef4 --- /dev/null +++ b/Emitron/.ruby-version @@ -0,0 +1 @@ +2.7.3 diff --git a/Emitron/Gemfile.lock b/Emitron/Gemfile.lock index 7311e82b..6383b267 100644 --- a/Emitron/Gemfile.lock +++ b/Emitron/Gemfile.lock @@ -1,52 +1,60 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.2) + CFPropertyList (3.0.3) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) + artifactory (3.0.15) atomos (0.1.3) - aws-eventstream (1.1.0) - aws-partitions (1.380.0) - aws-sdk-core (3.109.0) + aws-eventstream (1.1.1) + aws-partitions (1.447.0) + aws-sdk-core (3.114.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.239.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-kms (1.39.0) - aws-sdk-core (~> 3, >= 3.109.0) + aws-sdk-kms (1.43.0) + aws-sdk-core (~> 3, >= 3.112.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.83.0) - aws-sdk-core (~> 3, >= 3.109.0) + aws-sdk-s3 (1.93.1) + aws-sdk-core (~> 3, >= 3.112.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.2) + aws-sigv4 (1.2.3) aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.3) + babosa (1.0.4) claide (1.0.3) colored (1.2) colored2 (3.1.2) commander-fastlane (4.4.6) highline (~> 1.7.2) declarative (0.0.20) - declarative-option (0.1.0) - digest-crc (0.6.1) - rake (~> 13.0) + digest-crc (0.6.3) + rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) dotenv (2.7.6) - emoji_regex (3.0.0) - excon (0.76.0) - faraday (1.0.1) + emoji_regex (3.2.2) + excon (0.80.1) + faraday (1.4.1) + faraday-excon (~> 1.1) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.1) multipart-post (>= 1.2, < 3) + ruby2_keywords (>= 0.0.4) faraday-cookie_jar (0.0.7) faraday (>= 0.8.0) http-cookie (~> 1.0.0) + faraday-excon (1.1.0) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.1.0) faraday_middleware (1.0.0) faraday (~> 1.0) - fastimage (2.2.0) - fastlane (2.162.0) + fastimage (2.2.3) + fastlane (2.181.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.3, < 3.0.0) + artifactory (~> 3.0) aws-sdk-s3 (~> 1.0) babosa (>= 1.0.3, < 2.0.0) bundler (>= 1.12.0, < 3.0.0) @@ -67,6 +75,7 @@ GEM jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) multipart-post (~> 2.0.0) + naturally (~> 2.2) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) security (= 0.1.3) @@ -89,20 +98,35 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.0) signet (~> 0.12) - google-cloud-core (1.5.0) + google-apis-core (0.3.0) + addressable (~> 2.5, >= 2.5.1) + googleauth (~> 0.14) + httpclient (>= 2.8.1, < 3.0) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + rexml + signet (~> 0.14) + webrick + google-apis-iamcredentials_v1 (0.3.0) + google-apis-core (~> 0.1) + google-apis-storage_v1 (0.3.0) + google-apis-core (~> 0.1) + google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-env (1.3.3) + google-cloud-env (1.5.0) faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.0.1) - google-cloud-storage (1.29.0) + google-cloud-errors (1.1.0) + google-cloud-storage (1.31.0) addressable (~> 2.5) digest-crc (~> 0.4) - google-api-client (~> 0.33) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.1) google-cloud-core (~> 1.2) googleauth (~> 0.9) mini_mime (~> 1.0) - googleauth (0.13.1) + googleauth (0.16.1) faraday (>= 0.17.3, < 2.0) jwt (>= 1.4, < 3.0) memoist (~> 0.16) @@ -114,28 +138,30 @@ GEM domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.4.0) - json (2.3.1) - jwt (2.2.2) + json (2.5.1) + jwt (2.2.3) memoist (0.16.2) - mini_magick (4.10.1) - mini_mime (1.0.2) + mini_magick (4.11.0) + mini_mime (1.1.0) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) - naturally (2.2.0) + naturally (2.2.1) os (1.1.1) - plist (3.5.0) + plist (3.6.0) public_suffix (4.0.6) - rake (13.0.1) - representable (3.0.4) + rake (13.0.3) + representable (3.1.1) declarative (< 0.1.0) - declarative-option (< 0.2.0) + trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) + rexml (3.2.5) rouge (2.0.7) + ruby2_keywords (0.0.4) rubyzip (2.3.0) security (0.1.3) - signet (0.14.0) + signet (0.15.0) addressable (~> 2.3) faraday (>= 0.17.3, < 2.0) jwt (>= 1.5, < 3.0) @@ -147,6 +173,7 @@ GEM terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) + trailblazer-option (0.1.1) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) @@ -156,8 +183,9 @@ GEM unf_ext unf_ext (0.0.7.7) unicode-display_width (1.7.0) + webrick (1.7.0) word_wrap (1.0.0) - xcodeproj (1.18.0) + xcodeproj (1.19.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -165,7 +193,7 @@ GEM nanaimo (~> 0.3.0) xcpretty (0.3.0) rouge (~> 2.0.7) - xcpretty-travis-formatter (1.0.0) + xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) PLATFORMS From 2ef2f544b04da4531e0b2c388899dfcab3c97213 Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Tue, 27 Apr 2021 20:20:42 +0100 Subject: [PATCH 61/69] #563: Attempting to use the new API key to authenticate --- Emitron/fastlane/Fastfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Emitron/fastlane/Fastfile b/Emitron/fastlane/Fastfile index 7ed6d7dd..799ee214 100644 --- a/Emitron/fastlane/Fastfile +++ b/Emitron/fastlane/Fastfile @@ -18,6 +18,7 @@ default_platform(:ios) platform :ios do desc 'Push a new beta build to TestFlight' lane :ci_upload_beta_testflight do + app_store_connect_api_key prepare_ci_build('Beta') changelog_from_git_commits upload_to_testflight(app_identifier: 'com.razeware.emitron.ios.beta', @@ -27,6 +28,7 @@ platform :ios do desc 'Push a new production build to TestFlight' lane :ci_upload_release_testflight do + app_store_connect_api_key prepare_ci_build('Release') changelog_from_git_commits upload_to_testflight(app_identifier: 'com.razeware.emitron.ios', @@ -36,6 +38,7 @@ platform :ios do desc 'Push a new release version to App Store Connect ready for release' lane :ci_upload_release_appstore do + app_store_connect_api_key prepare_ci_build('Release') upload_to_app_store(app_identifier: 'com.razeware.emitron.ios') slack(message: 'emitron successfully uploaded to App Store Connect!') From 95c312173d57d3296449b3a4b20bbc5a8826ec5a Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Tue, 27 Apr 2021 20:25:52 +0100 Subject: [PATCH 62/69] #563: Forgot to pass the secrets in --- .github/workflows/appstore-upload.yml | 3 +++ .github/workflows/testflight-beta.yml | 3 +++ .github/workflows/testflight-release.yml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/.github/workflows/appstore-upload.yml b/.github/workflows/appstore-upload.yml index 20c88886..14e8db49 100644 --- a/.github/workflows/appstore-upload.yml +++ b/.github/workflows/appstore-upload.yml @@ -35,6 +35,9 @@ jobs: MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} SLACK_URL: ${{ secrets.SLACK_URL }} + APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} + APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }} + APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }} FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 5 FASTLANE_XCODEBUILD_SETTINGS_RETRIES: 5 run: | diff --git a/.github/workflows/testflight-beta.yml b/.github/workflows/testflight-beta.yml index 3f196650..9665e178 100644 --- a/.github/workflows/testflight-beta.yml +++ b/.github/workflows/testflight-beta.yml @@ -35,6 +35,9 @@ jobs: MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} SLACK_URL: ${{ secrets.SLACK_URL }} + APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} + APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }} + APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }} FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 5 FASTLANE_XCODEBUILD_SETTINGS_RETRIES: 5 run: | diff --git a/.github/workflows/testflight-release.yml b/.github/workflows/testflight-release.yml index 8c0b9569..53afd6cb 100644 --- a/.github/workflows/testflight-release.yml +++ b/.github/workflows/testflight-release.yml @@ -35,6 +35,9 @@ jobs: MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} SLACK_URL: ${{ secrets.SLACK_URL }} + APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} + APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }} + APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }} FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 5 FASTLANE_XCODEBUILD_SETTINGS_RETRIES: 5 run: | From 1c8f904db5255c8920a87d0edcad8bf16b7ab091 Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Tue, 27 Apr 2021 20:38:50 +0100 Subject: [PATCH 63/69] #563: Updating to use Xcode 12.5 --- .github/workflows/appstore-upload.yml | 4 ++-- .github/workflows/run_tests.yml | 4 ++-- .github/workflows/testflight-beta.yml | 4 ++-- .github/workflows/testflight-release.yml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/appstore-upload.yml b/.github/workflows/appstore-upload.yml index 14e8db49..da4654a8 100644 --- a/.github/workflows/appstore-upload.yml +++ b/.github/workflows/appstore-upload.yml @@ -12,8 +12,8 @@ jobs: steps: - uses: actions/checkout@v1 - - name: Switch to Xcode 12 - run: sudo xcode-select --switch /Applications/Xcode_12.app + - name: Switch to Xcode 12.5 + run: sudo xcode-select --switch /Applications/Xcode_12.5.app - name: Update fastlane run: | cd Emitron diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 8d64db06..8078d6e1 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -9,8 +9,8 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v1 - - name: Switch to Xcode 12 - run: sudo xcode-select -s /Applications/Xcode_12.app + - name: Switch to Xcode 12.5 + run: sudo xcode-select -s /Applications/Xcode_12.5.app - name: Update fastlane run: | cd Emitron diff --git a/.github/workflows/testflight-beta.yml b/.github/workflows/testflight-beta.yml index 9665e178..ca157768 100644 --- a/.github/workflows/testflight-beta.yml +++ b/.github/workflows/testflight-beta.yml @@ -12,8 +12,8 @@ jobs: steps: - uses: actions/checkout@v1 - - name: Switch to Xcode 12 - run: sudo xcode-select --switch /Applications/Xcode_12.app + - name: Switch to Xcode 12.5 + run: sudo xcode-select --switch /Applications/Xcode_12.5.app - name: Update fastlane run: | cd Emitron diff --git a/.github/workflows/testflight-release.yml b/.github/workflows/testflight-release.yml index 53afd6cb..4fb82f91 100644 --- a/.github/workflows/testflight-release.yml +++ b/.github/workflows/testflight-release.yml @@ -12,8 +12,8 @@ jobs: steps: - uses: actions/checkout@v1 - - name: Switch to Xcode 12 - run: sudo xcode-select --switch /Applications/Xcode_12.app + - name: Switch to Xcode 12.5 + run: sudo xcode-select --switch /Applications/Xcode_12.5.app - name: Update fastlane run: | cd Emitron From fb70aae7663ad44c58683d9555679e180d350e3c Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Tue, 27 Apr 2021 20:41:18 +0100 Subject: [PATCH 64/69] #563: Removing unused environment variables --- .github/workflows/appstore-upload.yml | 3 --- .github/workflows/testflight-beta.yml | 3 --- .github/workflows/testflight-release.yml | 3 --- 3 files changed, 9 deletions(-) diff --git a/.github/workflows/appstore-upload.yml b/.github/workflows/appstore-upload.yml index da4654a8..79c0d5a7 100644 --- a/.github/workflows/appstore-upload.yml +++ b/.github/workflows/appstore-upload.yml @@ -29,9 +29,6 @@ jobs: scripts/download_s3.sh production > Emitron/Emitron/Configuration/secrets.production.xcconfig - name: Execute fastlane env: - FASTLANE_USER: engineering@razeware.com - FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} - FASTLANE_SESSION: ${{ secrets.FASTLANE_SESSION }} MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} SLACK_URL: ${{ secrets.SLACK_URL }} diff --git a/.github/workflows/testflight-beta.yml b/.github/workflows/testflight-beta.yml index ca157768..786e6e58 100644 --- a/.github/workflows/testflight-beta.yml +++ b/.github/workflows/testflight-beta.yml @@ -29,9 +29,6 @@ jobs: scripts/download_s3.sh production > Emitron/Emitron/Configuration/secrets.production.xcconfig - name: Execute fastlane env: - FASTLANE_USER: engineering@razeware.com - FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} - FASTLANE_SESSION: ${{ secrets.FASTLANE_SESSION }} MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} SLACK_URL: ${{ secrets.SLACK_URL }} diff --git a/.github/workflows/testflight-release.yml b/.github/workflows/testflight-release.yml index 4fb82f91..bfecd7ed 100644 --- a/.github/workflows/testflight-release.yml +++ b/.github/workflows/testflight-release.yml @@ -29,9 +29,6 @@ jobs: scripts/download_s3.sh production > Emitron/Emitron/Configuration/secrets.production.xcconfig - name: Execute fastlane env: - FASTLANE_USER: engineering@razeware.com - FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} - FASTLANE_SESSION: ${{ secrets.FASTLANE_SESSION }} MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} SLACK_URL: ${{ secrets.SLACK_URL }} From 18ccb87fb60ab295479a546be0a5c7e7aa6ee6a3 Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Tue, 27 Apr 2021 20:45:49 +0100 Subject: [PATCH 65/69] #563: Xcode 12.5 not generally available yet Need to move CI to macOS 11 first, which is on private preview atm --- .github/workflows/appstore-upload.yml | 4 ++-- .github/workflows/run_tests.yml | 4 ++-- .github/workflows/testflight-beta.yml | 4 ++-- .github/workflows/testflight-release.yml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/appstore-upload.yml b/.github/workflows/appstore-upload.yml index 79c0d5a7..3acb0e39 100644 --- a/.github/workflows/appstore-upload.yml +++ b/.github/workflows/appstore-upload.yml @@ -12,8 +12,8 @@ jobs: steps: - uses: actions/checkout@v1 - - name: Switch to Xcode 12.5 - run: sudo xcode-select --switch /Applications/Xcode_12.5.app + - name: Switch to Xcode 12.4 + run: sudo xcode-select --switch /Applications/Xcode_12.4.app - name: Update fastlane run: | cd Emitron diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 8078d6e1..b360126b 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -9,8 +9,8 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v1 - - name: Switch to Xcode 12.5 - run: sudo xcode-select -s /Applications/Xcode_12.5.app + - name: Switch to Xcode 12.4 + run: sudo xcode-select -s /Applications/Xcode_12.4.app - name: Update fastlane run: | cd Emitron diff --git a/.github/workflows/testflight-beta.yml b/.github/workflows/testflight-beta.yml index 786e6e58..536a3736 100644 --- a/.github/workflows/testflight-beta.yml +++ b/.github/workflows/testflight-beta.yml @@ -12,8 +12,8 @@ jobs: steps: - uses: actions/checkout@v1 - - name: Switch to Xcode 12.5 - run: sudo xcode-select --switch /Applications/Xcode_12.5.app + - name: Switch to Xcode 12.4 + run: sudo xcode-select --switch /Applications/Xcode_12.4.app - name: Update fastlane run: | cd Emitron diff --git a/.github/workflows/testflight-release.yml b/.github/workflows/testflight-release.yml index bfecd7ed..1016b740 100644 --- a/.github/workflows/testflight-release.yml +++ b/.github/workflows/testflight-release.yml @@ -12,8 +12,8 @@ jobs: steps: - uses: actions/checkout@v1 - - name: Switch to Xcode 12.5 - run: sudo xcode-select --switch /Applications/Xcode_12.5.app + - name: Switch to Xcode 12.4 + run: sudo xcode-select --switch /Applications/Xcode_12.4.app - name: Update fastlane run: | cd Emitron From fccaac1932ad591acfabcef64db37dc3bc3554ca Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Wed, 28 Apr 2021 11:11:41 +0300 Subject: [PATCH 66/69] Minor Upgrade Swifty JSON 5.0.1 to clear warnings --- 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 928b5e22..1f00f384 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -2706,7 +2706,7 @@ repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 5.0.0; + minimumVersion = 5.0.1; }; }; 22C0512B23A4CBB9004D1223 /* XCRemoteSwiftPackageReference "GRDBCombine" */ = { diff --git a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8c232c2f..2cc54c8a 100644 --- a/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Emitron/Emitron.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/SwiftyJSON/SwiftyJSON", "state": { "branch": null, - "revision": "2b6054efa051565954e1d2b9da831680026cd768", - "version": "5.0.0" + "revision": "b3dcd7dbd0d488e1a7077cb33b00f2083e382f07", + "version": "5.0.1" } } ] From 18673ba993830ec98ebb569e2623d24954c36f70 Mon Sep 17 00:00:00 2001 From: Brian Moakley Date: Fri, 30 Apr 2021 18:12:51 -0400 Subject: [PATCH 67/69] Removes close button --- .../UI/Shared/Content Detail/ChildContentListingView.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift index 1bd0b16b..092585a3 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ChildContentListingView.swift @@ -62,11 +62,6 @@ private extension ChildContentListingView { .kerning(-0.5) .foregroundColor(.titleText) .padding([.top, .bottom]) - Button { - exit(0) - } label: { - Text("Close App0") - } Spacer() }.padding([.leading, .trailing], 20) } From 9b7d8f0f4f3670c6207da661c8422345c3e3c318 Mon Sep 17 00:00:00 2001 From: Franklin Byaruhanga Date: Mon, 3 May 2021 21:23:35 +0300 Subject: [PATCH 68/69] Remove unused Variable to clear warning Signed-off-by: Franklin Byaruhanga --- Emitron/Emitron/UI/SceneDelegate.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Emitron/Emitron/UI/SceneDelegate.swift b/Emitron/Emitron/UI/SceneDelegate.swift index 4dee968c..f2802a5a 100644 --- a/Emitron/Emitron/UI/SceneDelegate.swift +++ b/Emitron/Emitron/UI/SceneDelegate.swift @@ -70,7 +70,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let mainView = MainView() .environmentObject(sessionController) .environmentObject(dataManager) - let date = Calendar.current.date(byAdding: .day, value: -20, to: Date()) if NSUbiquitousKeyValueStore.default.object(forKey: LookupKey.requestReview) == nil { NSUbiquitousKeyValueStore.default.set(Date().timeIntervalSince1970, forKey: LookupKey.requestReview) } From c6ea7c5b64bee24c36be2cfae650248d29b4f361 Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Tue, 11 May 2021 20:52:18 +0100 Subject: [PATCH 69/69] Bumping version number to 1.0.7 --- 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 1f00f384..26b8aee3 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -2390,7 +2390,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.6; + MARKETING_VERSION = 1.0.7; PRODUCT_BUNDLE_IDENTIFIER = "com.razeware.emitron.ios$(BUNDLE_ID_SUFFIX)"; PRODUCT_MODULE_NAME = Emitron; PRODUCT_NAME = raywenderlich; @@ -2595,7 +2595,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.6; + MARKETING_VERSION = 1.0.7; PRODUCT_BUNDLE_IDENTIFIER = "com.razeware.emitron.ios$(BUNDLE_ID_SUFFIX)"; PRODUCT_MODULE_NAME = Emitron; PRODUCT_NAME = raywenderlich; @@ -2621,7 +2621,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.6; + MARKETING_VERSION = 1.0.7; PRODUCT_BUNDLE_IDENTIFIER = "com.razeware.emitron.ios$(BUNDLE_ID_SUFFIX)"; PRODUCT_MODULE_NAME = Emitron; PRODUCT_NAME = raywenderlich;