From 3347752e0fa8943fe32db1044b06aab1f69c7cfc Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Wed, 26 Apr 2023 19:45:40 +0100 Subject: [PATCH 1/5] Fixing the alignment on the login page for iPad --- Emitron/Emitron/UI/App Root/LoginView.swift | 37 +++++++++++---------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Emitron/Emitron/UI/App Root/LoginView.swift b/Emitron/Emitron/UI/App Root/LoginView.swift index 2e61b788..cb6b129a 100644 --- a/Emitron/Emitron/UI/App Root/LoginView.swift +++ b/Emitron/Emitron/UI/App Root/LoginView.swift @@ -34,27 +34,30 @@ struct LoginView: View { var body: some View { GeometryReader { proxy in - VStack { - + HStack { Spacer() - - Image("logo") - .resizable() - .scaledToFit() - .padding([.top], proxy.safeAreaInsets.top) - .padding(.horizontal, 92) - + VStack { + + Spacer() + + Image("logo") + .resizable() + .scaledToFit() + .padding([.top], proxy.safeAreaInsets.top) + .padding(.horizontal, 92) + + Spacer() + + MainButtonView(title: "Sign In", type: .primary(withArrow: false)) { + Task(priority: .userInitiated) { try await sessionController.logIn() } + } + .padding(.horizontal, 18) + .padding([.bottom], 38) + }.frame(maxWidth: 400) Spacer() - - MainButtonView(title: "Sign In", type: .primary(withArrow: false)) { - Task(priority: .userInitiated) { try await sessionController.logIn() } - } - .padding(.horizontal, 18) - .padding([.bottom], 38) } - .background(Color.background) .edgesIgnoringSafeArea([.all]) - } + }.background(Color.background) } } From 2d56ee67a41d05e47233da3810cd5ef4b3259851 Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Thu, 27 Apr 2023 00:00:45 +0100 Subject: [PATCH 2/5] A fair amount of unpleasantness to prevent the perpetual loading screen This occurs immediately post-first-login. Sometimes. This hack should force a reload. But we'll see. --- Emitron/Emitron/Logging/Logger.swift | 2 +- Emitron/Emitron/UI/App Root/MainView.swift | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Emitron/Emitron/Logging/Logger.swift b/Emitron/Emitron/Logging/Logger.swift index 217da4cd..d8ad743a 100644 --- a/Emitron/Emitron/Logging/Logger.swift +++ b/Emitron/Emitron/Logging/Logger.swift @@ -130,7 +130,7 @@ struct Event { ) -> Self { .init( source: "\(Source.self)", - action: "Login" + action: "Refresh" ) } diff --git a/Emitron/Emitron/UI/App Root/MainView.swift b/Emitron/Emitron/UI/App Root/MainView.swift index baa1cdd3..3db8fa2f 100644 --- a/Emitron/Emitron/UI/App Root/MainView.swift +++ b/Emitron/Emitron/UI/App Root/MainView.swift @@ -35,6 +35,9 @@ struct MainView: View { @EnvironmentObject private var dataManager: DataManager @EnvironmentObject private var messageBus: MessageBus @EnvironmentObject private var settingsManager: SettingsManager + + // A bit of a hack variable to ensure we can refresh when we want to + @State private var refresh = false private let tabViewModel = TabViewModel() private let notification = NotificationCenter.default.publisher(for: .requestReview) @@ -50,6 +53,15 @@ struct MainView: View { makeReviewRequest() } } + .onReceive(sessionController.$permissionState) { state in + Task { + // This is a bit of a hack. It forces reloading this view, even though + // it shouldn't be necessary. However, it appears to maybe work? + try await Task.sleep(nanoseconds: 2_000_000_000) + print(":::STATE:::: \(state)") + refresh.toggle() + } + } } } } @@ -59,7 +71,11 @@ private extension MainView { @ViewBuilder var contentView: some View { if !sessionController.isLoggedIn { LoginView() - } else { + // This conditional is a mess. But it forces this view to be reliant on + // the refresh state var, which is triggered whenever the permission state + // changes. This is necesary because, sometimes, this view doesn't update + // when permission state changes. And I don't know why. + } else if refresh || !refresh { switch sessionController.permissionState { case .loaded: tabBarView From 0fb09258abb9df112ccfd0b321aec7f0de105c0d Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Thu, 27 Apr 2023 07:42:25 +0100 Subject: [PATCH 3/5] Removing the pro tag from content --- Emitron/Emitron.xcodeproj/project.pbxproj | 4 -- .../Colours/Tags/Contents.json | 6 +- .../proTagBackground.colorset/Contents.json | 20 ------- .../Tags/proTagBorder.colorset/Contents.json | 56 ------------------- .../proTagForeground.colorset/Contents.json | 20 ------- .../Emitron/Styleguide/Color+Extensions.swift | 12 ---- .../Content Detail/ContentSummaryView.swift | 18 ++---- .../UI/Shared/Content List/CardView.swift | 6 +- Emitron/Emitron/UI/Shared/Tags/ProTag.swift | 50 ----------------- 9 files changed, 9 insertions(+), 183 deletions(-) delete mode 100644 Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagBackground.colorset/Contents.json delete mode 100644 Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagBorder.colorset/Contents.json delete mode 100644 Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagForeground.colorset/Contents.json delete mode 100644 Emitron/Emitron/UI/Shared/Tags/ProTag.swift diff --git a/Emitron/Emitron.xcodeproj/project.pbxproj b/Emitron/Emitron.xcodeproj/project.pbxproj index fc1ff7f0..00e31d59 100644 --- a/Emitron/Emitron.xcodeproj/project.pbxproj +++ b/Emitron/Emitron.xcodeproj/project.pbxproj @@ -213,7 +213,6 @@ 495E2B1A27F4FE8C003EEE86 /* Optional+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495E2B1927F4FE8C003EEE86 /* Optional+Extensions.swift */; }; 49971FEA27B297DA00FBCCEA /* TabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49971FE927B297DA00FBCCEA /* TabView.swift */; }; 8B283DEF23169A1F001F1B17 /* ProgressBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B283DEE23169A1E001F1B17 /* ProgressBarView.swift */; }; - 8B7E96DD2357A65F0083DA38 /* ProTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B7E96DC2357A65F0083DA38 /* ProTag.swift */; }; 8B7E96DF2357A66A0083DA38 /* CompletedTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B7E96DE2357A66A0083DA38 /* CompletedTag.swift */; }; 8BFC74BC23648DF3001979F1 /* ContentPaginatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BFC74BB23648DF3001979F1 /* ContentPaginatable.swift */; }; 8BFC74C32364A9BE001979F1 /* ContentScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BFC74C22364A9BE001979F1 /* ContentScreen.swift */; }; @@ -514,7 +513,6 @@ 495E2B1927F4FE8C003EEE86 /* Optional+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extensions.swift"; sourceTree = ""; }; 49971FE927B297DA00FBCCEA /* TabView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabView.swift; sourceTree = ""; }; 8B283DEE23169A1E001F1B17 /* ProgressBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressBarView.swift; sourceTree = ""; }; - 8B7E96DC2357A65F0083DA38 /* ProTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProTag.swift; sourceTree = ""; }; 8B7E96DE2357A66A0083DA38 /* CompletedTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletedTag.swift; sourceTree = ""; }; 8BFC74BB23648DF3001979F1 /* ContentPaginatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPaginatable.swift; sourceTree = ""; }; 8BFC74C22364A9BE001979F1 /* ContentScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentScreen.swift; sourceTree = ""; }; @@ -1051,7 +1049,6 @@ children = ( 22C6410723FA903F00CBFDE5 /* TagView.swift */, 8B7E96DE2357A66A0083DA38 /* CompletedTag.swift */, - 8B7E96DC2357A65F0083DA38 /* ProTag.swift */, ); path = Tags; sourceTree = ""; @@ -1941,7 +1938,6 @@ 222EEF7B23CDC24500B025A4 /* VideoPlaybackViewModel.swift in Sources */, 223D77AB23B0C099005BE95D /* Download.swift in Sources */, 22C0514023A4FBCC004D1223 /* Group+Persistence.swift in Sources */, - 8B7E96DD2357A65F0083DA38 /* ProTag.swift in Sources */, 2278AE4A240A565C00855221 /* Icon.swift in Sources */, 22DC505B2403EA0600A6E808 /* DownloadWarningView.swift in Sources */, 22E2268023CA2D7F00A83601 /* Group+GroupDisplayable.swift in Sources */, diff --git a/Emitron/Emitron/Assets.xcassets/Colours/Tags/Contents.json b/Emitron/Emitron/Assets.xcassets/Colours/Tags/Contents.json index da4a164c..73c00596 100644 --- a/Emitron/Emitron/Assets.xcassets/Colours/Tags/Contents.json +++ b/Emitron/Emitron/Assets.xcassets/Colours/Tags/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/Colours/Tags/proTagBackground.colorset/Contents.json b/Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagBackground.colorset/Contents.json deleted file mode 100644 index 83b409b1..00000000 --- a/Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagBackground.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x00", - "green" : "0x3F", - "red" : "0xCE" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagBorder.colorset/Contents.json b/Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagBorder.colorset/Contents.json deleted file mode 100644 index 7e8e15c5..00000000 --- a/Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagBorder.colorset/Contents.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - }, - "colors" : [ - { - "idiom" : "universal", - "color" : { - "color-space" : "srgb", - "components" : { - "red" : "1.000", - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000" - } - } - }, - { - "idiom" : "universal", - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "red" : "0.082", - "alpha" : "1.000", - "blue" : "0.722", - "green" : "0.486" - } - } - }, - { - "idiom" : "universal", - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "red" : "1.000", - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000" - } - } - } - ] -} \ No newline at end of file diff --git a/Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagForeground.colorset/Contents.json b/Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagForeground.colorset/Contents.json deleted file mode 100644 index c6e5d3d4..00000000 --- a/Emitron/Emitron/Assets.xcassets/Colours/Tags/proTagForeground.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - }, - "colors" : [ - { - "idiom" : "universal", - "color" : { - "color-space" : "srgb", - "components" : { - "red" : "1.000", - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000" - } - } - } - ] -} \ No newline at end of file diff --git a/Emitron/Emitron/Styleguide/Color+Extensions.swift b/Emitron/Emitron/Styleguide/Color+Extensions.swift index 21bc9310..fa95d7a2 100644 --- a/Emitron/Emitron/Styleguide/Color+Extensions.swift +++ b/Emitron/Emitron/Styleguide/Color+Extensions.swift @@ -70,18 +70,6 @@ extension Color { .init("tagForeground") } - static var proTagBackground: Color { - .init("proTagBackground") - } - - static var proTagForeground: Color { - .init("proTagForeground") - } - - static var proTagBorder: Color { - .init("proTagBorder") - } - static var filterTagBackground: Color { .init("filterTagBackground") } diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift index 475a2535..07c348ff 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ContentSummaryView.swift @@ -54,19 +54,11 @@ extension ContentSummaryView: View { dynamicContentViewModel.initialiseIfRequired() return VStack(alignment: .leading) { - HStack { - Text(content.technologyTripleString.uppercased()) - .font(.uiUppercase) - .foregroundColor(.contentText) - .kerning(0.5) - - Spacer() - - if content.professional { - ProTag() - } - } - .padding([.top], 20) + Text(content.technologyTripleString.uppercased()) + .font(.uiUppercase) + .foregroundColor(.contentText) + .kerning(0.5) + .padding([.top], 20) Text(content.name) .font(.uiTitle1) diff --git a/Emitron/Emitron/UI/Shared/Content List/CardView.swift b/Emitron/Emitron/UI/Shared/Content List/CardView.swift index 438910d4..9cc2d388 100644 --- a/Emitron/Emitron/UI/Shared/Content List/CardView.swift +++ b/Emitron/Emitron/UI/Shared/Content List/CardView.swift @@ -73,11 +73,7 @@ struct CardView: View { .lineSpacing(3) .foregroundColor(.contentText) - HStack(spacing: 8) { - if model.professional { - ProTag() - } - + HStack(spacing: 8) { completedTagOrReleasedAt Spacer() diff --git a/Emitron/Emitron/UI/Shared/Tags/ProTag.swift b/Emitron/Emitron/UI/Shared/Tags/ProTag.swift deleted file mode 100644 index c008f2e8..00000000 --- a/Emitron/Emitron/UI/Shared/Tags/ProTag.swift +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2022 Kodeco Inc - -// -// 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 ProTag: View { - var body: some View { - TagView( - text: "pro", - textColor: .proTagForeground, - backgroundColor: .proTagBackground, - borderColor: .proTagBorder - ) - } -} - -struct ProTag_Previews: PreviewProvider { - static var previews: some View { - ProTag() - .padding() - .background(Color.background) - .inAllColorSchemes - } -} From 094b3f390415bd6dae3d68c8937426c26a9ad74c Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Thu, 27 Apr 2023 07:54:10 +0100 Subject: [PATCH 4/5] Incorporating rules for new subscription types. It's hacky, but it'll do for now. --- Emitron/Emitron/Constants.swift | 2 +- Emitron/Emitron/Models/Permission.swift | 8 +++++++- Emitron/Emitron/Models/User.swift | 10 +++++++++- .../Content Detail/ProContentLockedOverlayView.swift | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Emitron/Emitron/Constants.swift b/Emitron/Emitron/Constants.swift index 9916a5b1..e05bb354 100644 --- a/Emitron/Emitron/Constants.swift +++ b/Emitron/Emitron/Constants.swift @@ -55,7 +55,7 @@ extension String { static let tutorials = "Tutorials" // MARK: Detail View - static let detailContentLockedCosPro = "Upgrade your account to watch this and other Pro courses" + static let detailContentLockedCosPro = "Upgrade your account to watch this and other locked courses" // MARK: Messaging static let bookmarkCreated = "Content bookmarked successfully." diff --git a/Emitron/Emitron/Models/Permission.swift b/Emitron/Emitron/Models/Permission.swift index 66058fb3..169fd917 100644 --- a/Emitron/Emitron/Models/Permission.swift +++ b/Emitron/Emitron/Models/Permission.swift @@ -29,10 +29,12 @@ import struct Foundation.Date -struct Permission: Equatable, Codable { +struct Permission: Equatable, Codable, Hashable { enum Tag: Int, Codable { case streamBeginner case streamPro + case streamTeam + case streamPersonal case download init?(from string: String) { @@ -43,6 +45,10 @@ struct Permission: Equatable, Codable { self = .streamPro case "download-videos": self = .download + case "stream-personal-videos": + self = .streamPersonal + case "stream-team-videos": + self = .streamTeam default: return nil } diff --git a/Emitron/Emitron/Models/User.swift b/Emitron/Emitron/Models/User.swift index 346d3b75..405110fb 100644 --- a/Emitron/Emitron/Models/User.swift +++ b/Emitron/Emitron/Models/User.swift @@ -90,6 +90,14 @@ private extension User { } func can(_ tag: Permission.Tag) -> Bool { - permissions?.lazy.map(\.tag).contains(tag) == true + // This is a hack, and relies on the fact that personal and team subs can + // pretty much stream all pro content. In future we need to add permissions + // to the contents API, and then do a comparison between content permissions + // and user permissions. + if tag == .streamPro { + let tags = Set([.streamPersonal, .streamTeam, .streamPro]) + return !tags.isDisjoint(with: permissions?.lazy.map(\.tag) ?? []) + } + return permissions?.lazy.map(\.tag).contains(tag) == true } } diff --git a/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift b/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift index a1c5eb70..ed144baa 100644 --- a/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift +++ b/Emitron/Emitron/UI/Shared/Content Detail/ProContentLockedOverlayView.swift @@ -35,7 +35,7 @@ struct ProContentLockedOverlayView: View { HStack { Image.padlock - Text("Pro Course") + Text("Locked Course") .font(.uiTitle1) .foregroundColor(.titleText) } From 9afc333e6b90feb9905e86c22977c8a8640bd989 Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Thu, 27 Apr 2023 08:16:42 +0100 Subject: [PATCH 5/5] Swiftlint complaints --- Emitron/Emitron/Guardpost/Guardpost.swift | 2 -- Emitron/Emitron/Models/DataCache.swift | 3 +-- Emitron/Emitron/Persistence/EmitronDatabase.swift | 4 ++++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Emitron/Emitron/Guardpost/Guardpost.swift b/Emitron/Emitron/Guardpost/Guardpost.swift index 81e40f51..890c6133 100644 --- a/Emitron/Emitron/Guardpost/Guardpost.swift +++ b/Emitron/Emitron/Guardpost/Guardpost.swift @@ -113,9 +113,7 @@ public extension Guardpost { // 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() } diff --git a/Emitron/Emitron/Models/DataCache.swift b/Emitron/Emitron/Models/DataCache.swift index 541265a2..4fd28651 100644 --- a/Emitron/Emitron/Models/DataCache.swift +++ b/Emitron/Emitron/Models/DataCache.swift @@ -71,8 +71,7 @@ extension DataCache { cacheUpdate.progressions.forEach { progressions[$0.contentID] = $0 } cacheUpdate.groups.forEach { groupIndexedGroups[$0.id] = $0 } - // swiftlint:disable generic_type_name - func mergeWithCacheUpdate( + func mergeWithCacheUpdate( // swiftlint:disable:this generic_type_name _ dictionary: inout [Int: [contentID]], _ getContentID: (DataCacheUpdate) -> [contentID] ) { diff --git a/Emitron/Emitron/Persistence/EmitronDatabase.swift b/Emitron/Emitron/Persistence/EmitronDatabase.swift index a444cd8a..6cee961d 100644 --- a/Emitron/Emitron/Persistence/EmitronDatabase.swift +++ b/Emitron/Emitron/Persistence/EmitronDatabase.swift @@ -29,6 +29,7 @@ import GRDB +// We use `t` as a variable name rather a lot // swiftlint:disable identifier_name /// A type responsible for initialising the application's database @@ -170,3 +171,6 @@ enum EmitronDatabase { return migrator } } + +// Stop swiftlint complaining +// swiftlint:enable identifier_name