Skip to content

Commit

Permalink
Add premium UI for signals
Browse files Browse the repository at this point in the history
  • Loading branch information
ant013 committed Dec 31, 2024
1 parent 2fa88c4 commit 92f6406
Show file tree
Hide file tree
Showing 20 changed files with 246 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ struct MarketAdvancedSearchResultsView: View {

@State private var sortBySelectorPresented = false
@State private var presentedCoin: Coin?
@State private var signalsPresented = false
@State private var subscriptionPresented = false

init(marketInfos: [MarketInfo], timePeriod: HsTimePeriod, isParentPresented: Binding<Bool>) {
_viewModel = StateObject(wrappedValue: MarketAdvancedSearchResultsViewModel(marketInfos: marketInfos, timePeriod: timePeriod))
Expand All @@ -30,6 +32,7 @@ struct MarketAdvancedSearchResultsView: View {
}) {
itemContent(
coin: coin,
indicatorResult: marketInfo.indicatorsResult,
marketCap: marketInfo.marketCap,
price: marketInfo.price.flatMap { ValueFormatter.instance.formatFull(currency: viewModel.currency, value: $0) } ?? "n/a".localized,
rank: marketInfo.marketCapRank,
Expand Down Expand Up @@ -67,6 +70,14 @@ struct MarketAdvancedSearchResultsView: View {
viewModel.sortBy = viewModel.sortBys[index]
}
)
.sheet(isPresented: $signalsPresented) {
MarketWatchlistSignalsView(setShowSignals: { [weak viewModel] in
viewModel?.set(showSignals: $0)
}, isPresented: $signalsPresented)
}
.sheet(isPresented: $subscriptionPresented) {
PurchasesView()
}
}

@ViewBuilder private func header() -> some View {
Expand All @@ -78,6 +89,19 @@ struct MarketAdvancedSearchResultsView: View {
Text(viewModel.sortBy.title)
}
.buttonStyle(SecondaryButtonStyle(style: .default, rightAccessory: .dropDown))

if viewModel.showSignals {
signalsButton()
.buttonStyle(SecondaryActiveButtonStyle(leftAccessory:
.custom(icon: "crown_20", enabledColor: .themeDark, disabledColor: .themeDark)
))
} else {
signalsButton()
.buttonStyle(
SecondaryButtonStyle(leftAccessory:
.custom(image: Image("crown_20"), pressedColor: .themeJacob, activeColor: .themeJacob, disabledColor: .themeJacob)
))
}
}
.padding(.horizontal, .margin16)
.padding(.vertical, .margin8)
Expand All @@ -96,12 +120,34 @@ struct MarketAdvancedSearchResultsView: View {
)
}

