diff --git a/.package.resolved b/.package.resolved index 64343e0b9..6aeef83ab 100644 --- a/.package.resolved +++ b/.package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/ProxymanApp/atlantis", "state" : { - "revision" : "131d757cf8e6e368ad338728379174f7cfff9326", - "version" : "1.23.0" + "revision" : "5145a7041ec71421d09653db87dcc80c81792004", + "version" : "1.24.0" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/CocoaLumberjack/CocoaLumberjack", "state" : { - "revision" : "363ed23d19a931809ea834a7d722da830353806a", - "version" : "3.8.2" + "revision" : "ae8e475260b978299134d42d739f0ec8241d9af0", + "version" : "3.8.3" } }, { @@ -94,8 +94,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Infomaniak/ios-version-checker", "state" : { - "revision" : "821a785fd229afae83c0a982d772fe89542ab502", - "version" : "1.0.1" + "revision" : "deeb4fa8377f9bcf3303216d1ea61c1e893b2abd", + "version" : "1.1.0" } }, { @@ -166,8 +166,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/realm/realm-core.git", "state" : { - "revision" : "7227d6a447821c28895daa099b6c7cd4c99d461b", - "version" : "13.25.1" + "revision" : "a5e87a39cffdcc591f3203c11cfca68100d0b9a6", + "version" : "13.26.0" } }, { @@ -175,8 +175,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/realm/realm-swift", "state" : { - "revision" : "836cc4b8619886f979f8961c3f592a82b0741591", - "version" : "10.45.3" + "revision" : "eafdd3720a8cc750bdd38bf776082d2c8cf743fc", + "version" : "10.46.0" } }, { @@ -236,10 +236,10 @@ { "identity" : "swift-log", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-log.git", + "location" : "https://github.com/apple/swift-log", "state" : { - "revision" : "532d8b529501fb73a2455b179e0bbb6d49b652ed", - "version" : "1.5.3" + "revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5", + "version" : "1.5.4" } }, { @@ -301,8 +301,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/markiv/SwiftUI-Shimmer", "state" : { - "revision" : "1f3a620e4abe890d00008cb2af7023d810b433a7", - "version" : "1.4.0" + "revision" : "5659a623567cefe258d1e3e67cb65585fbb6ecb6", + "version" : "1.4.2" } }, { diff --git a/Mail/RootView.swift b/Mail/RootView.swift index 7bc724ee1..46e035a96 100644 --- a/Mail/RootView.swift +++ b/Mail/RootView.swift @@ -32,14 +32,16 @@ struct RootView: View { .environmentObject(mainViewState) case .onboarding: OnboardingView() + case .authorization: + AuthorizationView() case .noMailboxes: NoMailboxView() case .unavailableMailboxes: UnavailableMailboxesView() + case .updateRequired: + MailUpdateRequiredView() case .preloading(let currentAccount): PreloadingView(currentAccount: currentAccount) - case .authorization: - AuthorizationView() } } } diff --git a/Mail/UserAccountScene.swift b/Mail/UserAccountScene.swift index def29c496..4192fc7dd 100644 --- a/Mail/UserAccountScene.swift +++ b/Mail/UserAccountScene.swift @@ -29,6 +29,7 @@ import MailResources import Sentry import SwiftUI import UIKit +import VersionChecker struct UserAccountScene: Scene { @Environment(\.scenePhase) private var scenePhase @@ -37,6 +38,7 @@ struct UserAccountScene: Scene { @LazyInjectService private var accountManager: AccountManager @LazyInjectService private var appLaunchCounter: AppLaunchCounter @LazyInjectService private var refreshAppBackgroundTask: RefreshAppBackgroundTask + @LazyInjectService private var platformDetector: PlatformDetectable @StateObject private var rootViewState = RootViewState() @@ -51,6 +53,7 @@ struct UserAccountScene: Scene { appLaunchCounter.increase() refreshCacheData() rootViewState.transitionToLockViewIfNeeded() + checkAppVersion() UserDefaults.shared.openingUntilReview -= 1 case .background: refreshAppBackgroundTask.scheduleForBackgroundLaunchIfNeeded() @@ -108,4 +111,25 @@ struct UserAccountScene: Scene { } } } + + func checkAppVersion() { + Task { + do { + let platform: Platform = platformDetector.isMacCatalyst ? .macOS : .ios + let versionStatus = try await VersionChecker.standard.checkAppVersionStatus(platform: platform) + switch versionStatus { + case .updateIsRequired: + rootViewState.transitionToRootViewDestination(.updateRequired) + case .canBeUpdated: + if case .mainView(let mainViewState) = rootViewState.state { + mainViewState.isShowingUpdateAvailable = true + } + case .isUpToDate: + break + } + } catch { + DDLogError("Error while checking version status: \(error)") + } + } + } } diff --git a/Mail/Views/MailUpdateRequiredView.swift b/Mail/Views/MailUpdateRequiredView.swift new file mode 100644 index 000000000..061b278fa --- /dev/null +++ b/Mail/Views/MailUpdateRequiredView.swift @@ -0,0 +1,51 @@ +/* + Infomaniak Mail - iOS App + Copyright (C) 2024 Infomaniak Network SA + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +import MailCore +import MailResources +import SwiftUI +import VersionChecker + +struct MailUpdateRequiredView: View { + @Environment(\.openURL) private var openURL + + private let sharedStyle = TemplateSharedStyle( + background: MailResourcesAsset.backgroundColor.swiftUIColor, + titleTextStyle: .init(font: MailTextStyle.header2.font, color: MailTextStyle.header2.color), + descriptionTextStyle: .init(font: MailTextStyle.bodySecondary.font, color: MailTextStyle.bodySecondary.color), + buttonStyle: .init( + background: .accentColor, + textStyle: .init(font: MailTextStyle.bodyAccent.font, color: UserDefaults.shared.accentColor.onAccent.swiftUIColor), + height: UIConstants.buttonLargeHeight, + radius: UIConstants.buttonsRadius + ) + ) + + var body: some View { + UpdateRequiredView(image: MailResourcesAsset.updateRequired.swiftUIImage, sharedStyle: sharedStyle, handler: updateApp) + } + + private func updateApp() { + let url: URLConstants = Bundle.main.isRunningInTestFlight ? .testFlight : .appStore + openURL(url.url) + } +} + +#Preview { + MailUpdateRequiredView() +} diff --git a/Mail/Views/SplitView.swift b/Mail/Views/SplitView.swift index 710d2ed85..60b2462b4 100644 --- a/Mail/Views/SplitView.swift +++ b/Mail/Views/SplitView.swift @@ -26,7 +26,6 @@ import MailResources import NavigationBackport import RealmSwift import SwiftUI -import VersionChecker @_spi(Advanced) import SwiftUIIntrospect public class SplitViewManager: ObservableObject { @@ -64,7 +63,6 @@ struct SplitView: View { @LazyInjectService private var platformDetector: PlatformDetectable @LazyInjectService private var appLaunchCounter: AppLaunchCounter - @State private var isShowingUpdateAvailable = false @State private var isShowingSyncDiscovery = false @State private var isShowingSyncProfile = false @@ -120,7 +118,7 @@ struct SplitView: View { } } } - .discoveryPresenter(isPresented: $isShowingUpdateAvailable) { + .discoveryPresenter(isPresented: $mainViewState.isShowingUpdateAvailable) { DiscoveryView(item: .updateDiscovery) { willUpdate in guard willUpdate else { return } let url: URLConstants = Bundle.main.isRunningInTestFlight ? .testFlight : .appStore @@ -166,8 +164,7 @@ struct SplitView: View { } guard !platformDetector.isDebug else { return } // We don't want to show both DiscoveryView at the same time - isShowingUpdateAvailable = try await VersionChecker.standard.showUpdateVersion() - isShowingSyncDiscovery = isShowingUpdateAvailable ? false : showSync() + isShowingSyncDiscovery = mainViewState.isShowingUpdateAvailable ? false : showSync() } } .onOpenURL { url in diff --git a/MailCore/Cache/MainViewState.swift b/MailCore/Cache/MainViewState.swift index 90c167ad5..1edfc7ac2 100644 --- a/MailCore/Cache/MainViewState.swift +++ b/MailCore/Cache/MainViewState.swift @@ -29,6 +29,7 @@ public class MainViewState: ObservableObject, SelectedThreadOwnable { @Published public var isShowingSearch = false @Published public var isShowingReviewAlert = false + @Published public var isShowingUpdateAvailable = false @Published public var isShowingSetAppAsDefaultDiscovery = false @Published public var isShowingChristmasEasterEgg = false diff --git a/MailCore/Cache/RootViewState.swift b/MailCore/Cache/RootViewState.swift index ba6d73544..112df3119 100644 --- a/MailCore/Cache/RootViewState.swift +++ b/MailCore/Cache/RootViewState.swift @@ -37,6 +37,8 @@ public enum RootViewType: Equatable { return true case (.unavailableMailboxes, .unavailableMailboxes): return true + case (.updateRequired, .updateRequired): + return true case (.mainView(let lhsMainViewState), .mainView(let rhsMainViewState)): return lhsMainViewState.mailboxManager == rhsMainViewState.mailboxManager case (.preloading(let lhsAccount), .preloading(let rhsAccount)): @@ -52,6 +54,7 @@ public enum RootViewType: Equatable { case authorization case noMailboxes case unavailableMailboxes + case updateRequired case preloading(Account) } @@ -61,6 +64,7 @@ public enum RootViewDestination { case onboarding case noMailboxes case unavailableMailboxes + case updateRequired } /// Something that represents the state of the root view @@ -127,6 +131,8 @@ public class RootViewState: ObservableObject { state = .noMailboxes case .unavailableMailboxes: state = .unavailableMailboxes + case .updateRequired: + state = .updateRequired } } } diff --git a/MailResources/Assets.xcassets/update-required.imageset/Contents.json b/MailResources/Assets.xcassets/update-required.imageset/Contents.json new file mode 100644 index 000000000..d51d2bd80 --- /dev/null +++ b/MailResources/Assets.xcassets/update-required.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "filename" : "update-required-light.svg", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "update-required-dark.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/MailResources/Assets.xcassets/update-required.imageset/update-required-dark.svg b/MailResources/Assets.xcassets/update-required.imageset/update-required-dark.svg new file mode 100644 index 000000000..fe91a0dc3 --- /dev/null +++ b/MailResources/Assets.xcassets/update-required.imageset/update-required-dark.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MailResources/Assets.xcassets/update-required.imageset/update-required-light.svg b/MailResources/Assets.xcassets/update-required.imageset/update-required-light.svg new file mode 100644 index 000000000..11e2c653e --- /dev/null +++ b/MailResources/Assets.xcassets/update-required.imageset/update-required-light.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Project.swift b/Project.swift index eb0c12303..574cc99e3 100644 --- a/Project.swift +++ b/Project.swift @@ -36,7 +36,7 @@ let project = Project(name: "Mail", .package(url: "https://github.com/Infomaniak/ios-bug-tracker", .upToNextMajor(from: "2.0.0")), .package(url: "https://github.com/Infomaniak/SQRichTextEditor", .upToNextMajor(from: "1.1.1")), .package(url: "https://github.com/Infomaniak/SwiftSoup", .upToNextMajor(from: "1.1.0")), - .package(url: "https://github.com/Infomaniak/ios-version-checker", .upToNextMajor(from: "1.0.1")), + .package(url: "https://github.com/Infomaniak/ios-version-checker", .upToNextMajor(from: "1.1.0")), .package(url: "https://github.com/ProxymanApp/atlantis", .upToNextMajor(from: "1.21.0")), .package(url: "https://github.com/Alamofire/Alamofire", .upToNextMajor(from: "5.2.2")), .package(url: "https://github.com/CocoaLumberjack/CocoaLumberjack", .upToNextMajor(from: "3.7.0")),