Compare commits

..
61 changed files with 827 additions and 926 deletions
@@ -1,8 +1,8 @@
(ns legacy.status-im.data-store.communities
(:require
[clojure.set :as set]
[status-im.constants :as constants]
[utils.transforms :as transforms]))
[clojure.walk :as walk]
[status-im.constants :as constants]))
(defn <-revealed-accounts-rpc
[accounts]
@@ -22,26 +22,19 @@
(reduce #(assoc %1 (key-fn %2) (<-request-to-join-community-rpc %2)) {} requests))
(defn <-chats-rpc
"This transformation from RPC is optimized differently because there can be
thousands of members in all chats and we don't want to transform them from JS
to CLJS because they will only be used to list community members or community
chat members."
[chats-js]
(let [chat-key-fn (fn [k]
(case k
"tokenGated" :token-gated?
"canPost" :can-post?
"can-view" :can-view?
"hideIfPermissionsNotMet" :hide-if-permissions-not-met?
(keyword k)))
chat-val-fn (fn [k v]
(if (= "members" k)
v
(transforms/js->clj v)))]
(transforms/<-js-map
chats-js
{:val-fn (fn [_ v]
(transforms/<-js-map v {:key-fn chat-key-fn :val-fn chat-val-fn}))})))
[chats]
(reduce-kv (fn [acc k v]
(assoc acc
(name k)
(-> v
(assoc :token-gated? (:tokenGated v)
:can-post? (:canPost v)
:can-view? (:canView v)
:hide-if-permissions-not-met? (:hideIfPermissionsNotMet v))
(dissoc :canPost :tokenGated :canView :hideIfPermissionsNotMet)
(update :members walk/stringify-keys))))
{}
chats))
(defn <-categories-rpc
[categ]
@@ -55,59 +48,49 @@
[token-permission]
(= (:type token-permission) constants/community-token-permission-become-member))
(defn- rename-community-key
[k]
(case k
"canRequestAccess" :can-request-access?
"canManageUsers" :can-manage-users?
"canDeleteMessageForEveryone" :can-delete-message-for-everyone?
;; This flag is misleading based on its name alone
;; because it should not be used to decide if the user
;; is *allowed* to join. Allowance is based on token
;; permissions. Still, the flag can be used to know
;; whether or not the user will have to wait until an
;; admin approves a join request.
"canJoin" :can-join?
"requestedToJoinAt" :requested-to-join-at
"isMember" :is-member?
"outroMessage" :outro-message
"adminSettings" :admin-settings
"tokenPermissions" :token-permissions
"communityTokensMetadata" :tokens-metadata
"introMessage" :intro-message
"muteTill" :muted-till
"lastOpenedAt" :last-opened-at
"joinedAt" :joined-at
(keyword k)))
(defn <-rpc
[c-js]
(let [community (transforms/<-js-map
c-js
{:key-fn rename-community-key
:val-fn (fn [k v]
(case k
"members" v
"chats" (<-chats-rpc v)
(transforms/js->clj v)))})]
(-> community
(update :admin-settings
set/rename-keys
{:pinMessageAllMembersEnabled :pin-message-all-members-enabled?})
(update :token-permissions seq)
(update :categories <-categories-rpc)
(assoc :role-permissions?
(->> community
:tokenPermissions
vals
(some role-permission?)))
(assoc :membership-permissions?
(->> community
:tokenPermissions
vals
(some membership-permission?)))
(assoc :token-images
(reduce (fn [acc {sym :symbol image :image}]
(assoc acc sym image))
{}
(:communityTokensMetadata community))))))
[c]
(-> c
(set/rename-keys
{:canRequestAccess :can-request-access?
:canManageUsers :can-manage-users?
:canDeleteMessageForEveryone :can-delete-message-for-everyone?
;; This flag is misleading based on its name alone
;; because it should not be used to decide if the user
;; is *allowed* to join. Allowance is based on token
;; permissions. Still, the flag can be used to know
;; whether or not the user will have to wait until an
;; admin approves a join request.
:canJoin :can-join?
:requestedToJoinAt :requested-to-join-at
:isMember :is-member?
:outroMessage :outro-message
:adminSettings :admin-settings
:tokenPermissions :token-permissions
:communityTokensMetadata :tokens-metadata
:introMessage :intro-message
:muteTill :muted-till
:lastOpenedAt :last-opened-at
:joinedAt :joined-at})
(update :admin-settings
set/rename-keys
{:pinMessageAllMembersEnabled :pin-message-all-members-enabled?})
(update :members walk/stringify-keys)
(update :chats <-chats-rpc)
(update :token-permissions seq)
(update :categories <-categories-rpc)
(assoc :role-permissions?
(->> c
:tokenPermissions
vals
(some role-permission?)))
(assoc :membership-permissions?
(->> c
:tokenPermissions
vals
(some membership-permission?)))
(assoc :token-images
(reduce (fn [acc {sym :symbol image :image}]
(assoc acc sym image))
{}
(:communityTokensMetadata c)))))
@@ -13,18 +13,15 @@
(native-module/logout)))
(rf/defn initialize-app-db
[{{:keys [keycard initials-avatar-font-file biometrics]
:network/keys [type status]
:wallet-connect/keys [web3-wallet]}
[{{:keys [keycard initials-avatar-font-file biometrics]
:network/keys [type]}
:db}]
{:db (assoc db/app-db
:network/type type
:network/status status
:initials-avatar-font-file initials-avatar-font-file
:keycard (dissoc keycard :secrets :pin :application-info)
:biometrics biometrics
:syncing nil
:wallet-connect/web3-wallet web3-wallet)})
:network/type type
:initials-avatar-font-file initials-avatar-font-file
:keycard (dissoc keycard :secrets :pin :application-info)
:biometrics biometrics
:syncing nil)})
(rf/defn logout-method
{:events [::logout-method]}
+29 -1
View File
@@ -4,6 +4,7 @@
[re-frame.core :as re-frame]
[react-native.platform :as utils.platform]
[status-im.common.json-rpc.events :as json-rpc]
[status-im.common.new-device-sheet.view :as new-device-sheet]
[status-im.config :as config]
[status-im.navigation.events :as navigation]
[taoensso.timbre :as log]
@@ -220,6 +221,12 @@
:name (:name metadata)
:device-type (:deviceType metadata))})
(defn should-show-syncing-pop-up?
[db installations]
(and (:syncing/pairing-process-initiated? db)
(first (filter #(not (get-in db [:pairing/installations (:id %)])) installations))))
(rf/defn handle-installations
[{:keys [db]} installations]
{:db (update db
@@ -228,7 +235,10 @@
(fn [acc {:keys [id] :as i}]
(update acc id merge (installation<-rpc i)))
%
installations))})
installations))
:fx [(when-let [new-installation (should-show-syncing-pop-up? db installations)]
[:dispatch
[:show-bottom-sheet {:content (fn [] [new-device-sheet/view (:id new-installation)])}]])]})
(rf/defn load-installations
{:events [:pairing.callback/get-our-installations-success]}
@@ -243,6 +253,24 @@
{}
installations))})
(rf/defn finish-seed-phrase-fallback-syncing
{:events [:pairing/finish-seed-phrase-fallback-syncing]}
[{:keys [db]}]
{:fx [[:dispatch [:show-bottom-sheet {:content (fn [] [new-device-sheet/view-2])}]]
[:json-rpc/call
[{:method "wakuext_finishPairingThroughSeedPhraseProcess"
:params [{:installationId (:syncing/installation-id db)}]
:js-response true
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])}]]]})
(rf/defn pair-and-sync
{:events [:pairing/pair-and-sync]}
[cofx installation-id]
{:fx [[:json-rpc/call
[{:method "wakuext_enableAndSyncInstallation"
:params [{:installationId installation-id}]
:on-success #(log/debug "successfully synced devices")}]]]})
(rf/defn enable-installation-success
{:events [:pairing.callback/enable-installation-success]}
[cofx installation-id]
@@ -20,6 +20,8 @@
light-client-enabled?
store-confirmations-enabled?
current-fleet
test-networks-enabled?
is-goerli-enabled?
peer-syncing-enabled?]}]
(keep
identity
@@ -85,6 +87,22 @@
[:wakuv2.ui/toggle-store-confirmations (not store-confirmations-enabled?)])
:accessory :switch
:active store-confirmations-enabled?}
{:size :small
:title "Testnet mode"
:accessibility-label :test-networks-enabled
:container-margin-bottom 8
:on-press
#(re-frame/dispatch [:profile.settings/toggle-test-networks])
:accessory :switch
:active test-networks-enabled?}
{:size :small
:title "Enable Goerli as test network"
:accessibility-label :enable-sepolia-as-test-network
:container-margin-bottom 8
:on-press
#(re-frame/dispatch [:profile.settings/toggle-goerli-test-network])
:accessory :switch
:active is-goerli-enabled?}
{:size :small
:title "Peer syncing"
:accessibility-label :peer-syncing
@@ -106,7 +124,9 @@
(views/defview advanced-settings
[]
(views/letsubs [light-client-enabled? [:profile/light-client-enabled?]
(views/letsubs [test-networks-enabled? [:profile/test-networks-enabled?]
is-goerli-enabled? [:profile/is-goerli-enabled?]
light-client-enabled? [:profile/light-client-enabled?]
store-confirmations-enabled? [:profile/store-confirmations-enabled?]
telemetry-enabled? [:profile/telemetry-enabled?]
current-log-level [:log-level/current-log-level]
@@ -127,6 +147,8 @@
:store-confirmations-enabled? store-confirmations-enabled?
:current-fleet current-fleet
:dev-mode? false
:test-networks-enabled? test-networks-enabled?
:is-goerli-enabled? is-goerli-enabled?
:peer-syncing-enabled? peer-syncing-enabled?})
:key-fn (fn [_ i] (str i))
:render-fn render-item}]]))
@@ -127,7 +127,7 @@
(when (and can-manage-users? (= constants/community-on-request-access (:access permissions)))
[requests-to-join community-id])
[rn/flat-list
{:data sorted-members
{:data (keys sorted-members)
:render-data {:community-id community-id
:my-public-key my-public-key
:can-kick-users? (and can-manage-users?
+1 -3
View File
@@ -332,9 +332,7 @@
(defn set-blank-preview-flag
[flag]
(log/debug "[native-module] set-blank-preview-flag")
;; Sometimes the app crashes during logout because `flag` is nil.
(when flag
(.setBlankPreviewFlag ^js (encryption) flag)))
(.setBlankPreviewFlag ^js (encryption) flag))
(defn get-device-model-info
[]
@@ -65,8 +65,9 @@
:style (style/container loading? theme size)}
[loading-view theme]]
[linear-gradient/linear-gradient
{:style (style/container loading? theme size)
:colors (linear-gradient-props theme customization-color)}
(assoc {:style (style/container loading? theme size)}
:colors
(linear-gradient-props theme customization-color))
[rn/pressable
{:accessibility-label :internal-link-card
:on-press on-press}
@@ -4,7 +4,7 @@
(defn main
[{:keys [type customization-color]} theme]
{:justify-content :flex-start
{:justify-content :center
:align-items :center
:height 32
:padding-left 4
@@ -16,7 +16,6 @@
(defn label
[type theme]
{:color (colors/theme-colors colors/neutral-100 colors/white theme)
:flex-shrink 1
:margin-left (if (= type :address) 6 4)})
(def collectible-image
@@ -58,19 +58,16 @@
- :emoji - string - emoji used for displaying account avatar
- :image-source - resource - image to display on :network, :collectible and :user
- :theme - :light / :dark"
[{:keys [label customization-color type container-style]
[{:keys [label customization-color type]
:as props
:or {customization-color colors/neutral-80-opa-5}}]
(let [theme (quo.theme/use-theme)]
[rn/view
{:accessibility-label :container
:style (merge (style/main (assoc props :customization-color customization-color)
theme)
container-style)}
:style (style/main (assoc props :customization-color customization-color) theme)}
[left-view props]
[text/text
{:style (style/label type theme)
:weight :semi-bold
:number-of-lines 1
:size :heading-1}
{:style (style/label type theme)
:weight :semi-bold
:size :heading-1}
label]]))
@@ -21,8 +21,3 @@
(if blur?
(colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70 theme)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme)))
(def text
{:flex 1
;; NOTE: assures the ellipses are not cut off when text is too long
:padding-right 2})
@@ -59,9 +59,7 @@
[rn/view {:style (merge style/container container-style)}
[text/text
{:size :heading-1
:number-of-lines 1
:weight :semi-bold
:style style/text
:accessibility-label accessibility-label}
title]
(case right
+11 -23
View File
@@ -5,7 +5,7 @@
[react-native.platform :as platform]))
(def account-colors
[:blue :yellow :purple :turquoise :magenta :sky :orange :army :pink :camel :copper])
[:blue :yellow :purple :turquoise :magenta :sky :orange :army :flamingo :camel :copper])
(defn alpha
[value opacity]
@@ -44,13 +44,6 @@
(alpha light-color light-opacity)
(alpha dark-color dark-opacity))))))
(defn valid-color?
[color]
(or (keyword? color)
(and (string? color)
(or (string/starts-with? color "#")
(string/starts-with? color "rgb")))))
;;;;Neutral
@@ -246,7 +239,7 @@
60 "#CC6438"}
:army {50 "#216266"
60 "#1A4E52"}
:pink {50 "#F66F8F"
:flamingo {50 "#F66F8F"
60 "#C55972"}
:purple {50 "#7140FD"
60 "#5A33CA"}
@@ -323,14 +316,13 @@
[s]
(and (string? s) (string/starts-with? s "#")))
(def fallback-color (customization :blue))
(defn- get-from-colors-map
[color suffix]
(let [color-without-suffix (get colors-map color fallback-color)]
(if (hex-string? color-without-suffix)
(let [color-without-suffix (get colors-map color)
resolved-color? (hex-string? color-without-suffix)]
(if resolved-color?
color-without-suffix
(get color-without-suffix suffix))))
(get-in colors-map [color suffix]))))
(defn- resolve-color*
([color theme]
@@ -364,15 +356,11 @@
([color suffix]
(custom-color color suffix nil))
([color suffix opacity]
(let [resolved-color (cond
(not (keyword? color))
color
(hex-string? (get colors-map color))
(get colors-map color fallback-color)
:else
(get-in colors-map [color suffix] (get fallback-color suffix)))]
(let [hex? (not (keyword? color))
resolved-color (cond hex? color
(hex-string? (get colors-map color)) (get colors-map color)
:else (get-in colors-map
[color suffix]))]
(if opacity
(alpha resolved-color (/ opacity 100))
resolved-color))))))
+2 -35
View File
@@ -1,39 +1,6 @@
(ns react-native.linear-gradient
(:require
["react-native-linear-gradient" :default LinearGradient]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[reagent.core :as reagent]
[taoensso.timbre :as log]))
[reagent.core :as reagent]))
(def ^:private linear-gradient* (reagent/adapt-react-class LinearGradient))
(defn- split-valid-colors
[acc idx color]
(let [color? (colors/valid-color? color)]
(cond-> acc
:always (update :safe-colors conj (if color? color "transparent"))
(not color?) (update :wrong-colors conj [idx color]))))
(defn- wrong-colors-str
[colors]
(reduce-kv (fn [s idx color]
(str s "Index: " idx ", color: " (prn-str color)))
"Invalid color values in vector passed to Linear Gradient:\n"
colors))
(defn linear-gradient
[props & children]
(when ^boolean js/goog.DEBUG
(assert (vector? (:colors props))))
(let [{:keys [wrong-colors safe-colors]} (rn/use-memo
(fn []
(reduce-kv split-valid-colors
{:safe-colors []
:wrong-colors {}}
(:colors props)))
[(:colors props)])]
(when (seq wrong-colors)
(log/error (wrong-colors-str wrong-colors)))
(into [linear-gradient* (assoc props :colors safe-colors)]
children)))
(def linear-gradient (reagent/adapt-react-class LinearGradient))
@@ -0,0 +1,24 @@
(ns status-im.common.new-device-sheet.style
(:require [quo.foundations.colors :as colors]))
(def heading
{:padding-left 20
:padding-bottom 8})
(def message
{:padding-horizontal 20
:padding-top 4})
(def warning
{:margin-horizontal 20
:margin-top 10})
(def drawer-container
{:padding-horizontal 13
:padding-top 16})
(def settings-subtext
{:color colors/white-opa-70
:align-self :center
:margin-bottom 12})
@@ -0,0 +1,82 @@
(ns status-im.common.new-device-sheet.view
(:require
[quo.core :as quo]
[status-im.common.new-device-sheet.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- dismiss-keyboard
[]
(rf/dispatch [:dismiss-keyboard]))
(defn- hide-bottom-sheet
[]
(rf/dispatch [:hide-bottom-sheet]))
(defn- pair-and-sync
[installation-id]
(rf/dispatch [:pairing/pair-and-sync installation-id])
(hide-bottom-sheet))
(defn view
[installation-id]
(dismiss-keyboard)
[:<>
[quo/text
{:weight :semi-bold
:size :heading-2
:accessibility-label :new-device-sheet-heading
:style style/heading}
(i18n/label :t/pair-new-device-and-sync)]
[quo/text
{:weight :regular
:size :paragraph-1
:accessibility-label :new-device-sheet-message
:style style/message}
(i18n/label :t/new-device-detected)]
[quo/text
{:weight :semi-bold
:size :heading-2
:accessibility-label :new-device-installation-id
:style style/heading}
installation-id]
[quo/bottom-actions
{:actions :two-actions
:blur? true
:container-style {:margin-top 12}
:button-two-label (i18n/label :t/cancel)
:button-two-props {:type :grey
:on-press hide-bottom-sheet}
:button-one-label (i18n/label :t/pair-and-sync)
:button-one-props {:on-press #(pair-and-sync installation-id)}}]])
(defn view-2
[]
(let [installation-id (rf/sub [:profile/installation-id])]
(dismiss-keyboard)
[:<>
[quo/text
{:weight :semi-bold
:size :heading-2
:accessibility-label :new-device-sheet-heading
:style style/heading}
(i18n/label :t/pair-new-device-and-sync)]
[quo/text
{:weight :regular
:size :paragraph-1
:accessibility-label :new-device-sheet-message
:style style/message}
(i18n/label :t/check-new-device)]
[quo/text
{:weight :semi-bold
:size :heading-2
:accessibility-label :new-device-installation-id
:style style/heading}
installation-id]
[quo/bottom-actions
{:actions :one-action
:blur? true
:container-style {:margin-top 12}
:button-one-label (i18n/label :t/close)
:button-one-props {:type :grey
:on-press hide-bottom-sheet}}]]))
@@ -1,14 +1,14 @@
(ns status-im.common.raw-data-block.view
(:require [quo.core :as quo]
[react-native.gesture :as gesture]
[react-native.core :as rn]
[status-im.common.raw-data-block.style :as style]))
(defn view
[data]
[gesture/scroll-view
{:style style/container}
[rn/scroll-view
{:style style/container
:content-container-style style/content}
[quo/text
{:size :paragraph-2
:style style/content
:weight :code}
data]])
@@ -1,5 +1,7 @@
(ns status-im.contexts.chat.messenger.messages.link-preview.events
(:require [camel-snake-kebab.core :as csk]
[status-im.common.json-rpc.events :as json-rpc]
[taoensso.timbre :as log]
[utils.collection]
[utils.re-frame :as rf]))
@@ -33,6 +35,17 @@
{:fx [[:dispatch
[:profile.settings/profile-update :link-preview-request-enabled (boolean enabled?)]]]}))
(rf/reg-event-fx :chat.ui/link-preview-whitelist-received
(fn [{:keys [db]} [whitelist]]
{:db (assoc db :link-previews-whitelist whitelist)}))
(rf/reg-fx :chat.ui/request-link-preview-whitelist
(fn []
(json-rpc/call {:method "wakuext_getLinkPreviewWhitelist"
:params []
:on-success [:chat.ui/link-preview-whitelist-received]
:on-error #(log/error "Failed to get link preview whitelist")})))
(rf/reg-event-fx :chat.ui/enable-link-previews
(fn [{{:profile/keys [profile]} :db} [site enabled?]]
(let [enabled-sites (if enabled?
@@ -103,11 +103,11 @@
(models.contact/process-js-contacts cofx response-js)
(seq communities)
(do
(let [communities-clj (types/js->clj communities)]
(js-delete response-js "communities")
(rf/merge cofx
(process-next response-js sync-handler)
(communities/handle-communities communities)))
(communities/handle-communities communities-clj)))
(seq bookmarks)
(let [bookmarks-clj (types/js->clj bookmarks)]
@@ -26,7 +26,7 @@
"0xD" {:address "0xD"
:operable? false
:position 3
:color :pink
:color :flamingo
:emoji "🦩"}})
(def permissioned-accounts
@@ -22,7 +22,7 @@
airdrop-account (rf/sub [:communities/airdrop-account id])
revealed-accounts (rf/sub [:communities/accounts-to-reveal id])
revealed-accounts-count (count revealed-accounts)
wallet-accounts-count (count (rf/sub [:wallet/operable-accounts]))
wallet-accounts-count (count (rf/sub [:wallet/operable-accounts-without-watched-accounts]))
addresses-shared-text (if (= revealed-accounts-count wallet-accounts-count)
(i18n/label :t/all-addresses)
(i18n/label-pluralize
@@ -57,24 +57,19 @@
:index index})
(defn- members
[community-id chat-id theme]
(let [online-members (rf/sub [:communities/chat-members-sorted community-id chat-id :online])
offline-members (rf/sub [:communities/chat-members-sorted community-id chat-id :offline])]
[rn/section-list
{:key-fn :public-key
:content-container-style {:padding-bottom 20}
:get-item-layout get-item-layout
:content-inset-adjustment-behavior :never
:sections [{:title (i18n/label :t/online)
:data online-members}
{:title (i18n/label :t/offline)
:data offline-members}]
:sticky-section-headers-enabled false
:render-section-header-fn contact-list/contacts-section-header
:render-section-footer-fn footer
:render-data {:theme theme}
:render-fn contact-item
:scroll-event-throttle 32}]))
[items theme]
[rn/section-list
{:key-fn :public-key
:content-container-style {:padding-bottom 20}
:get-item-layout get-item-layout
:content-inset-adjustment-behavior :never
:sections items
:sticky-section-headers-enabled false
:render-section-header-fn contact-list/contacts-section-header
:render-section-footer-fn footer
:render-data {:theme theme}
:render-fn contact-item
:scroll-event-throttle 32}])
(defn view
[]
@@ -83,6 +78,8 @@
{:keys [description chat-name emoji muted chat-type color]
:as chat} (rf/sub [:chats/chat-by-id chat-id])
pins-count (rf/sub [:chats/pin-messages-count chat-id])
items (rf/sub [:communities/sorted-community-members-section-list
community-id chat-id])
theme (quo.theme/use-theme)]
(rn/use-mount (fn []
(rf/dispatch [:pin-message/load-pin-messages chat-id])))
@@ -136,4 +133,4 @@
(if muted
(home.actions/unmute-chat-action chat-id)
(home.actions/mute-chat-action chat-id chat-type muted)))}]}]]]
[members community-id chat-id theme]]))
[members items theme]]))
@@ -54,7 +54,7 @@
:as item}]
(let [user-selected? (rf/sub [:is-contact-selected? public-key])
{:keys [id]} (rf/sub [:get-screen-params])
community-members-keys (set (rf/sub [:communities/community-members id]))
community-members-keys (set (keys (rf/sub [:communities/community-members id])))
community-member? (boolean (community-members-keys public-key))
on-toggle (fn []
(when-not community-member?
+28 -25
View File
@@ -39,6 +39,17 @@
(rf/reg-event-fx :communities/handle-community handle-community)
(schema/=> handle-community
[:=>
[:catn
[:cofx :schema.re-frame/cofx]
[:args
[:schema [:catn [:community-js map?]]]]]
[:maybe
[:map
[:db [:map [:communities map?]]]
[:fx vector?]]]])
(rf/defn handle-removed-chats
[{:keys [db]} chat-ids]
{:db (reduce (fn [db chat-id]
@@ -73,9 +84,10 @@
(rf/defn handle-communities
{:events [:community/fetch-success]}
[{:keys [db]} communities-js]
{:fx (map (fn [c] [:dispatch [:communities/handle-community c]])
communities-js)})
[{:keys [db]} communities]
{:fx
(->> communities
(map #(vector :dispatch [:communities/handle-community %])))})
(rf/reg-event-fx :communities/request-to-join-result
(fn [{:keys [db]} [community-id request-id response-js]]
@@ -124,30 +136,21 @@
{}
categories))}))
(rf/reg-event-fx :community/fetch-low-priority
(fn []
{:fx [[:json-rpc/call
[{:method "wakuext_checkAndDeletePendingRequestToJoinCommunity"
:params []
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error #(log/info "failed to fetch communities" %)}
{:method "wakuext_collapsedCommunityCategories"
:params []
:on-success [:communities/fetched-collapsed-categories-success]
:on-error #(log/error "failed to fetch collapsed community categories" %)}]]]}))
(rf/reg-event-fx :community/fetch
(fn [_]
{:fx [[:json-rpc/call
[{:method "wakuext_serializedCommunities"
:params []
:on-success [:community/fetch-success]
:js-response true
:on-error #(log/error "failed to fetch communities" %)}]]
;; Dispatch a little after 1000ms because other higher-priority events
;; after login are being processed at the 1000ms mark.
[:dispatch-later [{:ms 1200 :dispatch [:community/fetch-low-priority]}]]]}))
{:json-rpc/call [{:method "wakuext_serializedCommunities"
:params []
:on-success #(rf/dispatch [:community/fetch-success %])
:on-error #(log/error "failed to fetch communities" %)}
{:method "wakuext_checkAndDeletePendingRequestToJoinCommunity"
:params []
:js-response true
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/info "failed to fetch communities" %)}
{:method "wakuext_collapsedCommunityCategories"
:params []
:on-success #(rf/dispatch [:communities/fetched-collapsed-categories-success %])
:on-error #(log/error "failed to fetch collapsed community categories" %)}]}))
(defn update-previous-permission-addresses
[{:keys [db]} [community-id]]
@@ -151,7 +151,7 @@
(-> effects :json-rpc/call first (select-keys [:method :params]))))))))
(deftest handle-community-test
(let [community #js {:id community-id :clock 2}]
(let [community {:id community-id :clock 2}]
(testing "given a unjoined community"
(let [effects (events/handle-community {} [community])]
(is (match? community-id
@@ -163,7 +163,7 @@
(filter some? (:fx effects))))))
(testing "given a joined community"
(let [community #js {:id community-id :clock 2 :joined true}
(let [community (assoc community :joined true)
effects (events/handle-community {} [community])]
(is (match?
[[:dispatch
@@ -172,19 +172,16 @@
(filter some? (:fx effects))))))
(testing "given a community with token-permissions-check"
(let [community #js
{:id community-id :clock 2 :token-permissions-check :fake-token-permissions-check}
(let [community (assoc community :token-permissions-check :fake-token-permissions-check)
effects (events/handle-community {} [community])]
(is (match?
[[:dispatch
[:communities/check-permissions-to-join-community-with-all-addresses community-id]]]
(filter some? (:fx effects))))))
(testing "given a community with lower clock"
(let [effects (events/handle-community {:db {:communities {community-id {:clock 3}}}} [community])]
(is (nil? effects))))
(testing "given a community without clock"
(let [community #js {:id community-id}
(let [community (dissoc community :clock)
effects (events/handle-community {} [community])]
(is (nil? effects))))))
@@ -75,51 +75,46 @@
:banner (resources/get-image :discover)
:accessibility-label :communities-home-discover-card}})
(defn on-tab-change
[tab]
(rf/dispatch [:communities/select-tab tab]))
(defn view
[]
(let [flat-list-ref (rn/use-ref-atom nil)
set-flat-list-ref (rn/use-callback #(reset! flat-list-ref %))
theme (quo.theme/use-theme)
customization-color (rf/sub [:profile/customization-color])
selected-tab (or (rf/sub [:communities/selected-tab]) :joined)
{:keys [joined pending opened]} (rf/sub [:communities/grouped-by-status])
selected-items (case selected-tab
:joined joined
:pending pending
:opened opened)
scroll-shared-value (reanimated/use-shared-value 0)
on-scroll (rn/use-callback
(fn [event]
(common.banner/set-scroll-shared-value
{:scroll-input (oops/oget event
"nativeEvent.contentOffset.y")
:shared-value scroll-shared-value})))]
[:<>
(if (empty? selected-items)
[common.empty-state/view
{:selected-tab selected-tab
:tab->content (empty-state-content theme)}]
[reanimated/flat-list
{:ref set-flat-list-ref
:key-fn :id
:content-inset-adjustment-behavior :never
:header [common.header-spacing/view]
:render-fn item-render
:style {:margin-top -1}
:data selected-items
:scroll-event-throttle 8
:content-container-style {:padding-bottom
jump-to.constants/floating-shell-button-height}
:on-scroll on-scroll}])
[common.banner/animated-banner
{:content banner-data
:customization-color customization-color
:scroll-ref flat-list-ref
:tabs tabs-data
:selected-tab selected-tab
:on-tab-change on-tab-change
:scroll-shared-value scroll-shared-value}]]))
(let [flat-list-ref (atom nil)
set-flat-list-ref #(reset! flat-list-ref %)]
(fn []
(let [theme (quo.theme/use-theme)
customization-color (rf/sub [:profile/customization-color])
selected-tab (or (rf/sub [:communities/selected-tab]) :joined)
{:keys [joined pending opened]} (rf/sub [:communities/grouped-by-status])
selected-items (case selected-tab
:joined joined
:pending pending
:opened opened)
scroll-shared-value (reanimated/use-shared-value 0)]
[:<>
(if (empty? selected-items)
[common.empty-state/view
{:selected-tab selected-tab
:tab->content (empty-state-content theme)}]
[reanimated/flat-list
{:ref set-flat-list-ref
:key-fn :id
:content-inset-adjustment-behavior :never
:header [common.header-spacing/view]
:render-fn item-render
:style {:margin-top -1}
:data selected-items
:scroll-event-throttle 8
:content-container-style {:padding-bottom
jump-to.constants/floating-shell-button-height}
:on-scroll #(common.banner/set-scroll-shared-value
{:scroll-input (oops/oget
%
"nativeEvent.contentOffset.y")
:shared-value scroll-shared-value})}])
[:f> common.banner/animated-banner
{:content banner-data
:customization-color customization-color
:scroll-ref flat-list-ref
:tabs tabs-data
:selected-tab selected-tab
:on-tab-change (fn [tab] (rf/dispatch [:communities/select-tab tab]))
:scroll-shared-value scroll-shared-value}]]))))
@@ -17,3 +17,17 @@
{:events [:onboarding/overlay-dismiss]}
[_]
{:onboarding/overlay-dismiss-fx nil})
(re-frame/reg-fx
:onboarding/overlay-show-fx
(fn []
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
(blur-show-fn))
(when-let [push-animation-fn @profiles/push-animation-fn-atom]
(push-animation-fn))))
(rf/defn overlay-show
{:events [:onboarding/overlay-show]}
[_]
{:onboarding/overlay-show-fx nil})
+22 -16
View File
@@ -125,25 +125,31 @@
mnemonic key-uid]))
on-error]})
;; TODO: clear syncing data if key-uid does not match
(rf/defn seed-phrase-validated
{:events [:onboarding/seed-phrase-validated]}
[{:keys [db]} seed-phrase key-uid]
(if (contains? (:profile/profiles-overview db) key-uid)
{:effects.utils/show-confirmation
{:title (i18n/label :t/multiaccount-exists-title)
:content (i18n/label :t/multiaccount-exists-content)
:confirm-button-text (i18n/label :t/unlock)
:on-accept (fn []
(re-frame/dispatch [:pop-to-root :screen/profile.profiles])
(re-frame/dispatch
[:profile/profile-selected key-uid]))
:on-cancel #(re-frame/dispatch [:pop-to-root :multiaccounts])}}
{:db (assoc-in db [:onboarding/profile :seed-phrase] seed-phrase)
:dispatch [:navigate-to-within-stack
[:screen/onboarding.create-profile
(get db
:onboarding/navigated-to-enter-seed-phrase-from-screen
:screen/onboarding.new-to-status)]]}))
(let [next-screen (if (and (seq (:syncing/key-uid db))
(= (:syncing/key-uid db) key-uid))
:screen/onboarding.create-profile-password
:screen/onboarding.create-profile)]
(if (contains? (:profile/profiles-overview db) key-uid)
{:effects.utils/show-confirmation
{:title (i18n/label :t/multiaccount-exists-title)
:content (i18n/label :t/multiaccount-exists-content)
:confirm-button-text (i18n/label :t/unlock)
:on-accept (fn []
(re-frame/dispatch [:pop-to-root :screen/profile.profiles])
(re-frame/dispatch
[:profile/profile-selected key-uid]))
:on-cancel #(re-frame/dispatch [:pop-to-root :multiaccounts])}}
{:db (assoc-in db [:onboarding/profile :seed-phrase] seed-phrase)
:dispatch [:navigate-to-within-stack
[next-screen
(get db
:onboarding/navigated-to-enter-seed-phrase-from-screen
:screen/onboarding.new-to-status)]]})))
(rf/defn navigate-to-create-profile
{:events [:onboarding/navigate-to-create-profile]}
@@ -26,31 +26,58 @@
:title-accessibility-label :progress-screen-title
:description-accessibility-label :progress-screen-sub-title}])
(defn navigate-to-enter-seed-phrase
[view-id]
(if (= view-id :screen/onboarding.syncing-progress-intro)
(do
(rf/dispatch [:navigate-back-to :screen/onboarding.sync-or-recover-profile])
(debounce/debounce-and-dispatch
[:onboarding/navigate-to-sign-in-by-seed-phrase :screen/onboarding.sync-or-recover-profile]
300))
(do
(rf/dispatch [:navigate-back])
(debounce/debounce-and-dispatch [:onboarding/overlay-show] 100)
(debounce/debounce-and-dispatch [:open-modal :screen/onboarding.new-to-status] 200)
(debounce/debounce-and-dispatch
[:onboarding/navigate-to-sign-in-by-seed-phrase :screen/onboarding.new-to-status]
300))))
(defn try-again-button
[profile-color in-onboarding? logged-in?]
[quo/button
{:on-press (fn []
(rf/dispatch [:syncing/clear-states])
(cond
logged-in? (rf/dispatch [:navigate-back])
in-onboarding? (rf/dispatch [:navigate-back-to
:screen/onboarding.sign-in-intro])
:else (do
(rf/dispatch [:navigate-back])
(debounce/throttle-and-dispatch
[:open-modal
:screen/onboarding.sign-in]
1000))))
:accessibility-label :try-again-later-button
:customization-color profile-color
:container-style style/try-again-button}
(i18n/label :t/try-again)])
[profile-color in-onboarding? logged-in? view-id]
[rn/view
(when-not logged-in?
[quo/button
{:on-press #(navigate-to-enter-seed-phrase view-id)
:accessibility-label :try-seed-phrase-button
:customization-color profile-color
:container-style style/try-again-button}
(i18n/label :t/enter-seed-phrase)])
[quo/button
{:on-press (fn []
(rf/dispatch [:syncing/clear-states])
(cond
logged-in? (rf/dispatch [:navigate-back])
in-onboarding? (rf/dispatch [:navigate-back-to
:screen/onboarding.sign-in-intro])
:else (do
(rf/dispatch [:navigate-back])
(debounce/throttle-and-dispatch
[:open-modal
:screen/onboarding.sign-in]
1000))))
:accessibility-label :try-again-later-button
:customization-color profile-color
:container-style style/try-again-button}
(i18n/label :t/try-again)]])
(defn view
[in-onboarding?]
(let [pairing-status (rf/sub [:pairing/pairing-status])
profile-color (:color (rf/sub [:onboarding/profile]))
logged-in? (rf/sub [:multiaccount/logged-in?])]
logged-in? (rf/sub [:multiaccount/logged-in?])
view-id (rf/sub [:view-id])]
[rn/view {:style (style/page-container in-onboarding?)}
(when-not in-onboarding?
[rn/view {:style style/absolute-fill}
@@ -65,7 +92,7 @@
[quo/text "[Error here]"]])
[rn/view {:flex 1}])
(when-not (pairing-progress pairing-status)
[try-again-button profile-color in-onboarding? logged-in?])]))
[try-again-button profile-color in-onboarding? logged-in? view-id])]))
(defn view-onboarding
[]
@@ -14,7 +14,7 @@
[]
(let [state (reagent/atom {:type :default
:customization-color :blue
:account-color :pink
:account-color :flamingo
:title "Alisher Yakupov"
:address "0x21a...49e"
:on-options-press #(js/alert "Options button pressed!")})]
@@ -28,7 +28,7 @@
{:image-source (quo.resources/get-network :ethereum)
:label "Mainnet"}
:saved-address
{:customization-color :pink
{:customization-color :flamingo
:label "Peter Lambo"}
:account
{:label "Account"
@@ -57,15 +57,14 @@
[{:method "wakuext_startMessenger"
:on-success [:profile.login/messenger-started]
:on-error #(log/error "failed to start messenger" %)}]]
[:dispatch [:universal-links/generate-profile-url]]
[:dispatch [:community/fetch]]
;; Wallet initialization can be delayed a little bit because we
;; need to free the queue for heavier events first, such as
;; loading chats and communities. This globally helps alleviate
;; stuttering immediately after login.
[:dispatch-later [{:ms 500 :dispatch [:wallet/initialize]}]]
[:push-notifications/load-preferences]
[:profile.config/get-node-config]
[:logs/set-level log-level]
[:activity-center.notifications/fetch-pending-contact-requests-fx]
[:activity-center/update-seen-state]
[:activity-center.notifications/fetch-unread-count]
;; Immediately try to open last chat. We can't wait until the
;; messenger has started and has processed all chats because
@@ -91,45 +90,24 @@
;; login phase 2: we want to load and show chats faster, so we split login into 2 phases
(rf/reg-event-fx :profile.login/get-chats-callback
(fn [{:keys [db]}]
(let [{:keys [notifications-enabled? key-uid]} (:profile/profile db)]
(let [{:keys [notifications-enabled? key-uid
preview-privacy?]} (:profile/profile db)]
{:db db
:fx [[:effects.profile/enable-local-notifications]
[:contacts/initialize-contacts]
;; The delay is arbitrary. We just want to give some time for the
;; thread to process more important events first, but we can't delay
;; too much otherwise the UX may degrade due to stale data.
[:dispatch-later [{:ms 1500 :dispatch [:profile.login/non-critical-initialization]}]]
[:browser/initialize-browser]
[:dispatch [:mobile-network/on-network-status-change]]
[:group-chats/get-group-chat-invitations]
[:profile.settings/get-profile-picture key-uid]
[:profile.settings/blank-preview-flag-changed preview-privacy?]
[:chat.ui/request-link-preview-whitelist]
[:visibility-status-updates/fetch]
[:switcher-cards/fetch]
(when (ff/enabled? ::ff/wallet.wallet-connect)
[:dispatch [:wallet-connect/init]])
(when notifications-enabled?
[:effects/push-notifications-enable])]})))
;; Login phase 3: events at this phase can wait a bit longer to be processed in
;; order to leave room for higher-priority or heavy weight events.
(rf/reg-event-fx :profile.login/non-critical-initialization
(fn [{:keys [db]}]
(let [{:keys [preview-privacy?]} (:profile/profile db)]
{:fx [[:browser/initialize-browser]
[:logging/initialize-web3-client-version]
[:group-chats/get-group-chat-invitations]
[:profile.settings/blank-preview-flag-changed preview-privacy?]
(when (ff/enabled? ::ff/shell.jump-to)
[:switcher-cards/fetch])
[:visibility-status-updates/fetch]
[:dispatch [:universal-links/generate-profile-url]]
[:push-notifications/load-preferences]
[:profile.config/get-node-config]
[:activity-center.notifications/fetch-pending-contact-requests-fx]
[:activity-center/update-seen-state]
[:activity-center.notifications/fetch-unread-count]
[:pairing/get-our-installations]
[:json-rpc/call
[{:method "admin_nodeInfo"
:on-success [:profile.login/node-info-fetched]
:on-error #(log/error "node-info: failed error" %)}]]]})))
(rf/reg-event-fx :profile.login/messenger-started
(fn [{:keys [db]} [{:keys [mailservers]}]]
(let [new-account? (get db :onboarding/new-account?)]
@@ -141,6 +119,13 @@
(rf/dispatch [:chats-list/load-success result])
(rf/dispatch [:communities/get-user-requests-to-join])
(rf/dispatch [:profile.login/get-chats-callback]))}]
[:json-rpc/call
[{:method "admin_nodeInfo"
:on-success [:profile.login/node-info-fetched]
:on-error #(log/error "node-info: failed error" %)}]]
[:pairing/get-our-installations]
(when (:syncing/installation-id db)
[:dispatch [:pairing/finish-seed-phrase-fallback-syncing]])
(when-not new-account?
[:dispatch [:universal-links/process-stored-event]])]})))
@@ -156,9 +141,11 @@
(if error
{:db (update db :profile/login #(-> % (dissoc :processing) (assoc :error error)))}
{:db (dissoc db :profile/login)
:fx [(when (and new-account? (not recovered-account?))
[:dispatch-later [{:ms 1000 :dispatch [:wallet-legacy/set-initial-blocks-range]}]])
[:dispatch-later [{:ms 2000 :dispatch [:ens/update-usernames ensUsernames]}]]
:fx [[:logging/initialize-web3-client-version]
(when (and new-account? (not recovered-account?))
[:dispatch [:wallet-legacy/set-initial-blocks-range]])
[:dispatch [:ens/update-usernames ensUsernames]]
[:dispatch [:wallet/initialize]]
[:dispatch [:profile.login/login-existing-profile settings account]]]})))
(rf/reg-event-fx
@@ -2,6 +2,7 @@
(:require
[native-module.core :as native-module]
[status-im.common.emoji-picker.utils :as emoji-picker.utils]
[status-im.constants :as constants]
[status-im.contexts.profile.config :as profile.config]
status-im.contexts.profile.recover.effects
[utils.re-frame :as rf]
@@ -17,11 +18,11 @@
(assoc-in [:syncing :login-sha3-password] login-sha3-password))
:effects.profile/restore-and-login
(merge (profile.config/create)
{:displayName display-name
:mnemonic (security/safe-unmask-data seed-phrase)
:password login-sha3-password
:imagePath (profile.config/strip-file-prefix image-path)
:customizationColor color
:emoji (emoji-picker.utils/random-emoji)
:fetchBackup true})}))
(assoc (profile.config/create)
:displayName display-name
:mnemonic (security/safe-unmask-data seed-phrase)
:password login-sha3-password
:imagePath (profile.config/strip-file-prefix image-path)
:customizationColor (or color constants/profile-default-color)
:emoji (emoji-picker.utils/random-emoji)
:fetchBackup true)}))
+20 -3
View File
@@ -46,13 +46,29 @@
(log/info "[local-pairing] input-connection-string-for-bootstrapping callback"
{:response res
:event :syncing/input-connection-string-for-bootstrapping})
(let [error (when (sync-utils/extract-error res)
(str "generic-error: " res))]
(when (some? error)
(let [response (transforms/json->clj res)
installation-id (:installationId response)
key-uid (:keyUID response)
error (:error response)]
(when (seq installation-id)
(rf/dispatch [:syncing/set-syncing-installation-id installation-id key-uid]))
(when (seq error)
(rf/dispatch [:toasts/upsert
{:type :negative
:text error}]))))
(rf/defn initiate-pairing-process
{:events [:syncing/initiate-pairing-process]}
[{:keys [db]}]
{:db (assoc db :syncing/pairing-process-initiated? true)})
(rf/defn set-syncing-installation-id
{:events [:syncing/set-syncing-installation-id]}
[{:keys [db]} installation-id key-uid]
{:db (assoc db
:syncing/key-uid key-uid
:syncing/installation-id installation-id)})
(rf/defn preflight-outbound-check-for-local-pairing
{:events [:syncing/preflight-outbound-check]}
[_ set-checks-passed]
@@ -96,6 +112,7 @@
(when (sync-utils/valid-connection-string? response)
(on-valid-connection-string response)
(rf/dispatch [:syncing/update-role constants/local-pairing-role-sender])
(rf/dispatch [:syncing/initiate-pairing-process])
(rf/dispatch [:hide-bottom-sheet])))]
(when-not (and error (string/blank? error))
(let [key-uid (get-in db [:profile/profile :key-uid])
@@ -1,11 +1,9 @@
(ns status-im.contexts.wallet.common.activity-tab.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[status-im.common.resources :as resources]
[status-im.constants :as constants]
[status-im.contexts.shell.jump-to.constants :as jump-to.constants]
[status-im.contexts.wallet.common.empty-tab.view :as empty-tab]
[utils.i18n :as i18n]
@@ -82,10 +80,6 @@
; :network-logo network-logo}
; :blur? false}])
(defn- section-header
[{:keys [title]}]
[quo/divider-date title])
(defn activity-item
[{:keys [transaction] :as activity}]
(case transaction
@@ -94,63 +88,20 @@
;; :mint [mint-activity activity]
nil))
(defn- pressable-text
[{:keys [on-press text]}]
[rn/text
{:style {:text-decoration-line :underline}
:on-press on-press}
text])
(defn view
[]
(let [theme (quo.theme/use-theme)
address (rf/sub [:wallet/current-viewing-account-address])
activity-list (rf/sub [:wallet/activities-for-current-viewing-account])
open-eth-chain-explorer (rn/use-callback
#(rf/dispatch [:wallet/navigate-to-chain-explorer
{:address address
:network constants/mainnet-network-name}])
[address])
open-oeth-chain-explorer (rn/use-callback
#(rf/dispatch [:wallet/navigate-to-chain-explorer
{:address address
:network constants/optimism-network-name}])
[address])
open-arb-chain-explorer (rn/use-callback
#(rf/dispatch [:wallet/navigate-to-chain-explorer
{:address address
:network constants/arbitrum-network-name}])
[address])]
[:<>
[quo/information-box
{:type :informative
:icon :i/info
:closable? false
:style {:margin-horizontal 20 :margin-vertical 8}}
[:<>
(str (i18n/label :t/wallet-activity-beta-message) " ")
[pressable-text
{:on-press open-eth-chain-explorer
:text (i18n/label :t/etherscan)}]
", "
[pressable-text
{:on-press open-oeth-chain-explorer
:text (i18n/label :t/op-explorer)}]
(str ", " (string/lower-case (i18n/label :t/or)) " ")
[pressable-text
{:on-press open-arb-chain-explorer
:text (i18n/label :t/arbiscan)}]
"."]]
(if (empty? activity-list)
[empty-tab/view
{:title (i18n/label :t/no-activity)
:description (i18n/label :t/empty-tab-description)
:image (resources/get-themed-image :no-activity theme)}]
[rn/section-list
{:sections activity-list
:sticky-section-headers-enabled false
:style {:flex 1
:padding-horizontal 8}
:content-container-style {:padding-bottom jump-to.constants/floating-shell-button-height}
:render-fn activity-item
:render-section-header-fn section-header}])]))
(let [theme (quo.theme/use-theme)
activity-list (rf/sub [:wallet/activities-for-current-viewing-account])]
(if (empty? activity-list)
[empty-tab/view
{:title (i18n/label :t/no-activity)
:description (i18n/label :t/empty-tab-description)
:image (resources/get-themed-image :no-activity theme)}]
[rn/section-list
{:sections activity-list
:sticky-section-headers-enabled false
:style {:flex 1
:padding-horizontal 8}
:content-container-style {:padding-bottom jump-to.constants/floating-shell-button-height}
:render-fn activity-item
:render-section-header-fn (fn [{:keys [title]}] [quo/divider-date title])}])))
@@ -14,20 +14,17 @@
(rf/reg-event-fx
:wallet-connect/init
(fn [{:keys [db]}]
(let [network-status (:network/status db)
web3-wallet-missing? (-> db :wallet-connect/web3-wallet boolean not)]
(if (and (= network-status :online) web3-wallet-missing?)
(do (log/info "Initialising WalletConnect SDK")
{:fx [[:effects.wallet-connect/init
{:on-success #(rf/dispatch [:wallet-connect/on-init-success %])
:on-fail #(rf/dispatch [:wallet-connect/on-init-fail %])}]]})
(let [network-status (:network/status db)]
(if (= network-status :online)
{:fx [[:effects.wallet-connect/init
{:on-success #(rf/dispatch [:wallet-connect/on-init-success %])
:on-fail #(rf/dispatch [:wallet-connect/on-init-fail %])}]]}
;; NOTE: when offline, fetching persistent sessions only
{:fx [[:dispatch [:wallet-connect/fetch-persisted-sessions]]]}))))
(rf/reg-event-fx
:wallet-connect/on-init-success
(fn [{:keys [db]} [web3-wallet]]
(log/info "WalletConnect SDK initialisation successful")
{:db (assoc db :wallet-connect/web3-wallet web3-wallet)
:fx [[:dispatch [:wallet-connect/register-event-listeners]]
[:dispatch [:wallet-connect/fetch-persisted-sessions]]]}))
@@ -35,9 +32,9 @@
(rf/reg-event-fx
:wallet-connect/reload-on-network-change
(fn [{:keys [db]} [is-connected?]]
(let [logged-in? (-> db :profile/profile boolean)]
(when (and is-connected? logged-in?)
(log/info "Re-Initialising WalletConnect SDK due to network change")
(let [logged-in? (-> db :profile/profile boolean)
web3-wallet-missing? (-> db :wallet-connect/web3-wallet boolean not)]
(when (and is-connected? logged-in? web3-wallet-missing?)
{:fx [[:dispatch [:wallet-connect/init]]]}))))
(rf/reg-event-fx
@@ -79,7 +76,6 @@
(if (and (not-empty session-networks) required-networks-supported?)
{:db (update db
:wallet-connect/current-proposal assoc
:response-sent? false
:request proposal
:session-networks session-networks
:address (or current-viewing-address
@@ -168,32 +164,23 @@
current-address (get-in db [:wallet-connect/current-proposal :address])
accounts (-> (partial wallet-connect-core/format-eip155-address current-address)
(map session-networks))
network-status (:network/status db)
expiry (get-in current-proposal [:params :expiryTimestamp])]
network-status (:network/status db)]
(if (= network-status :online)
{:db (assoc-in db [:wallet-connect/current-proposal :response-sent?] true)
:fx [(if (wc-utils/timestamp-expired? expiry)
[:dispatch
[:toasts/upsert
{:id :wallet-connect-proposal-expired
:type :negative
:text (i18n/label :t/wallet-connect-proposal-expired)}]]
[:effects.wallet-connect/approve-session
{:web3-wallet web3-wallet
:proposal current-proposal
:networks session-networks
:accounts accounts
:on-success (fn [approved-session]
(log/info "Wallet Connect session approved")
(rf/dispatch [:wallet-connect/reset-current-session-proposal])
(rf/dispatch [:wallet-connect/persist-session
approved-session]))
:on-fail (fn [error]
(log/error "Wallet Connect session approval failed"
{:error error
:event :wallet-connect/approve-session})
(rf/dispatch
[:wallet-connect/reset-current-session-proposal]))}])
{:fx [[:effects.wallet-connect/approve-session
{:web3-wallet web3-wallet
:proposal current-proposal
:networks session-networks
:accounts accounts
:on-success (fn [approved-session]
(log/info "Wallet Connect session approved")
(rf/dispatch [:wallet-connect/reset-current-session-proposal])
(rf/dispatch [:wallet-connect/persist-session approved-session]))
:on-fail (fn [error]
(log/error "Wallet Connect session approval failed"
{:error error
:event :wallet-connect/approve-session})
(rf/dispatch
[:wallet-connect/reset-current-session-proposal]))}]
[:dispatch [:dismiss-modal :screen/wallet.wallet-connect-session-proposal]]]}
{:fx [[:dispatch [:wallet-connect/no-internet-toast]]]}))))
@@ -227,10 +214,7 @@
(not version-supported?)
(i18n/label :t/wallet-connect-version-not-supported
{:version version})
:else
(i18n/label :t/something-went-wrong))}]]]}
{:version version}))}]]]}
{:fx [[:dispatch [:wallet-connect/pair scanned-text]]]}))))
;; We first load sessions from database, then we initiate a call to Wallet Connect SDK and
@@ -1,23 +1,10 @@
(ns status-im.contexts.wallet.wallet-connect.modals.common.header.style
(:require [quo.foundations.typography :as typography]))
(def ^:private line-height (:line-height typography/heading-1))
(ns status-im.contexts.wallet.wallet-connect.modals.common.header.style)
(def header-container
{:padding-vertical 12
:justify-content :flex-start
:align-items :center
:flex-direction :row
:flex-wrap :wrap
:row-gap 2})
{:padding-vertical 12})
(def word-container
{:height line-height
:justify-content :center})
(def header-dapp-name
{:margin-top -4})
(def dapp-container
{:margin-top 0
:height line-height})
(def account-container
{:height line-height})
(def header-account-name
{:padding-top 4})
@@ -1,31 +1,30 @@
(ns status-im.contexts.wallet.wallet-connect.modals.common.header.view
(:require [clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.wallet.wallet-connect.core :as core]
[status-im.contexts.wallet.wallet-connect.modals.common.header.style :as style]))
(:require
[quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.wallet.wallet-connect.core :as core]
[status-im.contexts.wallet.wallet-connect.modals.common.header.style :as style]
[utils.string]))
(defn view
[{:keys [label dapp account]}]
[rn/view {:style style/header-container}
(let [{:keys [name iconUrl url]} dapp
image-source (core/compute-dapp-icon-path iconUrl url)]
[rn/view {:style style/dapp-container}
[quo/summary-tag
{:type :dapp
:label name
:image-source image-source}]])
(for [word (string/split label #" ")]
^{:key word}
[rn/view {:style style/word-container}
[quo/text
{:size :heading-1
:weight :semi-bold}
(str " " word)]])
(let [{:keys [emoji customization-color name]} account]
[rn/view {:style style/account-container}
[quo/summary-tag
{:type :account
:emoji emoji
:label name
:customization-color customization-color}]])])
[rn/view
{:style style/header-container}
[quo/text
{:size :heading-1
:weight :semi-bold}
(let [{:keys [name iconUrl url]} dapp
image-source (core/compute-dapp-icon-path iconUrl url)]
[rn/view {:style style/header-dapp-name}
[quo/summary-tag
{:type :dapp
:label name
:image-source image-source}]])
(str " " label " ")
(let [{:keys [emoji customization-color name]} account]
[rn/view {:style style/header-account-name}
[quo/summary-tag
{:type :account
:emoji emoji
:label name
:customization-color customization-color}]])]])
@@ -7,5 +7,6 @@
[quo/page-nav
{:icon-name :i/close
:background :blur
:on-press #(rf/dispatch [:wallet-connect/dismiss-request-modal])
:on-press #(do (rf/dispatch [:wallet-connect/dismiss-request-modal])
(rf/dispatch [:wallet-connect/reject-session-request]))
:accessibility-label accessibility-label}])
@@ -22,7 +22,6 @@
network (rf/sub [:wallet-connect/current-request-network])
{:keys [max-fees-fiat-formatted
error-state]} (rf/sub [:wallet-connect/current-request-transaction-information])]
(rn/use-unmount #(rf/dispatch [:wallet-connect/on-request-modal-dismissed]))
[rn/view {:style (style/container bottom)}
[quo/gradient-cover {:customization-color customization-color}]
[page-nav/view
@@ -18,7 +18,6 @@
{:keys [customization-color]
:as account} (rf/sub [:wallet-connect/current-request-account-details])
dapp (rf/sub [:wallet-connect/current-request-dapp])]
(rn/use-unmount #(rf/dispatch [:wallet-connect/on-request-modal-dismissed]))
[rn/view {:style (style/container bottom)}
[quo/gradient-cover {:customization-color customization-color}]
[page-nav/view
@@ -21,7 +21,6 @@
network (rf/sub [:wallet-connect/current-request-network])
{:keys [max-fees-fiat-formatted
error-state]} (rf/sub [:wallet-connect/current-request-transaction-information])]
(rn/use-unmount #(rf/dispatch [:wallet-connect/on-request-modal-dismissed]))
[rn/view {:style (style/container bottom)}
[quo/gradient-cover {:customization-color customization-color}]
[page-nav/view
@@ -29,9 +29,7 @@
existing-event (get-in db [:wallet-connect/current-request :event])]
;; NOTE: make sure we don't show two requests at the same time
(when-not existing-event
{:db (-> db
(assoc-in [:wallet-connect/current-request :event] event)
(assoc-in [:wallet-connect/current-request :response-sent?] false))
{:db (assoc-in db [:wallet-connect/current-request :event] event)
:fx [(condp = method
constants/wallet-connect-eth-send-transaction-method
[:dispatch [:wallet-connect/process-eth-send-transaction]]
@@ -193,4 +191,4 @@
:method method
:wallet-connect-event event
:event :wallet-connect/on-processing-error})
{:fx [[:dispatch [:wallet-connect/dismiss-request-modal]]]})))
{:fx [[:dispatch [:wallet-connect/reject-session-request]]]})))
@@ -3,42 +3,31 @@
[react-native.wallet-connect :as wallet-connect]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core]
[status-im.contexts.wallet.wallet-connect.utils :as wc-utils]
[taoensso.timbre :as log]
[utils.i18n :as i18n]))
[taoensso.timbre :as log]))
(rf/reg-event-fx
:wallet-connect/respond-current-session
(fn [{:keys [db]} [password]]
(let [event (get-in db [:wallet-connect/current-request :event])
method (wallet-connect-core/get-request-method event)
screen (wallet-connect-core/method-to-screen method)
expiry (get-in event [:params :request :expiryTimestamp])]
(if (wc-utils/timestamp-expired? expiry)
{:fx [[:dispatch
[:toasts/upsert
{:id :new-wallet-account-created
:type :negative
:text (i18n/label :t/wallet-connect-request-expired)}]]
[:dispatch [:dismiss-modal screen]]]}
{:fx [(condp = method
constants/wallet-connect-personal-sign-method
[:dispatch [:wallet-connect/respond-sign-message password :personal-sign]]
method (wallet-connect-core/get-request-method event)]
{:fx [(condp = method
constants/wallet-connect-personal-sign-method
[:dispatch [:wallet-connect/respond-sign-message password :personal-sign]]
constants/wallet-connect-eth-sign-method
[:dispatch [:wallet-connect/respond-sign-message password :eth-sign]]
constants/wallet-connect-eth-sign-method
[:dispatch [:wallet-connect/respond-sign-message password :eth-sign]]
constants/wallet-connect-eth-send-transaction-method
[:dispatch [:wallet-connect/respond-send-transaction-data password]]
constants/wallet-connect-eth-send-transaction-method
[:dispatch [:wallet-connect/respond-send-transaction-data password]]
constants/wallet-connect-eth-sign-transaction-method
[:dispatch [:wallet-connect/respond-sign-transaction-data password]]
constants/wallet-connect-eth-sign-transaction-method
[:dispatch [:wallet-connect/respond-sign-transaction-data password]]
constants/wallet-connect-eth-sign-typed-method
[:dispatch [:wallet-connect/respond-sign-typed-data password :v1]]
constants/wallet-connect-eth-sign-typed-method
[:dispatch [:wallet-connect/respond-sign-typed-data password :v1]]
constants/wallet-connect-eth-sign-typed-v4-method
[:dispatch [:wallet-connect/respond-sign-typed-data password :v4]])]}))))
constants/wallet-connect-eth-sign-typed-v4-method
[:dispatch [:wallet-connect/respond-sign-typed-data password :v4]])]})))
(rf/reg-event-fx
:wallet-connect/respond-sign-message
@@ -106,29 +95,31 @@
:method method
:wallet-connect-event event
:event :wallet-connect/on-sign-error})
{:fx [[:dispatch [:wallet-connect/dismiss-request-modal]]]})))
{:fx [[:dispatch [:wallet-connect/reject-session-request]]
[:dispatch [:wallet-connect/dismiss-request-modal]]]})))
(rf/reg-event-fx
:wallet-connect/send-response
(fn [{:keys [db]} [{:keys [result error]}]]
(when-let [{:keys [id topic] :as event} (get-in db [:wallet-connect/current-request :event])]
(let [method (wallet-connect-core/get-request-method event)
web3-wallet (get db :wallet-connect/web3-wallet)]
{:db (assoc-in db [:wallet-connect/current-request :response-sent?] true)
:fx [[:effects.wallet-connect/respond-session-request
{:web3-wallet web3-wallet
:topic topic
:id id
:result result
:error error
:on-error (fn [error]
(log/error "Failed to send Wallet Connect response"
{:error error
:method method
:event :wallet-connect/send-response
:wallet-connect-event event}))
:on-success (fn []
(log/info "Successfully sent Wallet Connect response to dApp"))}]]}))))
(let [{:keys [id topic] :as event} (get-in db [:wallet-connect/current-request :event])
method (wallet-connect-core/get-request-method event)
web3-wallet (get db :wallet-connect/web3-wallet)]
{:fx [[:effects.wallet-connect/respond-session-request
{:web3-wallet web3-wallet
:topic topic
:id id
:result result
:error error
:on-error (fn [error]
(log/error "Failed to send Wallet Connect response"
{:error error
:method method
:event :wallet-connect/send-response
:wallet-connect-event event})
(rf/dispatch [:wallet-connect/reset-current-request]))
:on-success (fn []
(log/info "Successfully sent Wallet Connect response to dApp")
(rf/dispatch [:wallet-connect/reset-current-request]))}]]})))
(rf/reg-event-fx
:wallet-connect/dismiss-request-modal
@@ -148,18 +139,16 @@
(rf/reg-event-fx
:wallet-connect/reject-session-proposal
(fn [{:keys [db]} _]
(let [web3-wallet (get db :wallet-connect/web3-wallet)
{:keys [request response-sent?]} (:wallet-connect/current-proposal db)]
{:fx [(when-not response-sent?
[:effects.wallet-connect/reject-session-proposal
{:web3-wallet web3-wallet
:proposal request
:on-success #(log/info "Wallet Connect session proposal rejected")
:on-error #(log/error "Wallet Connect unable to reject session proposal")}])
(let [web3-wallet (get db :wallet-connect/web3-wallet)
current-proposal (get-in db [:wallet-connect/current-proposal :request])]
{:fx [[:effects.wallet-connect/reject-session-proposal
{:web3-wallet web3-wallet
:proposal current-proposal
:on-success #(log/info "Wallet Connect session proposal rejected")
:on-error #(log/error "Wallet Connect unable to reject session proposal")}]
[:dispatch [:wallet-connect/reset-current-session-proposal]]]})))
;; NOTE: Currently we only reject a session if the user dismissed a modal
;; without accepting the session first.
;; NOTE: Currently we only reject a session if the user rejected it
;; But this needs to be solidified to ensure other cases:
;; - Unsupported WC version
;; - Invalid params from dapps
@@ -167,11 +156,9 @@
;; - Failed processing of request
;; - Failed "responding" (signing or sending message/transaction)
(rf/reg-event-fx
:wallet-connect/on-request-modal-dismissed
(fn [{:keys [db]}]
{:fx [(when-not (get-in db [:wallet-connect/current-request :response-sent?])
[:dispatch
[:wallet-connect/send-response
{:error (wallet-connect/get-sdk-error
constants/wallet-connect-user-rejected-error-key)}]])
[:dispatch [:wallet-connect/reset-current-request]]]}))
:wallet-connect/reject-session-request
(fn [_ _]
{:fx [[:dispatch
[:wallet-connect/send-response
{:error (wallet-connect/get-sdk-error
constants/wallet-connect-user-rejected-error-key)}]]]}))
@@ -148,9 +148,10 @@
:button-two-label (i18n/label :t/decline)
:button-two-props {:type :grey
:accessibility-label :wc-deny-connection
:on-press #(rf/dispatch
[:dismiss-modal
:screen/wallet.wallet-connect-session-proposal])}
:on-press (fn []
(rf/dispatch [:navigate-back])
(rf/dispatch
[:wallet-connect/reject-session-proposal]))}
:button-one-label (i18n/label :t/connect)
:button-one-props {:customization-color customization-color
:type :primary
@@ -164,13 +165,11 @@
{:type :no-title
:background :blur
:icon-name :i/close
:on-press (rn/use-callback
#(rf/dispatch [:dismiss-modal :screen/wallet.wallet-connect-session-proposal]))
:on-press (rn/use-callback #(rf/dispatch [:navigate-back]))
:accessibility-label :wc-session-proposal-top-bar}])
(defn view
[]
(rn/use-unmount #(rf/dispatch [:wallet-connect/reject-session-proposal]))
[floating-button-page/view
{:footer-container-padding 0
:header [header]
-2
View File
@@ -2,7 +2,6 @@
(:require
["react-native" :refer (DevSettings LogBox NativeModules)]
[react-native.platform :as platform]
[status-im.setup.oops :as setup.oops]
[status-im.setup.schema :as schema]
[utils.re-frame :as rf]))
@@ -48,7 +47,6 @@
:utils/dispatch-later
:json-rpc/call})
(when ^:boolean js/goog.DEBUG
(setup.oops/setup!)
(schema/setup!)
(when platform/ios?
;; on Android this method doesn't work
-20
View File
@@ -1,20 +0,0 @@
(ns status-im.setup.oops
(:require [oops.config]))
(defn setup!
"Change oops defaults to warn and print instead of throwing exceptions during
development."
[]
(oops.config/update-current-runtime-config!
merge
{:error-reporting :console
:expected-function-value :warn
:invalid-selector :warn
:missing-object-key :warn
:object-is-frozen :warn
:object-is-sealed :warn
:object-key-not-writable :warn
:unexpected-empty-selector :warn
:unexpected-object-value :warn
:unexpected-punching-selector :warn
:unexpected-soft-selector :warn}))
+1 -4
View File
@@ -2,8 +2,7 @@
(:require
["bignumber.js" :as BigNumber]
[matcher-combinators.core :as matcher-combinators]
[matcher-combinators.model :as matcher.model]
[status-im.setup.oops :as setup.oops]))
[matcher-combinators.model :as matcher.model]))
;; We must implement Matcher in order for tests to work with the `match?`
;; directive.
@@ -17,5 +16,3 @@
:matcher-combinators.result/value actual}
{:matcher-combinators.result/type :mismatch
:matcher-combinators.result/value (matcher.model/->Mismatch this actual)})))
(setup.oops/setup!)
+37 -75
View File
@@ -6,9 +6,7 @@
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.contexts.communities.utils :as utils]
[status-im.contexts.profile.utils :as profile.utils]
[status-im.subs.chat.utils :as subs.utils]
[status-im.subs.contact.utils :as contact.utils]
[utils.i18n :as i18n]
[utils.money :as money]))
@@ -71,7 +69,7 @@
(fn [[_ community-id]]
[(re-frame/subscribe [:communities/community community-id])])
(fn [[{:keys [members]}] _]
(js-keys members)))
members))
(re-frame/reg-sub
:communities/community-chat-members
@@ -94,80 +92,51 @@
{}
public-keys))
(defn- sort-members-by-name-old
(defn- sort-members-by-name
[names descending? members]
(if descending?
(sort-by #(get names %) #(compare %2 %1) members)
(sort-by #(get names %) members)))
(sort-by #(get names (first %)) #(compare %2 %1) members)
(sort-by #(get names (first %)) members)))
(defn- sort-members-by-name
[names members-keys]
(let [forced-last-key "zzzzzz"
sort-keyfn (fn [k]
(if-let [[primary-name secondary-name] (get names k)]
(or (some-> primary-name
string/lower-case)
(some-> secondary-name
string/lower-case))
;; Sort unknown keys at the end.
forced-last-key))]
(sort-by sort-keyfn members-keys)))
;; This implementation is wrong, but since it's only used in a legacy view, we
;; can ignore it for now.
(re-frame/reg-sub :communities/sorted-community-members
(re-frame/reg-sub
:communities/sorted-community-members
(fn [[_ community-id]]
[(re-frame/subscribe [:profile/profile])
(re-frame/subscribe [:communities/community-members community-id])])
(let [profile (re-frame/subscribe [:profile/profile])
members (re-frame/subscribe [:communities/community-members community-id])]
[profile members]))
(fn [[profile members] _]
(let [names (keys->names members profile)]
(let [names (keys->names (keys members) profile)]
(->> members
(sort-members-by-name-old names false)
(sort-members-by-name names false)
(sort-by #(visibility-status-utils/visibility-status-order (get % 0)))))))
(re-frame/reg-sub :communities/chat-members
(re-frame/reg-sub
:communities/sorted-community-members-section-list
(fn [[_ community-id chat-id]]
[(re-frame/subscribe [:profile/public-key])
(re-frame/subscribe [:communities/community-chat-members community-id chat-id])
(re-frame/subscribe [:visibility-status-updates])
(re-frame/subscribe [:multiaccount/current-user-visibility-status])])
(fn [[profile-pub-key members-js visibility-status-updates my-status-update] [_ _ _ visibility-status]]
(let [members-keys (js-keys members-js)
online? (fn [public-key]
(let [{visibility-status-type :status-type}
(if (or (string/blank? profile-pub-key)
(= profile-pub-key public-key))
my-status-update
(get visibility-status-updates public-key))]
(subs.utils/online? visibility-status-type)))]
(filter (if (= :online visibility-status)
online?
(complement online?))
members-keys))))
(defn- names-by-key
[contacts profile public-keys]
(let [names (reduce (fn [acc k]
(if-let [contact (get contacts k)]
(assoc acc k (contact.utils/contact-two-names contact profile))
acc))
{}
public-keys)]
(assoc names
(:public-key profile)
[(profile.utils/displayed-name profile) nil])))
;; This is a potentially expensive subscription because we don't control how
;; many members and contacts exist in the app-db. Future improvements include
;; removing members from the payload and paginating them from status-go.
(re-frame/reg-sub :communities/chat-members-sorted
(fn [[_ community-id chat-id visibility-status]]
[(re-frame/subscribe [:profile/profile])
(re-frame/subscribe [:contacts/contacts-raw])
(re-frame/subscribe [:communities/chat-members community-id chat-id visibility-status])])
(fn [[profile contacts ^js members-keys]]
(sort-members-by-name (names-by-key contacts profile members-keys)
members-keys)))
(let [profile (re-frame/subscribe [:profile/profile])
members (re-frame/subscribe [:communities/community-chat-members
community-id chat-id])
visibility-status-updates (re-frame/subscribe
[:visibility-status-updates])
my-status-update (re-frame/subscribe
[:multiaccount/current-user-visibility-status])]
[profile members visibility-status-updates my-status-update]))
(fn [[profile members visibility-status-updates my-status-update] _]
(let [online? (fn [public-key]
(let [{visibility-status-type :status-type}
(if (or (string/blank? (:public-key profile))
(= (:public-key profile) public-key))
my-status-update
(get visibility-status-updates public-key))]
(subs.utils/online? visibility-status-type)))
names (keys->names (keys members) profile)]
(->> members
(sort-members-by-name names true)
keys
(group-by online?)
(map (fn [[k v]]
{:title (if k (i18n/label :t/online) (i18n/label :t/offline))
:data v}))))))
(re-frame/reg-sub
:communities/featured-contract-communities
@@ -208,13 +177,6 @@
(if (or (empty? @memo-communities-stack-items) (= view-id :communities-stack))
(let [grouped-communities (->> communities
vals
;; Remove data that can grow fast or is
;; reliably not needed to list communities.
;; We could use an allowlist of keys for
;; optimal performance of this sub, but
;; that's harder to maintain in case we miss
;; any key.
(map #(dissoc % :members :chats :token-permissions :tokens-metadata))
(group-by #(group-communities-by-status requests %))
merge-opened-communities
(map (fn [[k v]]
+58 -112
View File
@@ -11,8 +11,6 @@
[utils.re-frame :as rf]))
(def community-id "0x02b5bdaf5a25fcfe2ee14c501fab1836b8de57f61621080c3d52073d16de0d98d6")
(def channel-id "0x1-channel-id")
(def chat-id (str community-id channel-id))
(h/deftest-sub :communities
[sub-name]
@@ -459,117 +457,65 @@
(match? []
(rf/sub [sub-name community-id]))))))
(h/deftest-sub :communities/chat-members-sorted
(h/deftest-sub :communities/sorted-community-members-section-list
[sub-name]
(let [token-image-eth "data:image/jpeg;base64,/9j/2w"
channel-id-1 "89f98a1e-6776-4e5f-8626-8ab9f855253f"
channel-id-2 "a076358e-4638-470e-a3fb-584d0a542ce6"
chat-id-2 (str community-id channel-id-2)
member-id-1 "0x01"
member-id-2 "0x02"
visibility-status-updates
{member-id-1 {:status-type constants/visibility-status-always-online}
member-id-2 {:status-type constants/visibility-status-always-online}}
contacts
{member-id-1 {:display-name "John Marston"}
member-id-2 {:display-name "Arthur Morgan"}}
community {:id community-id
:permissions {:access 3}
:token-images {"ETH" token-image-eth}
:name "Community super name"
:chats {channel-id-1
{:description "x"
:emoji "🎲"
:permissions {:access 1}
:color "#88B0FF"
:name "random"
:categoryID "0c3c64e7-d56e-439b-a3fb-a946d83cb056"
:id channel-id-1
:position 4
:can-post? false
:members nil}
channel-id-2
{:description "General channel for the community"
:emoji "🥔"
:permissions {:access 1}
:color "#4360DF"
:name "general"
:categoryID "0c3c64e7-d56e-439b-a3fb-a946d83cb056"
:id channel-id-2
:position 0
:token-gated? true
:can-post? false
:members (clj->js {member-id-1 {"roles" [1]}
member-id-2 {"roles" [1]}
"0x05" {"roles" [1]}})}}
:members (js->clj {member-id-1 {"roles" [1]}
member-id-2 {"roles" [1]}
"0x03" {"roles" [1]}
"0x04" {"roles" [1]}})}]
(testing "returns sorted community members who are online"
(swap! rf-db/app-db assoc :contacts/contacts contacts)
(testing "returns sorted community members per online status"
(let [token-image-eth "data:image/jpeg;base64,/9j/2w"
channel-id-1 "89f98a1e-6776-4e5f-8626-8ab9f855253f"
channel-id-2 "a076358e-4638-470e-a3fb-584d0a542ce6"
chat-id-1 (str community-id channel-id-1)
chat-id-2 (str community-id channel-id-2)
community {:id community-id
:permissions {:access 3}
:token-images {"ETH" token-image-eth}
:name "Community super name"
:chats {channel-id-1
{:description "x"
:emoji "🎲"
:permissions {:access 1}
:color "#88B0FF"
:name "random"
:categoryID "0c3c64e7-d56e-439b-a3fb-a946d83cb056"
:id channel-id-1
:position 4
:can-post? false
:members nil}
channel-id-2
{:description "General channel for the community"
:emoji "🥔"
:permissions {:access 1}
:color "#4360DF"
:name "general"
:categoryID "0c3c64e7-d56e-439b-a3fb-a946d83cb056"
:id channel-id-2
:position 0
:token-gated? true
:can-post? false
:members {"0x01" {"roles" [1]}
"0x02" {"roles" [1]}
"0x05" {"roles" [1]}}}}
:members {"0x01" {"roles" [1]}
"0x02" {"roles" [1]}
"0x03" {"roles" [1]}
"0x04" {"roles" [1]}}
:can-request-access? false
:outroMessage "bla"
:verified false}]
(swap! rf-db/app-db assoc-in [:communities community-id] community)
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
(swap! rf-db/app-db assoc :visibility-status-updates visibility-status-updates)
(is (= [member-id-2 member-id-1]
(rf/sub [sub-name community-id chat-id-2 :online]))))
(testing "returns sorted community members per offline status"
(swap! rf-db/app-db assoc-in [:communities community-id] community)
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
(swap! rf-db/app-db assoc :visibility-status-updates visibility-status-updates)
(is (= ["0x05"] (rf/sub [sub-name community-id chat-id-2 :offline]))))))
(h/deftest-sub :communities/chat-members
[sub-name]
(let [member-1-id "0x1-member"
member-2-id "0x2-member"
visibility-status-updates
{member-1-id {:status-type constants/visibility-status-always-online}
member-2-id {:status-type constants/visibility-status-always-online}}
communities
{community-id {:id community-id
:chats {channel-id {:token-gated? false
:members (clj->js {member-2-id {}})}}
:members (clj->js {member-1-id {}
member-2-id {}})}}]
(testing "members from non token-gated channels and online"
(swap! rf-db/app-db assoc :visibility-status-updates visibility-status-updates)
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
(swap! rf-db/app-db assoc :communities communities)
;; When channel is not token-gated, all community members are considered.
(is (= [member-1-id member-2-id]
(rf/sub [sub-name community-id chat-id :online]))))
(testing "members from token-gated channels and online"
(swap! rf-db/app-db assoc :visibility-status-updates visibility-status-updates)
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
(swap! rf-db/app-db assoc
:communities
(assoc-in communities [community-id :chats channel-id :token-gated?] true))
;; When channel is token-gated, only its members are considered.
(is (= [member-2-id]
(rf/sub [sub-name community-id chat-id :online]))))
(testing "members from token-gated channels and offline"
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
(swap! rf-db/app-db assoc
:communities
(assoc-in communities [community-id :chats channel-id :token-gated?] true))
(is (= [member-2-id] (rf/sub [sub-name community-id chat-id :offline]))))
(testing "members from non token-gated channels and offline"
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
(swap! rf-db/app-db assoc :communities communities)
(is (= [member-1-id member-2-id]
(rf/sub [sub-name community-id chat-id :offline]))))))
:visibility-status-updates
{"0x01" {:status-type constants/visibility-status-always-online}
"0x02" {:status-type constants/visibility-status-always-online}})
(testing "a non-token gated community should look at all members of a community"
(is (= [{:title (i18n/label :t/online)
:data ["0x01" "0x02"]}
{:title (i18n/label :t/offline)
:data ["0x03" "0x04"]}]
(rf/sub [sub-name community-id chat-id-1]))))
(testing "a token gated community should use the members option in the channel"
(is (= [{:title (i18n/label :t/online)
:data ["0x01" "0x02"]}
{:title (i18n/label :t/offline)
:data ["0x05"]}]
(rf/sub [sub-name community-id chat-id-2])))))))
+55 -6
View File
@@ -2,15 +2,29 @@
(:require
[clojure.set :as set]
[clojure.string :as string]
[legacy.status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils]
[quo.theme]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.contexts.profile.utils :as profile.utils]
[status-im.subs.chat.utils :as chat.utils]
[status-im.subs.contact.utils :as contact.utils]
[utils.address :as address]
[utils.collection]
[utils.i18n :as i18n]))
(defn query-chat-contacts
[{:keys [contacts]} all-contacts query-fn]
(let [participant-set (into #{} (filter identity) contacts)]
(query-fn (comp participant-set :public-key) (vals all-contacts))))
(re-frame/reg-sub
::query-current-chat-contacts
:<- [:chats/current-chat]
:<- [:contacts/contacts]
(fn [[chat contacts] [_ query-fn]]
(query-chat-contacts chat contacts query-fn)))
(re-frame/reg-sub
:multiaccount/profile-pictures-show-to
:<- [:profile/profile]
@@ -110,6 +124,15 @@
sort
vals)))
(re-frame/reg-sub
:contacts/sorted-contacts
:<- [:contacts/active]
(fn [active-contacts]
(->> active-contacts
(sort-by :primary-name)
(sort-by
#(visibility-status-utils/visibility-status-order (:public-key %))))))
(re-frame/reg-sub
:contacts/sorted-and-grouped-by-first-letter
:<- [:contacts/active]
@@ -127,6 +150,12 @@
{:title title
:data data})))))
(re-frame/reg-sub
:contacts/active-count
:<- [:contacts/active]
(fn [active-contacts]
(count active-contacts)))
(re-frame/reg-sub
:contacts/blocked
:<- [:contacts/contacts]
@@ -142,6 +171,12 @@
(fn [contacts]
(into #{} (map :public-key contacts))))
(re-frame/reg-sub
:contacts/blocked-count
:<- [:contacts/blocked]
(fn [blocked-contacts]
(count blocked-contacts)))
(defn public-key-and-ens-name->new-contact
[public-key ens-name]
(let [contact {:public-key public-key}]
@@ -191,8 +226,24 @@
(fn [[_ contact-identity] _]
[(re-frame/subscribe [:contacts/contact-by-identity contact-identity])
(re-frame/subscribe [:profile/profile])])
(fn [[contact profile] [_ _]]
(contact.utils/contact-two-names contact profile)))
(fn [[{:keys [primary-name] :as contact}
{:keys [public-key preferred-name display-name name]}]
[_ contact-identity]]
[(if (= public-key contact-identity)
(cond
(not (string/blank? preferred-name)) preferred-name
(not (string/blank? display-name)) display-name
(not (string/blank? primary-name)) primary-name
(not (string/blank? name)) name
:else public-key)
(profile.utils/displayed-name contact))
(:secondary-name contact)]))
(re-frame/reg-sub
:contacts/all-contacts-not-in-current-chat
:<- [::query-current-chat-contacts remove]
(fn [contacts]
(filter :added? contacts)))
(defn get-all-contacts-in-group-chat
[members admins contacts {:keys [public-key preferred-name name display-name] :as current-account}]
@@ -249,10 +300,8 @@
:<- [:multiaccount/contact]
(fn [[contacts multiaccount] [_ address]]
(if (address/address= address (:public-key multiaccount))
(merge (contact.utils/build-contact-from-public-key address)
multiaccount)
(or (find-contact-by-address contacts address)
(contact.utils/build-contact-from-public-key address)))))
multiaccount
(find-contact-by-address contacts address))))
(re-frame/reg-sub
:contacts/contact-customization-color-by-address
+2 -22
View File
@@ -1,10 +1,8 @@
(ns status-im.subs.contact.utils
(:require
[clojure.string :as string]
[native-module.core :as native-module]
[status-im.common.pixel-ratio :as pixel-ratio]
[status-im.constants :as constants]
[status-im.contexts.profile.utils :as profile.utils]
[utils.address :as address]))
(defn replace-contact-image-uri
@@ -46,29 +44,11 @@
(assoc contact :images images)))
(defn- build-contact-from-public-key*
(defn build-contact-from-public-key
[public-key]
(when public-key
(let [compressed-key (native-module/serialize-legacy-key public-key)]
{:public-key public-key
:compressed-key compressed-key
:primary-name (address/get-shortened-compressed-key (or compressed-key public-key))})))
(def build-contact-from-public-key
"The result of this function is stable because it relies exclusively on the
public key, but it's not cheap to be performed hundreds of times in a row,
such as when displaying a long list of channel members."
(memoize build-contact-from-public-key*))
(defn contact-two-names
[{:keys [primary-name] :as contact}
{:keys [public-key preferred-name display-name name]}]
[(if (= public-key (:public-key contact))
(cond
(not (string/blank? preferred-name)) preferred-name
(not (string/blank? display-name)) display-name
(not (string/blank? primary-name)) primary-name
(not (string/blank? name)) name
:else public-key)
(profile.utils/displayed-name contact))
(:secondary-name contact)])
+1 -1
View File
@@ -187,7 +187,7 @@
:preferred-name "Preferred Name"}
:contacts/contacts
{profile-key {:primary-name "Primary Name" :public-key profile-key}
{profile-key {:primary-name "Primary Name"}
"contact-key" {:secondary-name "Secondary Name"}})
(is (= ["Preferred Name" nil] (rf/sub [sub-name profile-key])))
+5
View File
@@ -251,6 +251,11 @@
(fn [{:keys [preview-privacy?]}]
(boolean preview-privacy?)))
(re-frame/reg-sub :profile/installation-id
:<- [:profile/profile]
(fn [{:keys [installation-id]}]
installation-id))
(defn- replace-multiaccount-image-uri
[profile ens-names port font-file avatar-opts theme]
(let [{:keys [key-uid ens-name? images
+3 -4
View File
@@ -6,15 +6,14 @@
prefer to use it for more general purpose concepts, such as the re-frame event
layer."
(:require-macros test-helpers.unit)
(:require
(:require ;; We must require this namespace to register the custom cljs.test
;; directive `match-strict?`.
[re-frame.core :as rf]
[re-frame.db :as rf-db]
[re-frame.events :as rf-events]
[re-frame.registrar :as rf-registrar]
[re-frame.subs :as rf-subs]
[taoensso.timbre :as log]
;; We must require this namespace to register the custom cljs.test directive `match-strict?`.
[taoensso.timbre :as log] ;; We must require this namespace to register the custom cljs.test
test-helpers.matchers))
(defn db
-28
View File
@@ -15,34 +15,6 @@
(defn clj->json [data] (clj->pretty-json data 0))
(defn <-js-map
"Shallowly transforms JS Object keys/values with `key-fn`/`val-fn`.
Returns nil if `m` is not an instance of `js/Object`.
Implementation taken from `js->clj`, but with the ability to customize how
keys and/or values are transformed in one loop.
This function is useful when you don't want to recursively apply the same
transformation to keys/values. For example, many maps in the app-db are
indexed by ID, like `community.members`. If we convert the entire community
with (js->clj m :keywordize-keys true), then IDs will be converted to
keywords, but we want them as strings. Instead of transforming to keywords and
then transforming back to strings, it's better to not transform them at all.
"
([^js m]
(<-js-map m nil))
([^js m {:keys [key-fn val-fn]}]
(when (identical? (type m) js/Object)
(persistent!
(reduce (fn [r k]
(let [v (oops/oget+ m k)
new-key (if key-fn (key-fn k v) k)
new-val (if val-fn (val-fn k v) v)]
(assoc! r new-key new-val)))
(transient {})
(js-keys m))))))
(defn js-stringify
[js-object spaces]
(.stringify js/JSON js-object nil spaces))
-37
View File
@@ -1,37 +0,0 @@
(ns utils.transforms-test
(:require
[cljs.test :refer [are deftest is testing]]
[utils.transforms :as sut]))
(defn equals-as-json
[m1 m2]
(= (js/JSON.stringify (clj->js m1))
(js/JSON.stringify (clj->js m2))))
(deftest <-js-map-test
(testing "without transforming keys/values"
(are [expected m]
(is (equals-as-json expected (sut/<-js-map m)))
nil nil
nil #js []
#js {} #js {}
#js {"a" 1 "b" 2} #js {"a" 1 "b" 2}))
(testing "with key/value transformation"
(is (equals-as-json {"aa" [1] "bb" [2]}
(sut/<-js-map #js {"a" 1 "b" 2}
{:key-fn (fn [k] (str k k))
:val-fn (fn [_ v] (vector v))}))))
(testing "it is non-recursive"
(is (equals-as-json {:a 1 :b #js {"c" 3}}
(sut/<-js-map #js {"a" 1 "b" #js {"c" 3}}
{:key-fn (fn [k] (keyword k))}))))
(testing "value transformation based on the key"
(is (equals-as-json {"a" 1 "b" "banana"}
(sut/<-js-map #js {"a" 1 "b" 2}
{:val-fn (fn [k v]
(if (= "b" k)
"banana"
v))})))))
+3 -3
View File
@@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "f32d15cb887496ce55ca3c958e987ff104c83a8a",
"commit-sha1": "f32d15cb887496ce55ca3c958e987ff104c83a8a",
"src-sha256": "1x7p0pia6402hfyd4pnihz76g7pydby4isvmzmsdyl0hl1wn9q0b"
"version": "feat/enable_sync_fallback",
"commit-sha1": "880152abcac69bb7cf29088ce413d2c4e01be412",
"src-sha256": "067pj4xx0bph9573sdk57hdhq0xkaaz1zmgkk09d27a5fz2rdm4n"
}
+11 -4
View File
@@ -8,7 +8,6 @@ from os import environ
from sys import argv
import emoji
import pytest
import requests
from support.base_test_report import BaseTestReport
@@ -140,12 +139,20 @@ class TestrailReport(BaseTestReport):
for category in test_cases['pr']:
for case in self.get_cases([test_cases['pr'][category]]):
case_ids.append(case['id'])
case_ids.extend([703133, 702742, 702745, 702843])
# elif 'nightly' in argv:
else:
case_ids.extend([703133, 702742, 702745])
elif 'nightly' in argv:
for category in test_cases['nightly']:
for case in self.get_cases([test_cases['nightly'][category]]):
case_ids.append(case['id'])
elif 'upgrade' in argv and 'not upgrade' not in argv:
for case in self.get_cases([test_cases['upgrade']['general']]):
case_ids.append(case['id'])
else:
for phase in test_cases:
if phase != 'upgrade':
for category in test_cases[phase]:
for case in self.get_cases([test_cases[phase][category]]):
case_ids.append(case['id'])
return case_ids
def add_results(self):
@@ -253,7 +253,9 @@ class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):
self.home.navigate_back_to_home_view()
self.home.just_fyi("Turn off testnet in the profile settings")
profile = self.home.profile_button.click()
profile.switch_network()
profile.advanced_button.scroll_and_click()
profile.testnet_mode_toggle.click()
profile.ok_button.click()
self.sign_in.sign_in()
self.home.just_fyi("Check Discover Communities content")
@@ -366,7 +368,6 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
self.errors.append("Default username '%s' is not shown next to the received message" % self.username_1)
self.errors.verify_no_errors()
@marks.smoke
@marks.testrail_id(702843)
def test_community_message_edit(self):
message_before_edit, message_after_edit = 'Message BEFORE edit', "Message AFTER edit 2"
+13 -8
View File
@@ -304,6 +304,7 @@ class ProfileView(BaseView):
self.advanced_button = AdvancedButton(self.driver)
self.mutual_contact_request_switcher = Button(self.driver, accessibility_id="mutual-contact-requests-switch")
## Network
self.network_settings_button = Button(self.driver, accessibility_id="network-button")
self.active_network_name = Text(self.driver,
xpath="//android.widget.TextView[contains(@text,'with upstream RPC')]")
self.plus_button = Button(self.driver, xpath="(//android.widget.ImageView[@content-desc='icon'])[2]")
@@ -360,18 +361,22 @@ class ProfileView(BaseView):
self.profile_password_button = Button(self.driver, accessibility_id="icon, Password, label-component, icon")
self.profile_messages_button = Button(self.driver, accessibility_id="icon, Messages, label-component, icon")
self.profile_blocked_users_button = Button(self.driver, accessibility_id="Blocked users, label-component, icon")
self.profile_wallet_button = Button(self.driver, accessibility_id="icon, Wallet, label-component, icon")
self.network_settings_button = Button(self.driver, accessibility_id="Network settings, label-component, icon")
self.profile_legacy_button = Button(self.driver,
accessibility_id="icon, Legacy settings, label-component, icon")
self.testnet_mode_toggle = Button(self.driver, accessibility_id="icon, Testnet mode, label-component")
self.confirm_testnet_mode_change_button = Button(self.driver, accessibility_id="confirm-testnet-mode-change")
self.testnet_mode_toggle = Button(self.driver,
xpath="//*[@content-desc='test-networks-enabled']/android.widget.Switch")
def switch_network(self):
self.profile_wallet_button.click()
def switch_network(self, network='Mainnet with upstream RPC'):
self.driver.info("## Switch network to '%s'" % network, device=False)
self.advanced_button.click()
self.network_settings_button.click()
self.testnet_mode_toggle.click()
self.confirm_testnet_mode_change_button.click()
network_button = Button(self.driver, xpath="//*[@text='%s']" % network)
network_button.scroll_and_click()
self.connect_button.click_until_presence_of_element(self.confirm_button)
self.confirm_button.click_until_absense_of_element(self.confirm_button)
from views.sign_in_view import SignInView
SignInView(self.driver).sign_in()
self.driver.info("## Network is switched successfully!", device=False)
def open_contact_from_profile(self, username):
self.driver.info("Opening profile of '%s' via Contacts" % username)
+4 -6
View File
@@ -142,7 +142,6 @@
"approved-amount-symbol": "Approved {{amount}} {{symbol}}",
"approving-amount-symbol": "Approving {{amount}} {{symbol}}...",
"apr": "Apr",
"arbiscan": "Arbiscan",
"arbitrum": "Arbitrum",
"are-not-allowed": "{{check} are not allowed",
"are-you-sure": "Are you sure?",
@@ -358,6 +357,7 @@
"check-before-syncing-doc-checkbox-2": "Make sure you are logged in on the other device",
"check-before-syncing-doc-checkbox-3": "Disable the firewall and VPN on your devices",
"check-before-syncing-doc-description": "To sync your devices successfully, make sure to check and complete these steps:",
"check-new-device": "A new device has been detected. You can see device ID below and on your other device. Only confirm the request if device ID matches.",
"check-on-block-explorer": "Check on block explorer",
"check-on-opensea": "Check on opensea",
"check-your-account-balance-and-activity": "Check your account balance and activity",
@@ -976,7 +976,6 @@
"ethereum-address": "Ethereum address",
"ethereum-node-started-incorrectly-description": "Ethereum node was started with incorrect configuration, application will be stopped to recover from that condition. Configured network id = {{network-id}}, actual = {{fetched-network-id}}",
"ethereum-node-started-incorrectly-title": "Ethereum node started incorrectly",
"etherscan": "Etherscan",
"etherscan-lookup": "Look up on Etherscan",
"everyone": "Everyone",
"everyone-mention": "everyone",
@@ -1628,6 +1627,7 @@
"new-community-title": "New community",
"new-contact": "New contact",
"new-contract": "New Contract",
"new-device-detected": "A new device has been detected. You can see device ID below and on your other device. Only confirm the request if device ID matches.",
"new-favourite": "New favourite",
"new-group": "New group",
"new-group-chat": "New group chat",
@@ -1762,7 +1762,6 @@
"oops-this-qr-does-not-contain-an-address": "Oops! This QR does not contain an address",
"oops-wrong-password": "Oops, wrong password!",
"oops-wrong-word": "Oops! Wrong word",
"op-explorer": "OP Explorer",
"open": "Open",
"open-chat": "Open chat",
"open-dapp": "Open ÐApp",
@@ -1793,12 +1792,14 @@
"page-camera-request-blocked": "camera requests blocked. To enable camera requests go to Settings",
"page-would-like-to-use-camera": "would like to use your camera",
"pair": "Pair",
"pair-and-sync": "Pair and Sync",
"pair-card": "Pair to this device",
"pair-code": "Pair code",
"pair-code-explanation": "Pairs card to a different device (up to 5) to unlock keys and sign transactions with the same Keycard",
"pair-code-placeholder": "Pair code...",
"pair-device-toast": "Device successfully paired",
"pair-devices": "Pair devices",
"pair-new-device-and-sync": "Pair new device and sync profile",
"pair-this-card": "Pair this card",
"pair-this-device": "Advertise device",
"pair-this-device-description": "Pair your devices to sync contacts and chats between them",
@@ -2623,7 +2624,6 @@
"wakuv2-node-format": "/ip4/{node-ip}/tcp/{port}/p2p/{id}",
"wakuv2-settings": "Waku v2 settings",
"wallet": "Wallet",
"wallet-activity-beta-message": "Activity is in beta. If transactions are missing, check",
"wallet-address": "Wallet address",
"wallet-asset": "Asset",
"wallet-assets": "Assets",
@@ -2638,10 +2638,8 @@
"wallet-connect-networks-not-supported": "{{dapp}} requires an unsupported network.",
"wallet-connect-no-internet-warning": "Oops, you have no internet. Try again later!",
"wallet-connect-proposal-description": "By connecting you allow {{name}} to retrieve your account address and enable Web3",
"wallet-connect-proposal-expired": "WalletConnect proposal has expired",
"wallet-connect-proposal-title": "Would like to connect with your wallet",
"wallet-connect-qr-expired": "WalletConnect QR has expired",
"wallet-connect-request-expired": "WalletConnect request has expired",
"wallet-connect-send-transaction-header": "wants you to send this transaction with",
"wallet-connect-send-transaction-warning": "Send transactions only if you trust the dApp",
"wallet-connect-sign-message-header": "wants you to sign the message with",