Compare commits

..
Author SHA1 Message Date
Prem Chaitanya Prathi 387d3818c5 chore: pick go-waku updates 2024-07-26 16:33:46 +05:30
Prem Chaitanya Prathi 849330dfc1 chore: take filter ping related changes from status-go 2024-07-26 16:33:46 +05:30
John Ngei 737d8c4d53 rename sub to fix error when requesting to join community (#20868) 2024-07-26 11:38:49 +02:00
Parvesh Monu 3aa7e103f7 Sync process is blocked on Enabled notifications screen (#20883) 2024-07-26 13:48:38 +05:30
Icaro Motta c1d2d44da4 perf: Fix app freeze after login (#20729)
We do a few things to reduce the initial load and make the app more responsive
after login. The scenario we are covering is a user who joined communities with
a large number of members and/or which contain token-gated channels with many
members.

- Related to https://github.com/status-im/status-mobile/issues/20283
- Related to https://github.com/status-im/status-mobile/issues/20285

- Optimize how we convert a community from JS to CLJS. Community members and
  chat members are no longer transformed to CLJS, they are kept as JS. Read more
  details below.
- Delay processing lower-priority events by creating a third login phase. The
  goal is to not put on the same queue we process communities less important
  events, like fetching the count of unread notifications. Around 15 events
  could be delayed without causing trouble (and this further prevent a big chain
  of more events to be dispatched right after login).
- Tried to use re-frame's flush-dom metadata, but removed due to uncertainty,
  check out the discussion:
  https://github.com/status-im/status-mobile/pull/20729#discussion_r1683047969
  Use re-frame’s support for the flush-dom metadata whenever a signal arrives.
  According to the official documentation, this should tell re-frame to only
  process the event after the UI has been updated. It’s hard to say if this
  makes any difference, but the theory is sound.
- Reduce the amount of data returned to the subscription that renders a list of
  communities. We were returning too much, like all members, chats, token
  permissions, etc.

Other things I fixed or improved along the way:

- Because members are now stored as JS, I took the opportunity to fix how
  members are sorted when they are listed.
- Removed a few unused subs.
- Configured oops to not throw during development (in production the behavior is
  to never throw). This means oops is now safe to be used instead of interop
  that can mysteriously fail in advanced compilation.
- Show compressed key instead of public key in member list for the account
  currently logged in.

Technical details

The number one reason affecting the freeze after login was coming from
converting thousands of members inside communities and also because we were
doing it in an inefficient way using clojure.walk/stringify-keys. We shouldn't
also transform that much data on the client as the parent issue created by
flexsurfer correctly recommends. Ever since PR
https://github.com/status-im/status-mobile/pull/20414 was merged, status-go
doesn't return members in open channels, which greatly helps, for example, to
load the Status community. The problem still exists for communities with
token-gated channels with many members.

The current code in develop does something quite inefficient: it fetches the
communities, then transforms them recursively with js->clj and keywordizes keys,
then transforms again all the potentially thousands of member IDs back to
strings. This PR changes this. We now shallowly convert a community and ignore
members because they can grow too fast. From artificial benchmarks simulating
many members in token-gated channels, or communities with thousands of members,
the improvement is noticeable.

You will only really notice improvements if you have spectated or joined a
community with 1000+ members and/or a community with many token-gated channels,
each containing perhaps hundreds of members.

What's the ideal solution?

We should consider removing community members and channel members from the
community entity returned by status-go entirely. The members should be a
separate resource and paginated so that the client doesn't need to worry
about the number of members, for the most part.
2024-07-25 21:23:08 -03:00
Yevheniia Berdnyk 0fed8113d1 e2e: updated testnet switching and added one test into smoke 2024-07-25 23:46:13 +03:00
Ulises Manuel 53c35cb554 fix(wallet): Linear gradient exception on invalid colors for watched account cards (#20854)
* Create a linear-gradient wrapper to avoid crashes and notify color errors

* Fix references to the flamingo color

* Add customization color fallback
2024-07-25 13:35:46 -06:00
Mohamed Javid be82365542 chore(settings)_: Remove testnet toggle from legacy advanced settings (#20875)
This commit removes two toggles from legacy advanced settings

- "Testnet mode" - duplicate toggle as the user can switch to testnet from wallet settings
- "Enable Goerli as test network" - Goerli is depreciated and wallet services use Sepolia testnet as default. 

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-07-25 21:41:57 +05:30
Mohamed Javid eae8a65597 feat(wallet)_: Add beta info box in activity tab (#20873)
This commit adds the beta info box with links to chain explorers to the activity tab in the wallet.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-07-25 21:28:30 +05:30
Lungu Cristian fe54a25a3a fix: not clearing network & web3-wallet on logout (#20886) 2024-07-25 18:37:26 +03:00
Alexander 15a4219ef4 Reject wallet-connect request by dragging the modal down (#20763) (#20836)
* Reject wallet-connect request by dragging the modal down (#20763)

* Fix
2024-07-25 16:51:10 +02:00
Alexander 2ffbdac89e WalletConnect show expired toast (#20857)
* WalletConnect show expired toast

* Fixes

* Fix

* Post-rebase fix
2024-07-25 16:11:31 +02:00
Parvesh Monu 402eb8397d fix Issue with scrolling WalletConnect transaction on Android (#20867) 2024-07-25 18:22:17 +05:30
Lungu Cristian ff88049a08 Fix WalletConnect header alignment on Android (#20860)
* fix: header align and max width for summary-tag

* fix: standard-title show ellipse when text is long

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