[ISSUE #3537] Added mixpanel support

Signed-off-by: Julien Eluard <julien.eluard@gmail.com>
This commit is contained in:
Julien Eluard 2018-03-16 18:50:17 +01:00
parent 581d4f04a5
commit f1e41cc7ab
No known key found for this signature in database
GPG Key ID: 6FD7DB5437FCBEF6
28 changed files with 408 additions and 138 deletions

1
.env
View File

@ -13,3 +13,4 @@ RN_BRIDGE_THRESHOLD_WARNINGS=0
COMPILE_VIEWS_ENABLED=0
POW_TARGET=0.001
POW_TIME=1
MIXPANEL_TOKEN=3f2e1a8970f159aa2a3d5dc5d65eab38

View File

@ -11,5 +11,6 @@ QUEUE_MESSAGE_ENABLED=1
MANY_WHISPER_TOPICS_ENABLED=0
RN_BRIDGE_THRESHOLD_WARNINGS=0
COMPILE_VIEWS_ENABLED=0
MIXPANEL_TOKEN=3f2e1a8970f159aa2a3d5dc5d65eab38
POW_TARGET=0.001
POW_TIME=1

View File

@ -11,5 +11,6 @@ QUEUE_MESSAGE_ENABLED=0
MANY_WHISPER_TOPICS_ENABLED=0
RN_BRIDGE_THRESHOLD_WARNINGS=0
COMPILE_VIEWS_ENABLED=0
MIXPANEL_TOKEN=2584e00100d319d12e538cc4d0fa9fc1
POW_TARGET=0.2
POW_TIME=1

View File

@ -12,7 +12,7 @@
(defn- generate-context
"Generates context for jail call"
[current-account-id chat-id to group-id]
(merge {:platform platform/platform
(merge {:platform platform/os
:from current-account-id
:to to
:chat {:chat-id chat-id

View File

@ -109,7 +109,7 @@
::account-created
[re-frame/trim-v (re-frame/inject-cofx :get-new-keypair!)
(re-frame/inject-cofx ::get-signing-phrase) (re-frame/inject-cofx ::get-status)]
(fn [{:keys [keypair signing-phrase status db] :as cofx} [{:keys [pubkey address mnemonic]} password]]
(fn [{:keys [keypair signing-phrase status db]} [{:keys [pubkey address mnemonic]} password]]
(let [normalized-address (utils.hex/normalize-hex address)
account {:public-key pubkey
:address normalized-address
@ -208,6 +208,12 @@
:dispatch [:navigate-to-clean :usage-data]}
(account-update {:name (:name create)}))))
(handlers/register-handler-fx
:account-finalized
(fn [{db :db} _]
{:db db
:dispatch [:navigate-to-clean :home]}))
(handlers/register-handler-fx
:update-sign-in-time
(fn [{db :db now :now} _]
@ -221,4 +227,4 @@
(handlers/register-handler-fx
:switch-dev-mode
(fn [{db :db} [_ dev-mode]]
(account-update {:db db} {:dev-mode? dev-mode})))
(account-update {:db db} {:dev-mode? dev-mode})))

View File

@ -120,13 +120,14 @@
(register-handler-fx
:change-account-handler
(fn [{{:keys [view-id] :as db} :db} [_ error address]]
(fn [{{:keys [accounts/accounts view-id] :as db} :db} [_ error address]]
(if (nil? error)
{:db (cond-> (dissoc db :accounts/login)
(= view-id :create-account)
(assoc-in [:accounts/create :step] :enter-name))
:dispatch-n (concat
[[:stop-debugging]
(when (:sharing-usage-data? (accounts address)) [:register-mixpanel-tracking address])
[:initialize-account address
(when (not= view-id :create-account)
[[:navigate-to-clean :home]])]])}

View File

@ -5,7 +5,6 @@
[clojure.string :as s]
[status-im.protocol.core :as protocol]
[status-im.utils.contacts :as utils.contacts]
[status-im.utils.utils :refer [http-post]]
[status-im.utils.random :as random]
[taoensso.timbre :as log]
[cljs.reader :refer [read-string]]

View File

@ -34,12 +34,15 @@
[status-im.js-dependencies :as dependencies]
[status-im.ui.screens.db :refer [app-db]]
[status-im.utils.datetime :as time]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.random :as random]
[status-im.utils.config :as config]
[status-im.utils.crypt :as crypt]
[status-im.utils.notifications :as notifications]
[status-im.utils.handlers :as handlers]
[status-im.utils.http :as http]
[status-im.utils.instabug :as inst]
[status-im.utils.mixpanel :as mixpanel]
[status-im.utils.platform :as platform]
[status-im.utils.types :as types]
[status-im.utils.utils :as utils]
@ -113,14 +116,14 @@
(let [on-success #(re-frame/dispatch (success-event-creator %))
on-error #(re-frame/dispatch (failure-event-creator %))
opts {:timeout-ms timeout-ms}]
(utils/http-post action data on-success on-error opts))))
(http/post action data on-success on-error opts))))
(defn- http-get [{:keys [url response-validator success-event-creator failure-event-creator timeout-ms]}]
(let [on-success #(re-frame/dispatch (success-event-creator %))
on-error #(re-frame/dispatch (failure-event-creator %))
opts {:valid-response? response-validator
:timeout-ms timeout-ms}]
(utils/http-get url on-success on-error opts)))
(http/get url on-success on-error opts)))
(re-frame/reg-fx
:http-get
@ -204,7 +207,6 @@
:close-application
(fn [] (status/close-application)))
;;;; Handlers
(handlers/register-handler-db
@ -220,13 +222,13 @@
(handlers/register-handler-fx
:initialize-app
(fn [_ _]
{::testfairy-alert nil
:dispatch-n [[:initialize-db]
[:load-accounts]
[:initialize-views]
[:listen-to-network-status]
[:initialize-crypt]
[:initialize-geth]]}))
{::testfairy-alert nil
:dispatch-n [[:initialize-db]
[:load-accounts]
[:initialize-views]
[:listen-to-network-status]
[:initialize-crypt]
[:initialize-geth]]}))
(handlers/register-handler-fx
:initialize-db
@ -250,8 +252,7 @@
status-module-initialized? status-node-started?
inbox/wnode]
:or [network (get app-db :network)
wnode (get app-db :inbox/wnode)]
:as db} [_ address]]
wnode (get app-db :inbox/wnode)]} [_ address]]
(let [console-contact (get contacts console-chat-id)]
(cond-> (assoc app-db
:access-scope->commands-responses access-scope->commands-responses
@ -332,6 +333,25 @@
(fn [_ _]
{::get-fcm-token-fx nil}))
(defn- track [id event]
(let [anonid (ethereum/sha3 id)]
(doseq [{:keys [label properties]} (mixpanel/matching-events event mixpanel/event-by-trigger)]
(mixpanel/track anonid label properties))))
(def hook-id :mixpanel-callback)
(handlers/register-handler-fx
:register-mixpanel-tracking
(fn [_ [_ id]]
(re-frame/add-post-event-callback hook-id #(track id %))
nil))
(handlers/register-handler-fx
:unregister-mixpanel-tracking
(fn []
(re-frame/remove-post-event-callback hook-id)
nil))
;; Because we send command to jail in params and command `:ref` is a lookup vector with
;; keyword in it (for example `["transactor" :command 51 "send"]`), we lose that keyword
;; information in the process of converting to/from JSON, and we need to restore it

View File

@ -107,20 +107,22 @@
:icon-content [components.common/counter {:size 22} 1]}]
[profile.components/settings-item-separator]])])
(defn navigate-to-accounts []
(defn navigate-to-accounts [sharing-usage-data?]
;; TODO(rasom): probably not the best place for this call
(protocol/stop-whisper!)
(re-frame/dispatch [:navigate-to :accounts]))
(re-frame/dispatch [:navigate-to :accounts])
(when sharing-usage-data?
(re-frame/dispatch [:unregister-mixpanel-tracking])))
(defn handle-logout []
(defn handle-logout [sharing-usage-data?]
(utils/show-confirmation (i18n/label :t/logout-title)
(i18n/label :t/logout-are-you-sure)
(i18n/label :t/logout) navigate-to-accounts))
(i18n/label :t/logout) #(navigate-to-accounts sharing-usage-data?)))
(defn logout []
(defn logout [sharing-usage-data?]
[react/view {}
[react/touchable-highlight
{:on-press handle-logout
{:on-press #(handle-logout sharing-usage-data?)
:accessibility-label :log-out-button}
[react/view profile.components.styles/settings-item
[react/text {:style styles/logout-text
@ -159,7 +161,7 @@
:action-fn #(re-frame/dispatch [:switch-dev-mode %])}]])]))
(defview my-profile []
(letsubs [{:keys [public-key] :as current-account} [:get-current-account]
(letsubs [{:keys [public-key sharing-usage-data?] :as current-account} [:get-current-account]
editing? [:get :my-profile/editing?]
changed-account [:get :my-profile/profile]]
(let [shown-account (merge current-account changed-account)]
@ -174,5 +176,5 @@
[share-contact-code current-account public-key]]
[react/view styles/my-profile-info-container
[my-profile-settings current-account]]
[logout]
[advanced shown-account]]])))
[logout sharing-usage-data?]
[advanced shown-account]]])))

View File

@ -1,16 +1,13 @@
(ns status-im.ui.screens.qr-scanner.styles
(:require [status-im.ui.components.styles :refer [color-white]]
[status-im.ui.components.toolbar.styles :as toolbar.styles]
[status-im.utils.platform :as p]))
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.components.toolbar.styles :as toolbar.styles]))
(def barcode-scanner-container
{:flex 1
:background-color :white})
(def barcode-scanner
{:flex 1
:justify-content :flex-end
:align-items :center})
{:flex 1})
(def rectangle-container
{:position :absolute
@ -73,5 +70,5 @@
(def import-text
{:flex 1
:flex-direction :column
:color color-white
:color colors/white
:margin-left 8})

View File

@ -4,7 +4,9 @@
(handlers/register-handler-fx
:help-improve-handler
(fn [{db :db} [_ yes?]]
(fn [{db :db} [_ yes? address]]
(merge (when yes?
(accounts/account-update {:db db} {:sharing-usage-data? true}))
{:dispatch [:navigate-to-clean :home]})))
{:dispatch-n [(when yes? [:register-mixpanel-tracking address])
[:account-finalized]]})))

