Skip to content

Commit

Permalink
Simple UI Analytics (#343)
Browse files Browse the repository at this point in the history
* Help and Welcome

* FTU

* Analytic events

* User property

* Add generic routing events

* Remove SimpleUIPageName
  • Loading branch information
ruixhuang authored Jan 28, 2025
1 parent f01c262 commit d4179b7
Show file tree
Hide file tree
Showing 19 changed files with 176 additions and 88 deletions.
38 changes: 38 additions & 0 deletions dydx/dydxAnalytics/dydxAnalytics/AnalyticsEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,44 @@ public enum AnalyticsEventV2 {
self.type = type
}
}

public struct RoutingEvent: TrackableEvent {
let fromPath: String?
let toPath: String
let fromQuery: String?
let toQuery: String?

public var name: String { "RoutingEvent" }
public var customParameters: [String: Any] {[
"fromPath": fromPath ?? "nil",
"toPath": toPath,
"fromQuery": fromQuery ?? "nil",
"toQuery": toQuery ?? "nil"
]}

public init(fromPath: String? = nil, toPath: String, fromQuery: String? = nil, toQuery: String? = nil) {
self.fromPath = fromPath
self.toPath = toPath
self.fromQuery = fromQuery
self.toQuery = toQuery
}
}

public struct ModeSelectorEvent: TrackableEvent {
let fromMode: String
let toMode: String

public var name: String { "ModeSelectorEvent" }
public var customParameters: [String: Any] {[
"from": fromMode,
"to": toMode
]}

public init(fromMode: String, toMode: String) {
self.fromMode = fromMode
self.toMode = toMode
}
}
}

public extension TrackingProtocol {
Expand Down
1 change: 1 addition & 0 deletions dydx/dydxAnalytics/dydxAnalytics/UserProperty.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public enum UserProperty: String {
case statsigFlags
case statsigStableId
case pushNotificationsEnabled
case appMode
}

public extension TrackingProtocol {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import PlatformParticles
import RoutingKit
import ParticlesKit
import PlatformUI
import dydxAnalytics

public class dydxAppModeViewBuilder: NSObject, ObjectBuilderProtocol {
public func build<T>() -> T? {
Expand Down Expand Up @@ -51,12 +52,12 @@ private class dydxAppModeViewPresenter: HostedViewPresenter<dydxAppModeViewModel
if mode != self?.viewModel?.appMode {
self?.viewModel?.appMode = mode
AppMode.current = mode

self?.loadRoot()
}

self?.loadRoot()
}
viewModel?.onCancel = {
Router.shared?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil)
viewModel?.onCancel = { [weak self] in
self?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil)
}
}

Expand All @@ -71,8 +72,8 @@ private class dydxAppModeViewPresenter: HostedViewPresenter<dydxAppModeViewModel
}