@ViewBuilder private func itemContent(coin: Coin?, marketCap: Decimal?, price: String, rank: Int?, diff: Decimal?) -> some View {
@ViewBuilder private func signalsButton() -> some View {
Button(action: {
guard viewModel.premiumEnabled else {
subscriptionPresented = true
return
}

if viewModel.showSignals {
viewModel.set(showSignals: false)
} else {
signalsPresented = true
}
}) {
Text("market.watchlist.signals".localized)
}
}

@ViewBuilder private func itemContent(coin: Coin?, indicatorResult: TechnicalAdvice.Advice?, marketCap: Decimal?, price: String, rank: Int?, diff: Decimal?) -> some View {
CoinIconView(coin: coin)

VStack(spacing: 1) {
HStack(spacing: .margin8) {
Text(coin?.code ?? "CODE").textBody()

if viewModel.showSignals, let signal = indicatorResult {
MarketWatchlistSignalBadge(signal: signal)
}

Spacer()
Text(price).textBody()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,28 @@ import Foundation
import MarketKit

class MarketAdvancedSearchResultsViewModel: ObservableObject {
private static let showSignalKey = "advanced_search_show_signal_key"

private let currencyManager = App.shared.currencyManager
private let purchaseManager = App.shared.purchaseManager
private let userDefaultsStorage = App.shared.userDefaultsStorage
private var cancellables = Set<AnyCancellable>()

private let internalMarketInfos: [MarketInfo]
let timePeriod: HsTimePeriod

private var showSignalsVar: Bool {
get {
userDefaultsStorage.value(for: Self.showSignalKey) ?? false
}
set {
userDefaultsStorage.set(value: newValue, for: Self.showSignalKey)
}
}

@Published private(set) var premiumEnabled: Bool = false
@Published var marketInfos: [MarketInfo] = []
@Published var showSignals: Bool = false

var sortBy: MarketModule.SortBy = .highestCap {
didSet {
Expand All @@ -18,12 +33,29 @@ class MarketAdvancedSearchResultsViewModel: ObservableObject {
}

init(marketInfos: [MarketInfo], timePeriod: HsTimePeriod) {
let premiumEnabled = purchaseManager.subscription != nil

internalMarketInfos = marketInfos
self.timePeriod = timePeriod

showSignals = premiumEnabled && showSignalsVar
self.premiumEnabled = premiumEnabled

purchaseManager.$subscription
.receive(on: DispatchQueue.main)
.sink { [weak self] subscription in
self?.premiumEnabled = subscription != nil
self?.syncShowSignals()
}
.store(in: &cancellables)

syncState()
}

private func syncShowSignals() {
showSignals = premiumEnabled && showSignalsVar
}

private func syncState() {
marketInfos = internalMarketInfos.sorted(sortBy: sortBy, timePeriod: timePeriod)
}
Expand All @@ -37,4 +69,12 @@ extension MarketAdvancedSearchResultsViewModel {
var sortBys: [MarketModule.SortBy] {
[.highestCap, .lowestCap, .gainers, .losers]
}

func set(showSignals: Bool) {
stat(page: .markets, section: .searchResults, event: .showSignals(shown: showSignals))
syncState()
showSignalsVar = showSignals

self.showSignals = premiumEnabled && showSignals
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct MarketAdvancedSearchView: View {
@State var priceCloseToPresented = false
@State var priceChangePresented = false
@State var pricePeriodPresented = false
@State private var subscriptionPresented = false
@State var subscriptionPresented = false
@State var resultsPresented = false

var body: some View {
Expand Down Expand Up @@ -116,10 +116,6 @@ struct MarketAdvancedSearchView: View {
}
}

private func openPremiumSubscription() {
subscriptionPresented = true
}

@ViewBuilder private func topRow() -> some View {
ClickableRow(spacing: .margin8) {
topPresented = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ class MarketAdvancedSearchViewModel: ObservableObject {

premiumEnabled = purchaseManager.subscription != nil
purchaseManager.$subscription
.receive(on: DispatchQueue.main)
.sink { [weak self] subscription in
self?.premiumEnabled = subscription != nil
}
Expand Down Expand Up @@ -328,9 +329,11 @@ extension MarketAdvancedSearchViewModel {
func syncMarketInfos() {
tasks = Set()

internalState = .loading

Task { [weak self, marketKit, top, currencyManager] in
await MainActor.run { [weak self] in
self?.internalState = .loading
}

do {
let marketInfos = try await marketKit.advancedMarketInfos(top: top.rawValue, currencyCode: currencyManager.baseCurrency.code)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import MarketKit
import SwiftUI

struct MarketWatchlistSignalsView: View {
@ObservedObject var viewModel: MarketWatchlistViewModel
var setShowSignals: (Bool) -> Void
@Binding var isPresented: Bool

@State private var maxBadgeWidth: CGFloat = .zero
Expand Down Expand Up @@ -36,7 +36,7 @@ struct MarketWatchlistSignalsView: View {
}
} bottomContent: {
Button(action: {
viewModel.showSignals = true
setShowSignals(true)
isPresented = false
}) {
Text("market.watchlist.signals.turn_on".localized)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct MarketWatchlistView: View {
@State private var timePeriodSelectorPresented = false
@State private var presentedCoin: Coin?
@State private var signalsPresented = false
@State private var subscriptionPresented = false

@State private var editMode: EditMode = .inactive

Expand Down Expand Up @@ -78,11 +79,16 @@ struct MarketWatchlistView: View {

if viewModel.showSignals {
signalsButton()
.buttonStyle(SecondaryActiveButtonStyle())
.buttonStyle(SecondaryActiveButtonStyle(leftAccessory:
.custom(icon: "crown_20", enabledColor: .themeDark, disabledColor: .themeDark)
))
.disabled(disabled)
} else {
signalsButton()
.buttonStyle(SecondaryButtonStyle())
.buttonStyle(
SecondaryButtonStyle(leftAccessory:
.custom(image: Image("crown_20"), pressedColor: .themeJacob, activeColor: .themeJacob, disabledColor: .themeJacob)
))
.disabled(disabled)
}
}
Expand Down Expand Up @@ -114,14 +120,24 @@ struct MarketWatchlistView: View {
}
)
.sheet(isPresented: $signalsPresented) {
MarketWatchlistSignalsView(viewModel: viewModel, isPresented: $signalsPresented)
MarketWatchlistSignalsView(setShowSignals: { [weak viewModel] in
viewModel?.set(showSignals: $0)
}, isPresented: $signalsPresented)
}
.sheet(isPresented: $subscriptionPresented) {
PurchasesView()
}
}

@ViewBuilder private func signalsButton() -> some View {
Button(action: {
guard viewModel.premiumEnabled else {
subscriptionPresented = true
return
}

if viewModel.showSignals {
viewModel.showSignals = false
viewModel.set(showSignals: false)
} else {
signalsPresented = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class MarketWatchlistViewModel: ObservableObject {
private let watchlistManager = App.shared.watchlistManager
private let userDefaultsStorage = App.shared.userDefaultsStorage
private let appManager = App.shared.appManager
private let purchaseManager = App.shared.purchaseManager
private var cancellables = Set<AnyCancellable>()
private var tasks = Set<AnyTask>()

Expand All @@ -20,6 +21,7 @@ class MarketWatchlistViewModel: ObservableObject {
}
}

@Published private(set) var premiumEnabled: Bool = false
@Published var state: State = .loading

@Published var sortBy: WatchlistSortBy {
Expand All @@ -41,24 +43,33 @@ class MarketWatchlistViewModel: ObservableObject {
}
}

@Published var showSignals: Bool {
didSet {
stat(page: .markets, section: .watchlist, event: .showSignals(shown: showSignals))
syncState()
watchlistManager.showSignals = showSignals
}
}
@Published var showSignals: Bool

init() {
let premiumEnabled = purchaseManager.subscription != nil

sortBy = watchlistManager.sortBy
timePeriod = watchlistManager.timePeriod
showSignals = watchlistManager.showSignals
showSignals = premiumEnabled && watchlistManager.showSignals
self.premiumEnabled = premiumEnabled

watchlistManager.$timePeriod
.sink { [weak self] timePeriod in
self?.timePeriod = timePeriod
}
.store(in: &cancellables)

purchaseManager.$subscription
.receive(on: DispatchQueue.main)
.sink { [weak self] subscription in
self?.premiumEnabled = subscription != nil
self?.syncShowSignals()
}
.store(in: &cancellables)
}

private func syncShowSignals() {
showSignals = premiumEnabled && watchlistManager.showSignals
}

private func syncCoinUids() {
Expand Down Expand Up @@ -168,6 +179,14 @@ extension MarketWatchlistViewModel {
await _syncMarketInfos()
}

func set(showSignals: Bool) {
stat(page: .markets, section: .watchlist, event: .showSignals(shown: showSignals))
syncState()
watchlistManager.showSignals = showSignals

syncShowSignals()
}

func remove(coinUid: String) {
watchlistManager.remove(coinUid: coinUid)
stat(page: .markets, section: .watchlist, event: .removeFromWatchlist(coinUid: coinUid))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class MainSettingsPremiumCell: UITableViewCell {
private let tryForFreeLabel = UILabel()
private let boxImageView = UIImageView()

private let radialBackgroundView = RadialBackgroundView()
private let radialBackgroundView = RadialBackgroundView(background: .themeHelsing)

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
Expand Down
Loading

0 comments on commit 92f6406

Please sign in to comment.