View File

@ -1,5 +1,5 @@
(ns status-im.ui.screens.usage-data.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.components.react :as react]
[re-frame.core :as re-frame]
[status-im.react-native.resources :as resources]
@ -8,23 +8,24 @@
[status-im.i18n :as i18n]
[status-im.ui.components.status-bar.view :as status-bar]))
(defview usage-data []
[react/view {:style styles/usage-data-view}
[status-bar/status-bar {:flat? true}]
[react/view {:style styles/logo-container}
[components.common/logo styles/logo]
[react/image {:source (:analytics-image resources/ui)
:style styles/usage-data-image}]]
[react/text {:style styles/help-improve-text}
(i18n/label :t/help-improve)]
[react/view
[react/text {:style styles/help-improve-text-description}
(i18n/label :t/help-improve-description)]]
[react/view styles/buttons-container
[components.common/button {:style {:flex-direction :row}
:on-press #(re-frame/dispatch [:help-improve-handler true])
:label (i18n/label :t/share-usage-data)}]
[react/view styles/bottom-button-container
[components.common/button {:on-press #(re-frame/dispatch [:help-improve-handler false])
:label (i18n/label :t/dont-want-to-share)
:background? false}]]]])
(views/defview usage-data []
(views/letsubs [account [:get-current-account]]
[react/view {:style styles/usage-data-view}
[status-bar/status-bar {:flat? true}]
[react/view {:style styles/logo-container}
[components.common/logo styles/logo]
[react/image {:source (:analytics-image resources/ui)
:style styles/usage-data-image}]]
[react/text {:style styles/help-improve-text}
(i18n/label :t/help-improve)]
[react/view
[react/text {:style styles/help-improve-text-description}
(i18n/label :t/help-improve-description)]]
[react/view styles/buttons-container
[components.common/button {:style {:flex-direction :row}
:on-press #(re-frame/dispatch [:help-improve-handler true (:address account)])
:label (i18n/label :t/share-usage-data)}]
[react/view styles/bottom-button-container
[components.common/button {:on-press #(re-frame/dispatch [:help-improve-handler false (:address account)])
:label (i18n/label :t/dont-want-to-share)
:background? false}]]]]))

