Skip to content

Commit

Permalink
Merge pull request #1676 from p2p-org/feature/pwn-793
Browse files Browse the repository at this point in the history
Alert user about new update
  • Loading branch information
lisemyon authored Feb 8, 2024
2 parents ac7e377 + 58d27b1 commit 53a3311
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Combine
import Foundation

class ApplicationUpdateManager {
enum State {
case updateAvailable(Version)
case noUpdate
}

private let provider: ApplicationUpdateProvider

init(provider: ApplicationUpdateProvider) {
self.provider = provider
}

var currentInstalledVersion: Version? {
let appVersionKey = "CFBundleShortVersionString"
guard let appVersionValue = Bundle.main.object(forInfoDictionaryKey: appVersionKey) as? String else {
return nil
}

return try? Version(from: appVersionValue)
}

func isUpdateAvailable() async -> State {
guard
let appVersion = currentInstalledVersion,
let storeAppVersion = try? await provider.info()
else {
return .noUpdate
}

print("[ApplicationUpdateManager]", appVersion, storeAppVersion)

// Check
if storeAppVersion.major > appVersion.major {
return .updateAvailable(storeAppVersion)
} else if storeAppVersion.minor > appVersion.minor {
return .updateAvailable(storeAppVersion)
} else if storeAppVersion.patch > appVersion.patch {
return .updateAvailable(storeAppVersion)
}

return .noUpdate
}

func awareUser(version: Version) async {
UserDefaults.standard.set(version.string, forKey: "application_user_awareness")
}

func isUserAwareAboutUpdate(version: Version) async -> Bool {
guard let userAwareness = UserDefaults.standard.object(forKey: "application_user_awareness") as? String else {
return false
}

if version.string == userAwareness {
return true
} else {
return false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Firebase
import Foundation

protocol ApplicationUpdateProvider {
func info() async throws -> Version
}

class FirebaseApplicationUpdateProvider: ApplicationUpdateProvider {
func info() async throws -> Version {
let remoteConfig = RemoteConfig.remoteConfig()
let appVersion = remoteConfig.configValue(forKey: "app_version", source: .remote).stringValue

guard let appVersion else {
throw Version.VersionError.invalidFormat
}

return try Version(from: appVersion)
}
}
48 changes: 48 additions & 0 deletions p2p_wallet/Common/Services/ApplicationUpdate/Version.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Foundation

struct Version: Decodable {
// MARK: - Enumerations

enum VersionError: Error {
case invalidFormat
}

// MARK: - Public properties

let major: Int
let minor: Int
let patch: Int

// MARK: - Init

init(from decoder: Decoder) throws {
do {
let container = try decoder.singleValueContainer()
let version = try container.decode(String.self)
try self.init(from: version)
} catch {
throw VersionError.invalidFormat
}
}

init(from version: String) throws {
let versionComponents = version.components(separatedBy: ".").map { Int($0) }
guard versionComponents.count == 3 else {
throw VersionError.invalidFormat
}

guard let major = versionComponents[0], let minor = versionComponents[1],
let patch = versionComponents[2]
else {
throw VersionError.invalidFormat
}

self.major = major
self.minor = minor
self.patch = patch
}

var string: String {
"\(major).\(minor).\(patch)"
}
}
5 changes: 5 additions & 0 deletions p2p_wallet/Injection/Resolver+registerAllServices.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ extension Resolver: ResolverRegistering {
.implements(KeyAppTokenProvider.self)
.scope(.application)

register {
ApplicationUpdateManager(provider: FirebaseApplicationUpdateProvider())
}
.scope(.application)

register {
DeviceShareMigrationService(
isWeb3AuthUser: resolve(UserWalletManager.self)
Expand Down
1 change: 1 addition & 0 deletions p2p_wallet/Resources/Base.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@
"Token 2022 transfer fee" = "Token 2022 transfer fee";
"Calculated by subtracting the token 2022 transfer fee from your balance" = "Calculated by subtracting the token 2022 transfer fee from your balance";
"Calculated by subtracting the token 2022 transfer fee and account creation fee from your balance" = "Calculated by subtracting the token 2022 transfer fee and account creation fee from your balance";
"Update available" = "Update available";
"Referral program" = "Referral program";
"%@ all the time" = "%@ all the time";
"Here’s how do we count your profits for total balance and every single token" = "Here’s how do we count your profits for total balance and every single token";
Expand Down
1 change: 1 addition & 0 deletions p2p_wallet/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@
"Token 2022 transfer fee" = "Token 2022 transfer fee";
"Calculated by subtracting the token 2022 transfer fee from your balance" = "Calculated by subtracting the token 2022 transfer fee from your balance";
"Calculated by subtracting the token 2022 transfer fee and account creation fee from your balance" = "Calculated by subtracting the token 2022 transfer fee and account creation fee from your balance";
"Update available" = "Update available";
"Referral program" = "Referral program";
"%@ all the time" = "%@ all the time";
"Here’s how do we count your profits for total balance and every single token" = "Here’s how do we count your profits for total balance and every single token";
Expand Down
7 changes: 7 additions & 0 deletions p2p_wallet/Scenes/DebugMenu/View/DebugMenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ struct DebugMenuView: View {
DebugTextField(title: "Bridge:", content: $globalAppState.bridgeEndpoint)
DebugTextField(title: "Token:", content: $globalAppState.tokenEndpoint)
Toggle("Prefer direct swap", isOn: $globalAppState.preferDirectSwap)

Button {
Task {
UserDefaults.standard.set(nil, forKey: "application_user_awareness")
}
} label: { Text("Clean version alert") }

Button {
Task {
ResolverScope.session.reset()
Expand Down
9 changes: 9 additions & 0 deletions p2p_wallet/Scenes/Main/Crypto/Container/CryptoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,14 @@ struct CryptoView: View {
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
viewModel.viewAppeared()
}
.alert(L10n.updateAvailable, isPresented: $viewModel.updateAlert) {
Button(L10n.update, action: {
viewModel.openAppstore()
viewModel.userIsAwareAboutUpdate()
})
Button(L10n.cancel, role: .cancel, action: {
viewModel.userIsAwareAboutUpdate()
})
}
}
}
43 changes: 43 additions & 0 deletions p2p_wallet/Scenes/Main/Crypto/Container/CryptoViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import Resolver
import Sell
import Send
import SolanaSwift
import UIKit
import Wormhole

/// ViewModel of `Crypto` scene
final class CryptoViewModel: BaseViewModel, ObservableObject {
// MARK: - Dependencies

@Injected private var authenticationHandler: AuthenticationHandlerType
@Injected private var solanaAccountsService: SolanaAccountsService
@Injected private var ethereumAccountsService: EthereumAccountsService
@Injected private var analyticsManager: AnalyticsManager
Expand All @@ -23,6 +25,7 @@ final class CryptoViewModel: BaseViewModel, ObservableObject {
@Injected private var nameStorage: NameStorageType
@Injected private var sellDataService: any SellDataService
@Injected private var createNameService: CreateNameService
@Injected private var applicationUpdateManager: ApplicationUpdateManager
@Injected private var referralService: ReferralProgramService

let navigation: PassthroughSubject<CryptoNavigation, Never>
Expand All @@ -35,6 +38,9 @@ final class CryptoViewModel: BaseViewModel, ObservableObject {
@Published var state = State.pending
@Published var address = ""

@Published var updateAlert: Bool = false
@Published var newVersion: Version?

// MARK: - Initializers

init(navigation: PassthroughSubject<CryptoNavigation, Never>) {
Expand All @@ -56,6 +62,19 @@ final class CryptoViewModel: BaseViewModel, ObservableObject {
await CryptoAccountsSynchronizationService().refresh()
}

func userIsAwareAboutUpdate() {
guard let version = newVersion else { return }
Task { await applicationUpdateManager.awareUser(version: version) }
}

func openAppstore() {
UIApplication.shared.open(
URL(string: "itms-apps://itunes.apple.com/app/id1605603333")!,
options: [:],
completionHandler: nil
)
}

func viewAppeared() {
if available(.solanaNegativeStatus) {
solanaTracker.startTracking()
Expand Down Expand Up @@ -182,6 +201,30 @@ private extension CryptoViewModel {
}
.store(in: &subscriptions)

// Update
authenticationHandler.authenticationStatusPublisher
.filter { $0 == nil }
.delay(for: 3, scheduler: RunLoop.main)
.sink { [weak self] _ in
Task {
guard let self else { return }
let result = await self.applicationUpdateManager.isUpdateAvailable()
switch result {
case .noUpdate:
return
case let .updateAvailable(version):
if await self.applicationUpdateManager.isUserAwareAboutUpdate(version: version) {
return
} else {
await MainActor.run {
self.updateAlert = true
self.newVersion = version
}
}
}
}
}.store(in: &subscriptions)

openReferralProgramDetails
.map { CryptoNavigation.referral }
.sink { [weak self] navigation in
Expand Down
2 changes: 1 addition & 1 deletion p2p_wallet/Scenes/Main/NewHome/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ struct HomeView: View {
}
}

func navigation<Content: View>(@ViewBuilder content: @escaping () -> Content) -> some View {
func navigation(@ViewBuilder content: @escaping () -> some View) -> some View {
NavigationView {
ZStack {
Color(.smoke)
Expand Down

0 comments on commit 53a3311

Please sign in to comment.