diff --git a/src/status_im/utils/mixpanel.cljs b/src/status_im/utils/mixpanel.cljs index 5d68ed9c12..26eb9da9b7 100644 --- a/src/status_im/utils/mixpanel.cljs +++ b/src/status_im/utils/mixpanel.cljs @@ -9,7 +9,8 @@ [status-im.utils.http :as http] [status-im.utils.platform :as platform] [status-im.utils.types :as types] - [taoensso.timbre :as log])) + [taoensso.timbre :as log] + [status-im.utils.mixpanel-events :as mixpanel-events])) (def base-url "http://api.mixpanel.com/") (def base-track-url (str base-url "track/")) @@ -61,8 +62,8 @@ (recur (conj accumulator event)) accumulator))] (async/go - (doseq [batch (partition-all max-batch-size events)] - (async/triggers + "Transform definitions vector into map which will be used for matching later -(defn matches? [event trigger] - (cond (= 1 (count trigger)) - (= (first event) (first trigger)) - (= 2 (count trigger)) - (and - (= (first event) (first trigger)) - (= (second event) (second trigger))) - :else - (= event trigger))) + [{:trigger [:en1]} + {:trigger [:en1 :p1]} + {:trigger [:en2 :p1]} + {:trigger [:en3 :p1 :p2 :p3]}] -(defn matching-events [event definitions] - (reduce-kv #(if (matches? event %2) (conj %1 %3) %1) [] definitions)) + will be transformed into + {:en1 {events ({:trigger [:en1]}) + :p1 {events ({:trigger [:en1 :p1]})}} + :en2 {:p1 {events ({:trigger [:en2 :p1]})}} + :en3 {:p1 {:p2 {:p3 {events ({:trigger [:en3 :p1 :p2 :p3]})}}}}}" + [events] + (reduce (fn [m {:keys [trigger] :as event}] + (update-in m (conj trigger event-tag) conj event)) + {} + events)) + +(def event-by-trigger + (event->triggers mixpanel-events/events)) + +(defn matching-events [[event-name first-arg :as event] triggers] + (let [cnt (count event) + triggers (cond-> + ;; first we get all events which are triggered by event name + ;; (the case when :trigger contains only one element) + ;; {:trigger [:only-event-name-here]} + (get-in triggers [event-name event-tag]) + + ;; when event contains two or more elements we are trying + ;; to match by first two elements of :trigger + ;; {:trigger [:event-name :one-parameter]} + (>= cnt 2) + (concat (get-in triggers [event-name first-arg event-tag])) + + ;; also if event contains more than one parameter (more than + ;; two elements) we are trying to match it with equal :trigger + ;; {:trigger [:e-name :p1 :p2 :p3]} + ;; will match only with [:e-name :p1 :p2 :p3] event + (> cnt 2) + (concat (get-in triggers (conj event event-tag))))] + (filter (fn [{:keys [filter-fn]}] + (or (not filter-fn) (filter-fn {:event event}))) + triggers))) diff --git a/src/status_im/utils/mixpanel_events.cljs b/src/status_im/utils/mixpanel_events.cljs new file mode 100644 index 0000000000..4cd491401a --- /dev/null +++ b/src/status_im/utils/mixpanel_events.cljs @@ -0,0 +1,195 @@ +(ns status-im.utils.mixpanel-events) +;; This file is supposed to be edited by Chad. +;; Chad loves mixpanel. When Chad was a child he dreamed of being a mixpanel tamer. + +;; To add a new event definition, add a new map to this vector +;; :label is a free string that is displayed in mixpanel +;; :trigger is the re-frame style event that when matched will generate the emission of a mixpanel event +;; :properties is a map of key / value pair that will be added to the mixpanel event to provide more context +(def events + [;; 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]} + + ; Push notification settings + + {:label "Granted push notifications" + :trigger [:request-notifications-granted]} + + {:label "Denied push notifications" + :trigger [:request-notifications-denied]} + + ;; 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}} + {:label "Tap" + :trigger [:set :my-profile/advanced? true] + :properties {:target :profile-advanced + :type :open}} + {:label "Tap" + :trigger [:set :my-profile/advanced? false] + :properties {:target :profile-advanced + :type :closed}} + {:label "Tap" + :trigger [:switch-dev-mode true] + :properties {:target :profile-dev-mode + :type :on}} + {:label "Tap" + :trigger [:switch-dev-mode false] + :properties {:target :profile-dev-mode + :type :off}} + {:label "Tap" + :trigger [:navigate-to :backup-seed] + :properties {:target :backup-your-seed-phrase}} + {:label "Tap" + :trigger [:set-in [:my-profile/seed :step] :12-words] + :properties {:target :seed-phrase + :type :welcome-ok}} + {:label "Tap" + :trigger [:my-profile/enter-two-random-words] + :properties {:target :seed-phrase + :type :step1-next}} + {:label "Tap" + :trigger [:my-profile/set-step :second-word] + :properties {:target :seed-phrase + :type :step2-next}} + {:label "Tap" + :trigger [:my-profile/finish] + :properties {:target :seed-phrase + :type :step3-done}}]) diff --git a/src/status_im/utils/mixpanel_events.edn b/src/status_im/utils/mixpanel_events.edn deleted file mode 100644 index 62e921b744..0000000000 --- a/src/status_im/utils/mixpanel_events.edn +++ /dev/null @@ -1,194 +0,0 @@ -;; This file is supposed to be edited by Chad. -;; Chad loves mixpanel. When Chad was a child he dreamed of being a mixpanel tamer. - -;; To add a new event definition, add a new map to this vector -;; :label is a free string that is displayed in mixpanel -;; :trigger is the re-frame style event that when matched will generate the emission of a mixpanel event -;; :properties is a map of key / value pair that will be added to the mixpanel event to provide more context - -[;; 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]} - - ; Push notification settings - - {:label "Granted push notifications" - :trigger [:request-notifications-granted]} - - {:label "Denied push notifications" - :trigger [:request-notifications-denied]} - - ;; 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}} - {:label "Tap" - :trigger [:set :my-profile/advanced? true] - :properties {:target :profile-advanced - :type :open}} - {:label "Tap" - :trigger [:set :my-profile/advanced? false] - :properties {:target :profile-advanced - :type :closed}} - {:label "Tap" - :trigger [:switch-dev-mode true] - :properties {:target :profile-dev-mode - :type :on}} - {:label "Tap" - :trigger [:switch-dev-mode false] - :properties {:target :profile-dev-mode - :type :off}} - {:label "Tap" - :trigger [:navigate-to :backup-seed] - :properties {:target :backup-your-seed-phrase}} - {:label "Tap" - :trigger [:set-in [:my-profile/seed :step] :12-words] - :properties {:target :seed-phrase - :type :welcome-ok}} - {:label "Tap" - :trigger [:my-profile/enter-two-random-words] - :properties {:target :seed-phrase - :type :step1-next}} - {:label "Tap" - :trigger [:my-profile/set-step :second-word] - :properties {:target :seed-phrase - :type :step2-next}} - {:label "Tap" - :trigger [:my-profile/finish] - :properties {:target :seed-phrase - :type :step3-done}}] diff --git a/test/cljs/status_im/test/utils/mixpanel.cljs b/test/cljs/status_im/test/utils/mixpanel.cljs index 9f1e51c99b..05a6c3983d 100644 --- a/test/cljs/status_im/test/utils/mixpanel.cljs +++ b/test/cljs/status_im/test/utils/mixpanel.cljs @@ -3,29 +3,32 @@ [status-im.utils.mixpanel :as mixpanel] [cljs.core.async :as async])) -(deftest events - (is (not (nil? mixpanel/events)))) +(def definitions + (mixpanel/event->triggers + [{:trigger [:key]} + {:trigger [:key :subkey]} + {:trigger [:key2] + :filter-fn (fn [{[_ first-parameter] :event}] + (true? first-parameter))} + {:trigger [:key3 :p1 :p2 :p3]}])) -(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 (= 1 (count (mixpanel/matching-events [:key2 true] definitions)))) + (is (= 1 (count (mixpanel/matching-events [:key3 :p1 :p2 :p3] definitions)))) + (is (empty? (mixpanel/matching-events [:key3 :p1 :p2 :p4] definitions))) + (is (empty? (mixpanel/matching-events [:key2 false] definitions))) (is (empty? (mixpanel/matching-events [:key1 :another-subkey] definitions)))) (deftest drain-events-queue!-test (async done - (let [queue (async/chan (async/sliding-buffer 2000)) - results (atom [])] - (async/go + (let [queue (async/chan (async/sliding-buffer 2000)) + results (atom [])] + (async/go (async/