View File

@ -1,13 +1,12 @@
(ns status-im.utils.build
(:require [cljs.analyzer :as analyzer]))
(:require [cljs.analyzer :as analyzer]
[clojure.java.shell :as shell]))
;; Some warnings are unavoidable due to dependencies. For example, reagent 0.6.0
;; has a warning in its util.cljs namespace. Adjust this as is necessary and
;; unavoidable warnings arise.
(def acceptable-warning?
#{
"Protocol IFn implements method -invoke with variadic signature (&)" ;; reagent 0.6.0 reagent/impl/util.cljs:61
})
#{"Protocol IFn implements method -invoke with variadic signature (&)"}) ;; reagent 0.6.0 reagent/impl/util.cljs:61
(defn nil-acceptable-warning [s]
(when-not (acceptable-warning? s)
@ -19,3 +18,6 @@
(binding [*out* *err*]
(println (analyzer/message env (str "\u001B[31mWARNING\u001B[0m: " s))))
(System/exit 1))))
(defmacro git-short-version []
(:out (shell/sh "bash" "-c" "git describe --always")))

View File

@ -0,0 +1,4 @@
(ns status-im.utils.build
(:require-macros [status-im.utils.build :refer [git-short-version]]))
(def version (git-short-version))

View File

@ -33,6 +33,7 @@
(def many-whisper-topics-enabled? (enabled? (get-config :MANY_WHISPER_TOPICS_ENABLED 0)))
(def rn-bridge-threshold-warnings-enabled? (enabled? (get-config :RN_BRIDGE_THRESHOLD_WARNINGS 0)))
(def compile-views-enabled? (enabled? (get-config :COMPILE_VIEWS_ENABLED 0)))
(def mixpanel-token (get-config :MIXPANEL_TOKEN))
(def pow-target (js/parseFloat (get-config :POW_TARGET "0.001")))
(def pow-time (js/parseInt (get-config :POW_TIME "1")))