private func loadRoot() {
Router.shared?.navigate(to: RoutingRequest(path: "/loading"), animated: true, completion: { _, _ in
Router.shared?.navigate(to: RoutingRequest(path: "/"), animated: true, completion: { _, _ in
navigate(to: RoutingRequest(path: "/loading"), animated: true, completion: { _, _ in
self.navigate(to: RoutingRequest(path: "/"), animated: true, completion: { _, _ in
})
})
}
Expand All @@ -87,7 +88,13 @@ public extension AppMode {
return nil
}
set {
SettingsStore.shared?.setValue(newValue?.rawValue, forDydxKey: .appMode)
if current != newValue {
let fromMode = current?.rawValue ?? "none"
let toMode = newValue?.rawValue ?? "none"
Tracking.shared?.log(event: AnalyticsEventV2.ModeSelectorEvent(fromMode: fromMode, toMode: toMode))

SettingsStore.shared?.setValue(newValue?.rawValue, forDydxKey: .appMode)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class dydxRootBuilder: NSObject, ObjectBuilderProtocol {
AbacusStateManager.shared.state.onboarded
.prefix(1)
.sink { onboarded in
Tracking.shared?.setUserProperty(AppMode.current?.rawValue, forUserProperty: .appMode)

if dydxBoolFeatureFlag.simple_ui.isEnabled {
if AppMode.current == nil {
if onboarded {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class dydxFirstTimeViewPresenter: HostedViewPresenter<dydxFirstTimeViewModel>, d
if !started {
started = true
let params = ["mode": "welcome"]
Router.shared?.navigate(to: RoutingRequest(path: "/onboard", params: params), animated: true, completion: nil)
navigate(to: RoutingRequest(path: "/onboard", params: params), animated: true, completion: nil)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ class dydxSimpleUIMarketBuySellViewPresenter: HostedViewPresenter<dydxSimpleUIMa

viewModel?.buyAction = { [weak self] in
guard let marketId = self?.marketId else { return }
Router.shared?.navigate(to: RoutingRequest(path: "/trade/simple",
params: ["side": "buy", "market": marketId]), animated: true, completion: nil)
self?.navigate(to: RoutingRequest(path: "/trade/simple",
params: ["side": "buy", "market": marketId]), animated: true, completion: nil)
}

viewModel?.sellAction = { [weak self] in
guard let marketId = self?.marketId else { return }
Router.shared?.navigate(to: RoutingRequest(path: "/trade/simple",
params: ["side": "sell", "market": marketId]), animated: true, completion: nil)
self?.navigate(to: RoutingRequest(path: "/trade/simple",
params: ["side": "sell", "market": marketId]), animated: true, completion: nil)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ class dydxSimpleUIMarketInfoHeaderViewPresenter: HostedViewPresenter<dydxSimpleU

override init() {
let viewModel = dydxSimpleUIMarketInfoHeaderViewModel()
viewModel.onBackButtonTap = {
Router.shared?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil)
}

marketPresenter.$viewModel.assign(to: &viewModel.$sharedMarketViewModel)

super.init()

self.viewModel = viewModel

viewModel.onBackButtonTap = { [weak self] in
self?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil)
}

$marketId.assign(to: &marketPresenter.$marketId)

attachChildren(workers: childPresenters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ class dydxSimpleUIMarketPositionViewPresenter: HostedViewPresenter<dydxSimpleUIM
viewModel?.amount = dydxFormatter.shared.dollar(number: position.notionalTotal.current?.doubleValue, digits: 2)
viewModel?.funding = SignedAmountViewModel(amount: position.netFunding?.doubleValue, displayType: .dollar, coloringOption: .allText)

viewModel?.closeAction = {
Router.shared?.navigate(to: RoutingRequest(path: "/trade/simple/close",
params: ["marketId": position.id]),
animated: true, completion: nil)
viewModel?.closeAction = { [weak self] in
self?.navigate(to: RoutingRequest(path: "/trade/simple/close",
params: ["marketId": position.id]),
animated: true, completion: nil)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import PlatformUI
import Combine
import dydxStateManager
import Abacus
import dydxAnalytics

public class dydxSimpleUIMarketInfoViewBuilder: NSObject, ObjectBuilderProtocol {
public func build<T>() -> T? {
Expand Down Expand Up @@ -78,12 +79,4 @@ private class dydxSimpleUIMarketInfoViewPresenter: HostedViewPresenter<dydxSimpl

attachChildren(workers: childPresenters)
}

private func floatTradeInput() {
if shouldDisplayFullTradeInputOnAppear {
Router.shared?.navigate(to: RoutingRequest(path: "/trade/input", params: ["full": "true", "market": marketId ?? ""]), animated: true, completion: nil)
} else {
Router.shared?.navigate(to: RoutingRequest(path: "/trade/input", params: ["market": marketId ?? ""]), animated: true, completion: nil)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,70 +46,75 @@ class dydxSimpleUIMarketsHeaderViewPresenter: HostedViewPresenter<dydxSimpleUIMa

let ethereumAddress = currentWallet?.ethereumAddress ?? ""
if onboarded {
viewModel?.items = [.transfers, .history, .settings, .help, .signOut(ethereumAddress: ethereumAddress), .switchMode]
viewModel?.depositAction = {
Router.shared?.navigate(to: RoutingRequest(path: "/transfer", params: ["section": TransferSection.deposit.rawValue]), animated: true, completion: nil)
viewModel?.items = [transfers, history, settings, help, signOut(ethereumAddress: ethereumAddress), switchMode]
viewModel?.depositAction = { [weak self] in
self?.navigate(to: RoutingRequest(path: "/transfer", params: ["section": TransferSection.deposit.rawValue]), animated: true, completion: nil)
}
// viewModel?.withdrawAction = {
// Router.shared?.navigate(to: RoutingRequest(path: "/transfer", params: ["section": TransferSection.withdrawal.rawValue]), animated: true, completion: nil)
// }
} else {
viewModel?.items = [.signIn, .settings, .help, .switchMode]
viewModel?.items = [signIn, settings, help, switchMode]
viewModel?.depositAction = nil
viewModel?.withdrawAction = nil
}
}
}

private extension dydxSimpleUIMarketsHeaderViewModel.MenuItem {

static let signIn = dydxSimpleUIMarketsHeaderViewModel.MenuItem(
private var signIn: dydxSimpleUIMarketsHeaderViewModel.MenuItem { dydxSimpleUIMarketsHeaderViewModel.MenuItem(
icon: "icon_wallet_connect",
title: DataLocalizer.localize(path: "APP.GENERAL.CONNECT_WALLET")) {
Router.shared?.navigate(to: RoutingRequest(path: "/onboard/wallets"), animated: true, completion: nil)
title: DataLocalizer.localize(path: "APP.GENERAL.CONNECT_WALLET")) { [weak self] in
self?.navigate(to: RoutingRequest(path: "/onboard/wallets"), animated: true, completion: nil)
}
}

static func signOut(ethereumAddress: String) -> Self {
private func signOut(ethereumAddress: String) -> dydxSimpleUIMarketsHeaderViewModel.MenuItem {
dydxSimpleUIMarketsHeaderViewModel.MenuItem(
icon: "icon_close",
title: DataLocalizer.localize(path: "APP.GENERAL.SIGN_OUT"),
destructive: true) {
Router.shared?.navigate(to: RoutingRequest(path: "/action/wallet/disconnect", params: ["ethereumAddress": ethereumAddress]), animated: true, completion: nil)
destructive: true) { [weak self] in
self?.navigate(to: RoutingRequest(path: "/action/wallet/disconnect", params: ["ethereumAddress": ethereumAddress]), animated: true, completion: nil)
}
}

static let settings = dydxSimpleUIMarketsHeaderViewModel.MenuItem(
icon: "icon_settings_1",
title: DataLocalizer.localize(path: "APP.EMAIL_NOTIFICATIONS.SETTINGS")) {
Router.shared?.navigate(to: RoutingRequest(url: "/settings"), animated: true, completion: nil)
private var settings: dydxSimpleUIMarketsHeaderViewModel.MenuItem { dydxSimpleUIMarketsHeaderViewModel.MenuItem(
icon: "icon_settings_1",
title: DataLocalizer.localize(path: "APP.EMAIL_NOTIFICATIONS.SETTINGS")) { [weak self] in
self?.navigate(to: RoutingRequest(url: "/settings"), animated: true, completion: nil)
}
}

static let history = dydxSimpleUIMarketsHeaderViewModel.MenuItem(
icon: "icon_clock",
title: DataLocalizer.localize(path: "APP.GENERAL.HISTORY")) {
Router.shared?.navigate(to: RoutingRequest(path: "/portfolio/history",
params: ["inTabBar": "false"]),
animated: true, completion: nil)
}
private var history: dydxSimpleUIMarketsHeaderViewModel.MenuItem {
dydxSimpleUIMarketsHeaderViewModel.MenuItem(
icon: "icon_clock",
title: DataLocalizer.localize(path: "APP.GENERAL.HISTORY")) { [weak self] in
self?.navigate(to: RoutingRequest(path: "/portfolio/history",
params: ["inTabBar": "false"]),
animated: true, completion: nil)
}
}

static let transfers = dydxSimpleUIMarketsHeaderViewModel.MenuItem(
private var transfers: dydxSimpleUIMarketsHeaderViewModel.MenuItem { dydxSimpleUIMarketsHeaderViewModel.MenuItem(
icon: "icon_transfer",
title: DataLocalizer.localize(path: "APP.GENERAL.TRANSFER")) {
Router.shared?.navigate(to: RoutingRequest(path: "/transfer"), animated: true, completion: nil)
title: DataLocalizer.localize(path: "APP.GENERAL.TRANSFER")) { [weak self] in
self?.navigate(to: RoutingRequest(path: "/transfer"), animated: true, completion: nil)
}
}

static let help = dydxSimpleUIMarketsHeaderViewModel.MenuItem(
icon: "icon_help",
title: DataLocalizer.localize(path: "APP.HEADER.HELP")) {
Router.shared?.navigate(to: RoutingRequest(path: "/help"), animated: true, completion: nil)
}
private var help: dydxSimpleUIMarketsHeaderViewModel.MenuItem {
dydxSimpleUIMarketsHeaderViewModel.MenuItem(
icon: "icon_help",
title: DataLocalizer.localize(path: "APP.HEADER.HELP")) { [weak self] in
self?.navigate(to: RoutingRequest(path: "/help"), animated: true, completion: nil)
}
}

static let switchMode = dydxSimpleUIMarketsHeaderViewModel.MenuItem(
private var switchMode: dydxSimpleUIMarketsHeaderViewModel.MenuItem { dydxSimpleUIMarketsHeaderViewModel.MenuItem(
icon: "icon_switch",
title: DataLocalizer.localize(path: "APP.TRADE.MODE.SWITCH_TO_PRO"),
subtitle: DataLocalizer.localize(path: "APP.TRADE.MODE.FULLY_FEATURED")) {
Router.shared?.navigate(to: RoutingRequest(path: "/action/mode/switch",
params: ["mode": "pro"]),
animated: true, completion: nil)
subtitle: DataLocalizer.localize(path: "APP.TRADE.MODE.FULLY_FEATURED")) { [weak self] in
self?.navigate(to: RoutingRequest(path: "/action/mode/switch",
params: ["mode": "pro"]),
animated: true, completion: nil)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import Utilities
import dydxViews
import PlatformParticles
import RoutingKit
import ParticlesKit
import PlatformUI
import dydxStateManager
Expand Down Expand Up @@ -68,12 +67,12 @@ class dydxSimpleUIPortfolioViewPresenter: HostedViewPresenter<dydxSimpleUIPortfo
if onboarded {
self?.viewModel?.state = .walletConnected
self?.viewModel?.buttonAction = {
Router.shared?.navigate(to: RoutingRequest(path: "/transfer"), animated: true, completion: nil)
self?.navigate(to: RoutingRequest(path: "/transfer"), animated: true, completion: nil)
}
} else {
self?.viewModel?.state = .loggedOut
self?.viewModel?.buttonAction = {
Router.shared?.navigate(to: RoutingRequest(path: "/onboard"), animated: true, completion: nil)
self?.navigate(to: RoutingRequest(path: "/onboard"), animated: true, completion: nil)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ class dydxSimpleUIPositionListViewPresenter: HostedViewPresenter<dydxSimpleUIPos
if position == nil {
return nil
}
return dydxSimpleUIMarketViewModel.createFrom(displayType: .position, market: market, asset: asset, position: position) {
Router.shared?.navigate(to: RoutingRequest(path: "/market", params: ["market": market.id]), animated: true, completion: nil)
return dydxSimpleUIMarketViewModel.createFrom(displayType: .position, market: market, asset: asset, position: position) { [weak self] in
self?.navigate(to: RoutingRequest(path: "/market", params: ["market": market.id]), animated: true, completion: nil)
}
}
.sorted { lhs, rhs in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ParticlesKit
import PlatformUI
import dydxStateManager
import Abacus
import dydxAnalytics

public class dydxSimpleUIMarketsViewBuilder: NSObject, ObjectBuilderProtocol {
public func build<T>() -> T? {
Expand Down Expand Up @@ -61,14 +62,15 @@ public class dydxSimpleUIMarketsViewPresenter: HostedViewPresenter<dydxSimpleUIM

self.viewModel = viewModel

viewModel.searchAction = {
Router.shared?.navigate(to: RoutingRequest(path: "/markets/search"),
animated: true,
completion: nil)
viewModel.searchAction = { [weak self] in
self?.navigate(to: RoutingRequest(path: "/markets/search"),
animated: true, completion: nil)
}

marketListPresenter.onMarketSelected = { marketId in
Router.shared?.navigate(to: RoutingRequest(path: "/market", params: ["market": marketId]), animated: true, completion: nil)
marketListPresenter.onMarketSelected = { [weak self] marketId in
self?.navigate(to: RoutingRequest(path: "/market",
params: ["market": marketId]),
animated: true, completion: nil)
}

attachChildren(workers: childPresenters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ private class dydxSimpleUIMarketSearchViewPresenter: HostedViewPresenter<dydxSim
self?.marketListPresenter.searchText = text
}

marketListPresenter.onMarketSelected = { marketId in
Router.shared?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true) { _, _ in
Router.shared?.navigate(to: RoutingRequest(path: "/market", params: ["market": marketId]), animated: true, completion: nil)
marketListPresenter.onMarketSelected = { [weak self] marketId in
self?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true) { _, _ in
self?.navigate(to: RoutingRequest(path: "/market", params: ["market": marketId]), animated: true, completion: nil)
}
}

Expand Down
Loading

0 comments on commit d4179b7

Please sign in to comment.