View File

@ -18,6 +18,12 @@
(handler db params)
db))
(defn- pretty-print-event [ctx]
(let [[first second] (get-coeffect ctx :event)]
(if (map? second)
first
(str first " " second))))
(def debug-handlers-names
"Interceptor which logs debug information to js/console for each event."
(->interceptor
@ -26,13 +32,12 @@
[context]
(when @pre-event-callback
(@pre-event-callback (get-coeffect context :event)))
(log/debug "Handling re-frame event: " (first (get-coeffect context :event)))
(log/debug "Handling re-frame event: " (pretty-print-event context))
context)))
(defn- check-spec-msg-path-problem [problem]
(let [pred (:pred problem)]
(str "Spec: " (-> problem :via last) "\n"
"Predicate: " (subs (str (:pred problem)) 0 50))))
(str "Spec: " (-> problem :via last) "\n"
"Predicate: " (subs (str (:pred problem)) 0 50)))
(defn- check-spec-msg-path-problems [path path-problems]
(str "Key path: " path "\n"

View File

@ -0,0 +1,61 @@
(ns status-im.utils.http
(:require [status-im.constants :as const]
[status-im.utils.utils :as utils]
[status-im.react-native.js-dependencies :as rn-dependencies])
(:refer-clojure :exclude [get]))
;; Default HTTP request timeout ms
(def http-request-default-timeout-ms 3000)
(defn post
"Performs an HTTP POST request"
([action data on-success]
(post action data on-success nil))
([action data on-success on-error]
(post action data on-success on-error nil))
([action data on-success on-error {:keys [timeout-ms]}]
(-> (rn-dependencies/fetch (str const/server-address action)
(clj->js {:method "POST"
:headers {:accept "application/json"
:content-type "application/json"}
:body (.stringify js/JSON (clj->js data))
:timeout (or timeout-ms http-request-default-timeout-ms)}))
(.then (fn [response]
(.text response)))
(.then (fn [text]
(let [json (.parse js/JSON text)
obj (js->clj json :keywordize-keys true)]
(on-success obj))))
(.catch (or on-error
(fn [error]
(utils/show-popup "Error" (str error))))))))
(defn get
"Performs an HTTP GET request"
([url] (get url nil))
([url on-success] (get url on-success nil))
([url on-success on-error]
(get url on-success on-error nil))
([url on-success on-error {:keys [valid-response? timeout-ms]}]
(-> (rn-dependencies/fetch url
(clj->js {:method "GET"
:headers {"Cache-Control" "no-cache"}
:timeout (or timeout-ms http-request-default-timeout-ms)}))
(.then (fn [response]
(let [ok? (.-ok response)
ok?' (if valid-response?
(and ok? (valid-response? response))
ok?)]
[(.-_bodyText response) ok?'])))
(.then (fn [[response ok?]]
(cond
(and on-success ok?)
(on-success response)
(and on-error (not ok?))
(on-error response)
:else false)))
(.catch (or on-error
(fn [error]
(utils/show-popup "Error" (str error))))))))

View File

@ -0,0 +1,48 @@
(ns status-im.utils.mixpanel
(:require-macros [status-im.utils.slurp :as slurp])
(:require [cljs.reader :as reader]
[goog.crypt.base64 :as b64]
[status-im.utils.build :as build]
[status-im.utils.config :as config]
[status-im.utils.datetime :as datetime]
[status-im.utils.http :as http]
[status-im.utils.platform :as platform]
[status-im.utils.types :as types]))
(def base-url "http://api.mixpanel.com/")
(def base-track-url (str base-url "track/"))
(defn encode [m]
(b64/encodeString (types/clj->json m)))
(defn- build-url [id label props]
(str base-track-url
"?data="
(encode {:event label
:properties (merge
{:token config/mixpanel-token
:distinct_id id
:os platform/os
:os-version platform/version
:app-version build/version
:time (datetime/timestamp)}
props)})))
(defn track [id label props]
(http/get (build-url id label props)))
;; Mixpanel events definition
(def events (reader/read-string (slurp/slurp "./src/status_im/utils/mixpanel_events.edn")))
(def event-by-trigger (reduce-kv #(assoc %1 (:trigger %3) %3) {} events))
(defn matches? [event trigger]
(if (= 1 (count trigger))
(= (first event) (first trigger))
(and
(= (first event) (first trigger))
(= (second event) (second trigger)))))
(defn matching-events [event definitions]
(reduce-kv #(if (matches? event %2) (conj %1 %3) %1) [] definitions))

View File

@ -0,0 +1,146 @@
;; This file is supposed to be edited by Chad.
;; Chad loves mixpanel. When Chad was a child he dreamed of being a mixpanel tamer.
[;; Account creation
{:label "Account created"
:trigger [:account-finalized]}
{:label "Account restored"
:trigger [:account-recovered]}
;; Account lifecycle
{:label "Login"
:trigger [:initialize-account]}
{:label "Logout"
:trigger [:navigate-to :accounts]}
;; Tab navigation
{:label "Tap"
:trigger [:navigate-to-clean :home]
:properties {:target :home}}
{:label "Tap"
:trigger [:navigate-to-tab :home]
:properties {:target :home}}
{:label "Tap"
:trigger [:navigate-to-tab :my-profile]
:properties {:target :my-profile}}
{:label "Tap"
:trigger [:navigate-to-clean :wallet]
:properties {:target :wallet}}
{:label "Tap"
:trigger [:navigate-to-tab :wallet]
:properties {:target :wallet}}
;; New
{:label "Tap"
:trigger [:navigate-to-tab :new]
:properties {:target :new}}
{:label "Tap"
:trigger [:navigate-to :new-chat]
:properties {:target :new-chat}}
{:label "Tap"
:trigger [:scan-qr-code]
:properties {:target :new-chat-qr-code}}
{:label "Tap"
:trigger [:show-profile]
:properties {:target :show-profile}}
{:label "Tap"
:trigger [:open-contact-toggle-list :chat-group]
:properties {:target :new-group-chat}}
{:label "Tap"
:trigger [:navigate-to :new-public-chat]
:properties {:target :new-public-chat}}
{:label "Tap"
:trigger [:navigate-to :open-dapp]
:properties {:target :open-dapp}}
{:label "Tap"
:trigger [:navigate-to :dapp-description]
:properties {:target :open-dapp-description}}
{:label "Tap"
:trigger [:open-dapp-in-browser]
:properties {:target :open-selected-dapp}}
{:label "Tap"
:trigger [:navigate-to :new-group]
:properties {:target :start-group-chat-next}}
{:label "Tap"
:trigger [:create-new-public-chat]
:properties {:target :create-public-chat}}
{:label "Tap"
:trigger [:navigate-to :new-public-chat]
:properties {:target :join-public-chat}}
;; Chat
{:label "Tap"
:trigger [:navigate-to-chat]
:properties {:target :open-existing-chat}}
{:label "Tap"
:trigger [:navigate-to :browser]
:properties {:target :open-existing-dapp}}
{:label "Tap"
:trigger [:start-chat]
:properties {:target :start-chat}}
{:label "Tap"
:trigger [:send-current-message]
:properties {:target :send-current-message}}
;; Wallet
{:label "Tap"
:trigger [:navigate-to :wallet-send-transaction]
:properties {:target :wallet-send-transaction}}
{:label "Tap"
:trigger [:navigate-to :wallet-request-transaction]
:properties {:target :wallet-request-transaction}}
{:label "Tap"
:trigger [:navigate-to :transactions-history]
:properties {:target :transactions-history}}
{:label "Tap"
:trigger [:navigate-to :recent-recipients]
:properties {:target :select-recipient
:type :recent-recipients}}
{:label "Tap"
:trigger [:navigate-to :recipient-qr-code]
:properties {:target :select-recipient
:type :recipient-qr-code}}
{:label "Tap"
:trigger [:navigate-to :contact-code]
:properties {:target :select-recipient
:type :contact-code}}
{:label "Tap"
:trigger [:navigate-to :wallet-send-assets]
:properties {:target :wallet-send-assets}}
{:label "Tap"
:trigger [:wallet.send/toggle-advanced]
:properties {:target :wallet-advanced}}
{:label "Tap"
:trigger [:wallet.send/set-signing?]
:properties {:target :wallet-open-sign-transaction}}
{:label "Tap"
:trigger [:wallet/sign-transaction]
:properties {:target :wallet-sign-transaction}}
{:label "Tap"
:trigger [:navigate-to-clean :wallet]
:properties {:target :wallet-got-it}}
{:label "Tap"
:trigger [:navigate-to :wallet-transaction-sent]
:properties {:target :wallet-transaction-sent}}
;;Profile
{:label "Tap"
:trigger [:my-profile/start-editing-profile]
:properties {:target :edit-profile}}
{:label "Tap"
:trigger [:my-profile/update-picture]
:properties {:target :edit-image
:type :gallery}}
{:label "Tap"
:trigger [:navigate-to :profile-photo-capture]
:properties {:target :edit-image
:type :capture}}
{:label "Tap"
:trigger [:navigate-to :profile-qr-viewer]
:properties {:target :share-contact-code}}]

View File

@ -4,11 +4,18 @@
[status-im.react-native.js-dependencies :as rn-dependencies]))
(def platform
(when-let [pl (.-Platform rn-dependencies/react-native)]
(.-OS pl)))
(.-Platform rn-dependencies/react-native))
(def android? (= platform "android"))
(def ios? (= platform "ios"))
(def os
(when platform
(.-OS platform)))
(def version
(when platform
(.-Version platform)))
(def android? (= os "android"))
(def ios? (= os "ios"))
(def iphone-x? (and ios? (ios/iphone-x-dimensions?)))
(def platform-specific

View File

@ -1,5 +1,5 @@
(ns status-im.utils.prices
(:require [status-im.utils.utils :as utils]
(:require [status-im.utils.http :as http]
[status-im.utils.types :as types]))
;; Responsible for interacting with Cryptocompare API to get current prices for
@ -25,7 +25,7 @@
:last-day (:OPEN24HOUR entry)}))
(defn get-prices [from to on-success on-error]
(utils/http-get
(http/get
(gen-price-url from to)
(fn [resp] (on-success (format-price-resp from to resp)))
on-error))

View File

@ -4,10 +4,10 @@
(defn wrap-first-time
"Allows to avoid
\"Use of undeclared Var status-im.utils.platform/platform\"
\"Use of undeclared Var status-im.utils.platform/os\"
warning. When defstyle or defnstyle is called first time status-im.utils.platform
namespace will be explicitly required so that clojurescript compiler will compile
it before using status-im.utils.platform/platform in macro"
it before using status-im.utils.platform/os in macro"
[body]
`(do
~@[(when @first-time
@ -18,7 +18,7 @@
(defn body [style]
`(let [style# ~style
common# (dissoc style# :android :ios)
platform# (keyword status-im.utils.platform/platform)
platform# (keyword status-im.utils.platform/os)
platform-specific# (get style# platform#)]
(if platform-specific#
(merge common# platform-specific#)

View File

@ -1,5 +1,5 @@
(ns status-im.utils.transactions
(:require [status-im.utils.utils :as utils]
(:require [status-im.utils.http :as http]
[status-im.utils.types :as types]))
(defn- get-network-subdomain [network]
@ -52,6 +52,6 @@
{})))
(defn get-transactions [network account on-success on-error]
(utils/http-get (get-transaction-url network account)
#(on-success (format-transactions-response % account))
on-error))
(http/get (get-transaction-url network account)
#(on-success (format-transactions-response % account))
on-error))

View File

@ -5,9 +5,6 @@
(name s)
s))
(defn to-edn-string [value]
(with-out-str (pr value)))
(defn clj->json [data]
(.stringify js/JSON (clj->js data)))

View File

@ -1,12 +1,7 @@
(ns status-im.utils.utils
(:require [status-im.constants :as const]
[status-im.i18n :as i18n]
[clojure.string :as str]
(:require [status-im.i18n :as i18n]
[status-im.react-native.js-dependencies :as rn-dependencies]))
;; Default HTTP request timeout ms
(def http-request-default-timeout-ms 3000)
(defn show-popup [title content]
(.alert (.-Alert rn-dependencies/react-native)
title
@ -47,56 +42,6 @@
:onPress on-accept
:accessibility-label :yes-button})))))
(defn http-post
"Performs an HTTP POST request"
([action data on-success]
(http-post action data on-success nil))
([action data on-success on-error]
(http-post action data on-success on-error nil))
([action data on-success on-error {:keys [timeout-ms] :as opts}]
(-> (rn-dependencies/fetch (str const/server-address action)
(clj->js {:method "POST"
:headers {:accept "application/json"
:content-type "application/json"}
:body (.stringify js/JSON (clj->js data))
:timeout (or timeout-ms http-request-default-timeout-ms)}))
(.then (fn [response]
(.text response)))
(.then (fn [text]
(let [json (.parse js/JSON text)
obj (js->clj json :keywordize-keys true)]
(on-success obj))))
(.catch (or on-error
(fn [error]
(show-popup "Error" (str error))))))))
(defn http-get
"Performs an HTTP GET request"
([url on-success on-error]
(http-get url on-success on-error nil))
([url on-success on-error {:keys [valid-response? timeout-ms] :as opts}]
(-> (rn-dependencies/fetch url
(clj->js {:method "GET"
:headers {"Cache-Control" "no-cache"}
:timeout (or timeout-ms http-request-default-timeout-ms)}))
(.then (fn [response]
(let [ok? (.-ok response)
ok?' (if valid-response?
(and ok? (valid-response? response))
ok?)]
[(.-_bodyText response) ok?'])))
(.then (fn [[response ok?]]
(cond
ok? (on-success response)
(and on-error (not ok?))
(on-error response)
:else false)))
(.catch (or on-error
(fn [error]
(show-popup "Error" (str error))))))))
;; background-timer
(defn set-timeout [cb ms]

View File

@ -36,6 +36,7 @@
(def snoopy-bars #js {:default #js {}})
(def snoopy-buffer #js {:default #js {}})
(def EventEmmiter #js {})
(def fetch #js {})
(def background-timer #js {:setTimeout js/setTimeout
:setInterval js/setInterval

View File

@ -23,7 +23,8 @@
[status-im.test.utils.signing-phrase.core]
[status-im.test.utils.transducers]
[status-im.test.utils.async]
[status-im.test.utils.datetime]))
[status-im.test.utils.datetime]
[status-im.test.utils.mixpanel]))
(enable-console-print!)
@ -57,4 +58,5 @@
'status-im.test.utils.gfycat.core
'status-im.test.utils.signing-phrase.core
'status-im.test.utils.transducers
'status-im.test.utils.datetime)
'status-im.test.utils.datetime
'status-im.test.utils.mixpanel)

View File

@ -0,0 +1,20 @@
(ns status-im.test.utils.mixpanel
(:require [cljs.test :refer-macros [deftest is testing async]]
[status-im.utils.mixpanel :as mixpanel]))
(deftest events
(is (not (nil? mixpanel/events))))
(deftest matches?
(is (true? (mixpanel/matches? [:key] [:key])))
(is (false? (mixpanel/matches? [:key1] [:key2])))
(is (true? (mixpanel/matches? [:key :subkey] [:key])))
(is (false? (mixpanel/matches? [:key] [:key :subkey]))))
(def definitions {[:key] {:trigger [:key]} [:key :subkey] {:trigger [:key :subkey]}})
(deftest matching-event
(is (empty? (mixpanel/matching-events [:non-existing] definitions)))
(is (= 1 (count (mixpanel/matching-events [:key] definitions))))
(is (= 2 (count (mixpanel/matching-events [:key :subkey] definitions))))
(is (empty? (mixpanel/matching-events [:key1 :another-subkey] definitions))))