Compare commits

..
Author SHA1 Message Date
alwx 3ff4b8d8b6 Fixes 2024-07-23 14:20:58 +02:00
alwx f775010691 WalletConnect request header UI is misaligned 2024-07-23 14:17:39 +02:00
Parvesh Monu 540094c229 fix Missing 'Mark as read' option on community channels (#20838) 2024-07-22 22:55:50 +05:30
Alexander 08b65cbcb7 Proper handling of disconnection, both from the dapp itself and from the list of connected dapps (#20817) 2024-07-22 15:20:52 +02:00
AlexanderandLungu Cristian d623cc8444 Only show the Wallet Connect requests for the logged in user (#20815)
Co-authored-by: Lungu Cristian <lungucristian95@gmail.com>
2024-07-22 14:57:45 +02:00
Brian Sztamfater 4b7c906df2 feat(swap): set spending cap screen (#20727)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-07-22 08:50:29 -03:00
Omar Basem 3b446963ef fix: assets sort order on bridge and assets screen (#20813)
* fix: assets sort order on bridge and assets screen (#20813)
2024-07-22 14:16:40 +04:00
Icaro Motta cec46f985a chore(style): Sort JSON keys in translations/en.json (#20785)
Sorts keys (non-recursively) in translations/en.json. The solution uses
the prettier plugin https://github.com/Gudahtt/prettier-plugin-sort-json.

We only format translations/en.json because it’s the only file we need to
manually change.

- Dependency added: prettier-plugin-sort-json version 4.0.0
- Dependency upgraded: prettier, from version 2.8.8 to 3.3.3.
2024-07-21 22:39:50 -03:00
Volodymyr Kozieiev 06b2996167 Do not update disabled networks when receiver network updated (#20756) 2024-07-19 15:10:13 +01:00
Lungu Cristian 02e24208db Wallet Connect transactions (#20755)
* feat: updated signing endpoints and refactor

https://github.com/status-im/status-go/compare/6e056348...e8aec741

* fix: using the generic warning at the bottom

* fix: show parsed transaction parameters

* feat: adding fees to transactions

https://github.com/status-im/status-go/compare/6e056348...b2e5e7a8

* feat: added fees

* feat: added eip-1559 fee estimation & tx priority

* feat: added fees and failed processing handling

* fix: show testnet name in the request

* fix: address review comments

* feat: added max-fee color when not enough balance

* ref: broke down tx fees subscription

* fix: handle gas estimation on status-go

https://github.com/status-im/status-go/compare/1ef2434b...5389f281

* fix: don't overwrite dynamic fees if already there

* fix: malli schema and review comments

* fix: addressed review comments 1

* fix: addressed comments 2

* fix: removed unused require

* fix: addressed QA review

https://github.com/status-im/status-go/compare/484b8aca...d07f9b5b

* fix: requests being shown simultaneously

* fix: removed support for eth_signTransaction
2024-07-19 14:16:51 +03:00
frank 84b8943fd4 fix_:still return the image when image server is unable to get public key (#20538)
https://github.com/status-im/status-go/compare/cba3ac57...4a43b2b2
2024-07-19 17:17:53 +08:00
Ajay Sivan c52b3e457e Wallet/Swap Input Component (#20318) 2024-07-19 01:00:38 -07:00
Omar Basem 21cc8a199a fix: button width (#20792)
* fix: button width (#20792)
2024-07-19 08:13:50 +04:00
Yevheniia Berdnyk 59374543ef e2e: disabled activity verification in wallet tests 2024-07-18 19:45:20 +03:00
Alexander 9f84a7b912 A pack of bugfixes for the user story "Connect to dApps" (#20711) 2024-07-18 14:57:53 +02:00
Vit∀ly Vlasov 2b0847ef76 fix_: ui part for share-all-future-addresses storage (PR #20549) (#20549)
Related status-go PR: https://github.com/status-im/status-go/pull/5354

https://github.com/status-im/status-go/compare/6e056348...8458cafe

Signed-off-by: Vitaly Vlasov <mail@vitv.ly>
2024-07-18 15:36:38 +03:00
Yevheniia Berdnyk 97b19c5440 e2e: fixes for wallet and deep/universal links tests 2024-07-18 00:52:06 +03:00
Icaro Motta b3e88508ac chore(activity-center): Various small refactors (#20690)
Potentially a solution to https://github.com/status-im/status-mobile/issues/15706

- [x] Fixes swipe button on Android and iOS.
- [x] Performance: we now subscribe only to the minimum from each community.
      This could be the reason the AC would lag as described in the parent
      issue.
- [x] Performance: was able to use flex and removed swipe button height
      calculation that was using `onLayout` and was causing a re-render.
- [x] Performance: reduced the initial number of items to render in the flatlist
      from 10 to 7.
- [x] Performance: delay rendering the heavy list of notification components.
      See in the video below how slow it is to open the AC with just 6
      notifications and that the opening animation is never displayed. And then
      check the improved version with the artificial delay provided by
      `rn/delay-render`. By opening the AC first and animating, this gives the
      user something to look for, and hopefully a few milliseconds more to think
      the app is not stuck, which will be preciously used to render
      notifications.

We refactor all views in the AC to:

- [x] Follow our newest standards with React hooks.
- [x] Removed prop-drilling by creating a separate React context to store the
      current swipeable item (because we need to call `.close` on a `Gesture
      Swipeable` instance whenever a new swipeable opens.
2024-07-17 12:16:06 -03:00
Mohsen 7b09402fcb [#20742] fix: blue background behind the "Save address" button (#20754) 2024-07-17 16:04:40 +03:30
mmilad75 a912125ba9 Token value shows fiat value on the transaction confirmation page #20734 (#20770) 2024-07-17 10:47:27 +02:00
Ulises Manuel 2d0437dd5e fix(wallet): Improvements on activity tab (#20703)
* Fix schema for networks in context-tags

* Fix wallet-activity component overflowing the activity tab

* Improve robustness of the activity tab fetching mechanism

* Handle `wallet-activity-filtering-entries-updated` signal

* Improve processing of data received for the activity tab
2024-07-16 15:07:33 -06:00
Parvesh Monu 43651ef0d0 Adjust on-boarding illustrations position and scale (#20692) 2024-07-16 20:43:19 +05:30
Yevheniia Berdnyk 13232dc7b7 e2e: updated create profile flow 2024-07-16 15:46:23 +01:00
Andrea Maria Piana 93b5f7a918 Add confirmation to centralized metrics
This commit add a confirmation for centralized metrics.
It is added in 3 places:

1) Onboarding -> Create new account
2) Onboarding -> Sync profile
3) On the accounts view if the user is upgrading

To test 1 & 2, you should just be able to do that on a newly installed
device.

To test 3, you will have to upgrade from a PR without this feature that
has at least an account. It should show the confirmation modal until you
either click on Not now or Share usage data.

The modal should also be added in settings, but I will do that as a
separate PR.
2024-07-16 15:46:23 +01:00
Mohsen 4898d32243 [#20664] feat: add share usage data confirmation modal 2024-07-16 15:46:23 +01:00
122 changed files with 5304 additions and 3924 deletions
+14 -9
View File
@@ -1,10 +1,15 @@
*
/*
# Format top-level js files.
!*.js
!*/
*.clj-kondo
*.shadow-cljs
modules
result
target
component-spec
/app
# Ignore all except src/js/**/*.js
!/src/
/src/*
!/src/js
!/src/js/**/*.js
# Ignore all except translations/en.json
!/translations/
/translations/*
!/translations/en.json
+4
View File
@@ -6,4 +6,8 @@ module.exports = {
tabWidth: 2,
trailingComma: 'all',
useTabs: false,
// JSON sorting
jsonSortOrder: '{ "/.*/": "caseInsensitiveLexical" } ',
plugins: ['prettier-plugin-sort-json'],
};
+1 -1
View File
@@ -317,7 +317,7 @@ lint: ##@test Run code style checks
scripts/lint/translations.clj && \
zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILES && \
sh scripts/lint/trailing-newline.sh && \
node_modules/.bin/prettier --write .
node_modules/.bin/prettier --check .
# NOTE: We run the linter twice because of https://github.com/kkinnear/zprint/issues/271
lint-fix: export TARGET := clojure
+1 -1
View File
@@ -1 +1 @@
2.29.20538
2.29.0
+2 -1
View File
@@ -97,7 +97,8 @@
"jest-silent-reporter": "^0.5.0",
"nodemon": "^2.0.16",
"nyc": "^14.1.1",
"prettier": "^2.8.8",
"prettier": "^3.3.3",
"prettier-plugin-sort-json": "^4.0.0",
"process": "0.11.10",
"react-test-renderer": "18.1.0",
"shadow-cljs": "2.26.2",
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

@@ -1,5 +1,6 @@
(ns quo.components.avatars.token-avatar.view
(:require [quo.components.avatars.token-avatar.style :as style]
[quo.components.utilities.token.view :as token]
[react-native.core :as rn]
[react-native.hole-view :as hole-view]
[react-native.platform :as platform]
@@ -12,13 +13,14 @@
[:map {:closed true}
[:type {:optional true} [:enum :asset :collectible]]
[:context? {:optional true} [:maybe :boolean]]
[:image :schema.common/image-source]
[:image {:optional true} [:maybe :schema.common/image-source]]
[:token {:optional true} [:maybe [:or :keyword :string]]]
[:network-image {:optional true} [:maybe :schema.common/image-source]]
[:container-style {:optional true} [:maybe :map]]]]]
:any])
(defn- view-internal
[{:keys [type context? image network-image container-style]}]
[{:keys [type context? image token network-image container-style]}]
[rn/view
{:style (merge style/container container-style)
:accessibility-label :token-avatar}
@@ -32,9 +34,11 @@
[])
:style style/hole-view}
platform/android? (assoc :key context?))
[rn/image
{:source image
:style (style/image type)}]]
[token/view
{:size :size-32
:token token
:style (style/image type)
:image-source image}]]
(when context?
[rn/image
{:source network-image
@@ -21,15 +21,17 @@
(colors/theme-colors colors/white colors/neutral-95 theme))})
(defn buttons-container
[actions]
{:flex-direction (if (= actions :two-vertical-actions) :column :row)
:justify-content :space-around
:padding-vertical 12
:gap 12
:padding-horizontal 20})
[actions container-style]
(merge
{:flex-direction (if (= actions :two-vertical-actions) :column :row)
:justify-content :space-around
:padding-vertical 12
:gap 12
:padding-horizontal 20}
container-style))
(def button-container
{:flex 1})
{:flex-grow 1})
(def description-top
{:flex-direction :row
@@ -30,7 +30,8 @@
[:button-two-props {:optional true} [:maybe :map]]
[:scroll? {:optional true} [:maybe :boolean]]
[:blur? {:optional true} [:maybe :boolean]]
[:container-style {:optional true} [:maybe :map]]]]]
[:container-style {:optional true} [:maybe :map]]
[:buttons-container-style {:optional true} [:maybe :map]]]]]
:any])
(def ^:private role-icon
@@ -42,7 +43,7 @@
(defn- view-internal
[{:keys [actions description description-text description-top-text error-message role button-one-label
button-two-label blur? button-one-props button-two-props scroll? container-style
context-tag-props]}]
buttons-container-style context-tag-props]}]
(let [theme (quo.theme/use-theme)]
[rn/view
{:style (merge (style/container scroll? blur? theme) container-style)}
@@ -72,14 +73,14 @@
:context (i18n/label (keyword "t" role))}
context-tag-props)]])
[rn/view {:style (style/buttons-container actions)}
[rn/view {:style (style/buttons-container actions buttons-container-style)}
(when (or (= actions :two-actions)
(= actions :two-vertical-actions))
[button/button
(merge
{:size 40
:background (when (or blur? scroll?) :blur)
:container-style style/button-container
:background (when (or blur? scroll?) :blur)
:theme theme
:accessibility-label :button-two}
button-two-props)
@@ -28,6 +28,7 @@
{:type :spending-cap
:label "Spending Cap"
:button-label "Edit"
:button-icon :i/options
:on-button-press on-button-press
:avatar-props {:image "image"}}])
(h/fire-event :press (h/get-by-text "Edit"))
@@ -107,7 +107,7 @@
:style (style/description blur? theme)}
description])]
(when (= type :account) [tiny-tag/view {:label tag-label}])
(when (= type :spending-cap)
(when (and (= type :spending-cap) button-icon)
[button/button
{:type :outline
:size 24
@@ -121,6 +121,7 @@
[:card? {:optional true} [:maybe :boolean]]
[:right-icon {:optional true} [:maybe :keyword]]
[:right-content {:optional true} [:maybe :map]]
[:icon-color {:optional true} [:maybe :schema.common/customization-color]]
[:status {:optional true} [:maybe [:enum :default :loading]]]
[:subtitle-type {:optional true} [:maybe [:enum :default :icon :network :account :editable]]]
[:size {:optional true} [:maybe [:enum :default :small :large]]]
@@ -53,7 +53,7 @@
(def ^:private ?network
[:map
[:network-logo {:optional true} [:maybe :schema.common/image-source]]
[:network-name {:optional true} [:maybe :string]]])
[:network-name {:optional true} [:maybe [:or :string :keyword]]]])
(def ^:private ?multinetwork
[:map
@@ -41,7 +41,8 @@
:align-items :center
:height size
:background-color background-color
:border-radius border-radius}
:border-radius border-radius
:flex-shrink 1}
(= state :selected) (assoc :height (+ size 2)
:border-color border-color
:border-width 1))))
@@ -50,11 +51,13 @@
[size]
{:margin-right (if (= size 24) 6 10)
:flex-direction :row
:flex-shrink 1
:align-items :center})
(defn tag-spacing
[size]
{:margin-left (if (= size 24) 4 8)})
[size shrinkable?]
(cond-> {:margin-left (if (= size 24) 4 8)}
shrinkable? (assoc :flex-shrink 1)))
(defn text
[theme]
+12 -9
View File
@@ -17,17 +17,19 @@
[schema.core :as schema]))
(defn- tag-skeleton
[{:keys [size text theme]
[{:keys [size text theme shrinkable?]
:or {size 24
theme (quo.theme/use-theme)}}
logo-component]
[rn/view {:style (style/tag-container size)}
logo-component
[rn/view {:style (style/tag-spacing size)}
[rn/view {:style (style/tag-spacing size shrinkable?)}
[text/text
{:style (style/text theme)
:weight :medium
:size (if (= size 24) :paragraph-2 :paragraph-1)}
{:style (style/text theme)
:weight :medium
:size (if (= size 24) :paragraph-2 :paragraph-1)
:number-of-lines 1
:ellipsize-mode :middle}
text]]])
(defn- communities-tag
@@ -37,7 +39,7 @@
icon-size (if (= size 24) 16 20)]
[rn/view {:style (style/tag-container size)}
[fast-image/fast-image {:style (style/circle-logo size) :source community-logo}]
[rn/view {:style (style/tag-spacing size)}
[rn/view {:style (style/tag-spacing size false)}
[text/text
{:style (style/text theme)
:weight :medium
@@ -148,9 +150,10 @@
:collectible
[tag-skeleton
{:theme theme
:size size
:text (str collectible-name " #" collectible-number)}
{:theme theme
:size size
:text (str collectible-name " #" collectible-number)
:shrinkable? true}
[rn/image {:style (style/rounded-logo size) :source collectible}]]
:account
@@ -4,8 +4,7 @@
(defn main
[{:keys [type customization-color]} theme]
{:justify-content :center
:align-items :center
{:align-items :center
:height 32
:padding-left 4
:padding-right 10
@@ -67,7 +67,9 @@
:style (style/main (assoc props :customization-color customization-color) theme)}
[left-view props]
[text/text
{:style (style/label type theme)
:weight :semi-bold
:size :heading-1}
{:style (style/label type theme)
:weight :semi-bold
:ellipsize-mode :tail
:number-of-lines 1
:size :heading-1}
label]]))
@@ -0,0 +1,31 @@
(ns quo.components.wallet.swap-input.component-spec
(:require [quo.components.wallet.swap-input.view :as swap-input]
[test-helpers.component :as h]))
(h/describe "Wallet: Swap Input"
(h/test "should render correctly with props"
(h/render-with-theme-provider
[swap-input/view
{:type :pay
:error? false
:token "SNT"
:status :default
:currency-symbol "€"
:value "5"
:fiat-value "1.50"
:network-tag-props {:title "Max: 200 SNT"}}])
(h/is-truthy (h/get-by-label-text :swap-input))
(h/is-truthy (h/get-by-text "SNT"))
(h/is-truthy (h/get-by-text "€1.50"))
(h/is-truthy (h/get-by-text "Max: 200 SNT")))
(h/test "should render correctly with approval label"
(h/render-with-theme-provider
[swap-input/view
{:type :pay
:show-approval-label? true
:approval-label-props
{:status :approve
:token-value "10"
:token-symbol "SNT"}}])
(h/is-truthy (h/get-by-text "Approve 10 SNT"))))
@@ -0,0 +1,74 @@
(ns quo.components.wallet.swap-input.style
(:require [quo.foundations.colors :as colors]
[quo.foundations.typography :as typography]))
(defn- border-color
[theme]
(colors/theme-colors colors/neutral-10 colors/neutral-80 theme))
(defn- loader-color
[theme]
(colors/theme-colors colors/neutral-5 colors/neutral-90 theme))
(defn content
[theme]
{:border-width 1
:border-radius 16
:border-color (border-color theme)
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
(defn row-1
[loading?]
{:padding 12
:gap 8
:align-items (if loading? :center :flex-end)
:flex-direction :row})
(defn row-1-loader
[theme]
{:width 74
:height 14
:border-radius 6
:background-color (loader-color theme)})
(def input-container
{:flex 1
:flex-direction :row
:gap 5
:height 32
:align-items :flex-end})
(defn input
[disabled? error? theme]
(assoc typography/font-semi-bold
:font-size 27
:flex-shrink 1
:padding 0
:color (cond
error? (colors/resolve-color :danger theme)
disabled? (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:else (colors/theme-colors colors/neutral-100 colors/white theme))
:line-height 32))
(defn token-symbol
[theme]
{:padding-bottom 3
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
(defn row-2
[align-right?]
{:flex-direction :row
:justify-content (if align-right? :flex-end :space-between)
:align-items :center
:padding 12})
(defn row-2-loader
[theme]
{:width 80
:height 10
:margin-vertical 7
:border-radius 6
:background-color (loader-color theme)})
(def fiat-amount
{:color colors/neutral-50})
@@ -0,0 +1,121 @@
(ns quo.components.wallet.swap-input.view
(:require [oops.core :as oops]
[quo.components.avatars.token-avatar.view :as token-avatar]
[quo.components.buttons.button.view :as buttons]
[quo.components.dividers.divider-line.view :as divider-line]
[quo.components.markdown.text :as text]
[quo.components.tags.network-tags.view :as network-tag]
[quo.components.wallet.approval-label.schema :as approval-label.schema]
[quo.components.wallet.approval-label.view :as approval-label]
[quo.components.wallet.swap-input.style :as style]
[quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]
[schema.core :as schema]))
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:type {:optional true} [:maybe [:enum :pay :receive]]]
[:status {:optional true} [:maybe [:enum :default :disabled :loading]]]
[:token {:optional true} [:maybe :string]]
[:value {:optional true} [:maybe :string]]
[:default-value {:optional true} [:maybe :string]]
[:currency-symbol {:optional true} [:maybe :string]]
[:fiat-value {:optional true} [:maybe :string]]
[:show-approval-label? {:optional true} [:maybe :boolean]]
[:error? {:optional true} [:maybe :boolean]]
[:show-keyboard? {:optional true} [:maybe :boolean]]
[:approval-label-props {:optional true} [:maybe approval-label.schema/?schema]]
[:network-tag-props {:optional true} [:maybe :map]]
[:on-change-text {:optional true} [:maybe fn?]]
[:enable-swap? {:optional true} [:maybe :boolean]]
[:on-swap-press {:optional true} [:maybe fn?]]
[:on-token-press {:optional true} [:maybe fn?]]
[:on-max-press {:optional true} [:maybe fn?]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:container-style {:optional true} [:maybe :map]]]]]
:any])
(defn view-internal
[{:keys [type status token value fiat-value show-approval-label? error? network-tag-props
approval-label-props default-value enable-swap?
currency-symbol on-change-text show-keyboard?
container-style on-swap-press on-token-press on-max-press]}]
(let [theme (quo.theme/use-theme)
pay? (= type :pay)
disabled? (= status :disabled)
loading? (= status :loading)
controlled-input? (some? value)
input-ref (rn/use-ref-atom nil)
set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref)) [])
focus-input (rn/use-callback (fn []
(some-> @input-ref
(oops/ocall "focus")))
[input-ref])]
[rn/view
{:style container-style
:accessibility-label :swap-input}
[rn/view {:style (style/content theme)}
[rn/view
{:style (style/row-1 loading?)}
[rn/pressable {:on-press on-token-press}
[token-avatar/view
{:type :asset
:token token}]]
(if loading?
[rn/view {:style (style/row-1-loader theme)}]
[:<>
[rn/pressable
{:style style/input-container
:on-press focus-input}
[rn/text-input
(cond-> {:ref set-input-ref
:style (style/input disabled? error? theme)
:placeholder-text-color (colors/theme-colors colors/neutral-40
colors/neutral-50
theme)
:keyboard-type :numeric
:auto-focus true
:on-change-text on-change-text
:show-soft-input-on-focus show-keyboard?
:default-value default-value
:placeholder "0"}
controlled-input? (assoc :value value))]
[text/text
{:size :paragraph-2
:weight :semi-bold
:style (style/token-symbol theme)}
token]]
(when (and pay? enable-swap?)
[buttons/button
{:type :outline
:size 32
:on-press on-swap-press
:icon-only? true}
:i/reorder])])]
[divider-line/view]
[rn/view
{:style (style/row-2 (or (not pay?) loading?))}
(when-not loading?
[:<>
(when pay?
[rn/pressable {:on-press on-max-press}
[network-tag/view
(assoc network-tag-props
:status
(if error? :error :default))]])
[text/text
{:size :paragraph-2
:style style/fiat-amount
:weight :medium}
(str currency-symbol fiat-value)]])
(when loading?
[rn/view {:style (style/row-2-loader theme)}])]]
(when (and (not= status :loading) (= type :pay) show-approval-label?)
[approval-label/view
approval-label-props])]))
(def view (schema/instrument #'view-internal ?schema))
@@ -51,8 +51,7 @@
(defn prop-text
[theme]
{:margin-right 4
:color (colors/theme-colors colors/neutral-100 colors/white theme)})
{:color (colors/theme-colors colors/neutral-100 colors/white theme)})
(def icon-container
{:width 32
@@ -61,12 +60,15 @@
(def container
{:flex-direction :row
:flex 1
:column-gap 8})
(def content-line
{:flex-direction :row
:margin-top 2
:align-items :center})
{:flex-direction :row
:margin-top 2
:align-items :center
:column-gap 4
:justify-content :flex-start})
(def icon-hole-view
{:width 32
@@ -98,8 +98,11 @@
(defn prop-tag
[props blur?]
[rn/view {:style {:margin-right 4}}
[context-tag/view (merge props {:size 24 :blur? blur?})]])
[context-tag/view
(assoc props
:size 24
:blur? blur?
:container-style {:flex-shrink 1})])
(defn- view-internal
[{:keys [state blur? first-tag second-tag third-tag fourth-tag on-press
@@ -121,7 +124,7 @@
:on-press-out on-press-out}
[rn/view {:style style/container}
[transaction-icon-view props theme]
[rn/view
[rn/view {:style {:flex 1}}
[transaction-header props theme]
[rn/view {:style style/content-line}
(when first-tag [prop-tag first-tag blur?])
+2
View File
@@ -186,6 +186,7 @@
quo.components.wallet.progress-bar.view
quo.components.wallet.required-tokens.view
quo.components.wallet.summary-info.view
quo.components.wallet.swap-input.view
quo.components.wallet.token-input.view
quo.components.wallet.transaction-progress.view
quo.components.wallet.transaction-summary.view
@@ -472,6 +473,7 @@
(def progress-bar quo.components.wallet.progress-bar.view/view)
(def required-tokens quo.components.wallet.required-tokens.view/view)
(def summary-info quo.components.wallet.summary-info.view/view)
(def swap-input quo.components.wallet.swap-input.view/view)
(def network-link quo.components.wallet.network-link.view/view)
(def token-input quo.components.wallet.token-input.view/view)
(def wallet-overview quo.components.wallet.wallet-overview.view/view)
+1
View File
@@ -112,6 +112,7 @@
quo.components.wallet.progress-bar.component-spec
quo.components.wallet.required-tokens.component-spec
quo.components.wallet.summary-info.component-spec
quo.components.wallet.swap-input.component-spec
quo.components.wallet.token-input.component-spec
quo.components.wallet.transaction-progress.component-spec
quo.components.wallet.transaction-summary.component-spec
+1
View File
@@ -43,6 +43,7 @@
:gnosis (js/require "../resources/images/networks/Gnosis.png")
:hermez (js/require "../resources/images/networks/Hermez.png")
:optimism (js/require "../resources/images/networks/Optimism.png")
:paraswap (js/require "../resources/images/networks/Paraswap.png")
:polygon (js/require "../resources/images/networks/Polygon.png")
:scroll (js/require "../resources/images/networks/Scroll.png")
:taiko (js/require "../resources/images/networks/Taiko.png")
+7 -6
View File
@@ -62,16 +62,17 @@
(bean/->js {:id id
:namespaces approved-namespaces})))
(defn disconnect-session
[{:keys [web3-wallet reason topic]}]
(oops/ocall web3-wallet
"disconnectSession"
(bean/->js {:topic topic
:reason reason})))
(defn get-active-sessions
[web3-wallet]
(oops/ocall web3-wallet "getActiveSessions"))
(defn core-pairing-disconnnect
[web3-wallet topic]
(oops/ocall web3-wallet
"core.pairing.disconnect"
(bean/->js {:topic topic})))
(defn core-pairing-pair
[web3-wallet url]
(oops/ocall web3-wallet
@@ -1,5 +1,6 @@
(ns status-im.common.floating-button-page.floating-container.style
(:require [react-native.safe-area :as safe-area]))
(:require [quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))
(defn content-container
[blur? keyboard-shown?]
@@ -11,7 +12,12 @@
:padding-horizontal 20}
blur? (dissoc :padding-vertical :padding-horizontal))))
(def blur-inner-container
{:background-color :transparent ; required, otherwise blur-view will shrink
(defn blur-inner-container
[theme shell-overlay?]
{:background-color (colors/theme-colors colors/white-70-blur
(if shell-overlay?
colors/neutral-80-opa-80-blur
colors/neutral-95-opa-70-blur)
theme)
:padding-vertical 12
:padding-horizontal 20})
@@ -1,27 +1,25 @@
(ns status-im.common.floating-button-page.floating-container.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[status-im.common.floating-button-page.floating-container.style :as style]))
(defn- blur-container
[child]
[child shell-overlay?]
(let [theme (quo.theme/use-theme)]
[quo/blur
{:blur-amount 52
:blur-radius 20
:blur-type :transparent
:blur-overlay-color (colors/theme-colors colors/white-70-blur colors/neutral-95-opa-70-blur theme)}
[rn/view {:style style/blur-inner-container}
{:blur-amount 20
:blur-type :transparent
:overlay-color :transparent}
[rn/view {:style (style/blur-inner-container theme shell-overlay?)}
child]]))
(defn view
[{:keys [on-layout keyboard-shown? blur?]} child]
[{:keys [on-layout keyboard-shown? blur? shell-overlay?]} child]
[rn/view
{:style (style/content-container blur? keyboard-shown?)
:on-layout on-layout}
(if blur?
[blur-container child]
[blur-container child shell-overlay?]
child)])
@@ -58,7 +58,7 @@
(defn view
[{:keys [header footer customization-color footer-container-padding header-container-style
content-container-style gradient-cover? keyboard-should-persist-taps]
content-container-style gradient-cover? keyboard-should-persist-taps shell-overlay?]
:or {footer-container-padding (safe-area/get-top)}}
& children]
(reagent/with-let [scroll-view-ref (atom nil)
@@ -121,7 +121,8 @@
[floating-container/view
{:on-layout set-footer-container-height
:keyboard-shown? keyboard-shown?
:blur? show-background?}
:blur? show-background?
:shell-overlay? shell-overlay?}
footer]]]])
(finally
(remove-listeners))))
@@ -0,0 +1,14 @@
(ns status-im.common.metrics-confirmation-modal.style)
(def points-wrapper
{:margin-top 11
:margin-horizontal 20
:gap 21
:margin-bottom 15})
(def item-text
{:margin-top 10
:margin-left -4})
(def info-text
{:margin-top -5})
@@ -0,0 +1,79 @@
(ns status-im.common.metrics-confirmation-modal.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[status-im.common.metrics-confirmation-modal.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- toggle-metrics
[enabled?]
(rf/dispatch [:centralized-metrics/toggle-centralized-metrics enabled?]))
(def ^:private will-receive-points
[:t/ip-address
:t/universally-unique-identifiers-of-device
:t/logs-of-actions-withing-the-app])
(def ^:private not-receive-points
[:t/your-profile-information
:t/your-addresses
:t/information-you-input-and-send])
(defn- bullet-points
[{:keys [title points]}]
[rn/view
[quo/text {:weight :semi-bold}
title]
(for [label points]
^{:key label}
[quo/markdown-list
{:description (i18n/label label)
:blur? true
:container-style style/item-text}])])
(defn- on-share-usage
[]
(toggle-metrics true)
(hide-bottom-sheet))
(defn- on-do-not-share
[]
(toggle-metrics false)
(hide-bottom-sheet))
(defn view
[{:keys [settings?]}]
(rn/use-mount #(dismiss-keyboard))
[:<>
[quo/drawer-top
{:title (i18n/label :t/help-us-improve-status)
:description (i18n/label :t/collecting-usage-data)}]
[rn/view {:style style/points-wrapper}
[bullet-points
{:title (i18n/label :t/what-we-will-receive)
:points will-receive-points}]
[bullet-points
{:title (i18n/label :t/what-we-wont-receive)
:points not-receive-points}]
(when-not settings?
[quo/text
{:size :paragraph-2
:style style/info-text}
(i18n/label :t/sharing-usage-data-can-be-turned-off)])]
[quo/bottom-actions
{:actions :two-actions
:blur? true
:button-one-label (i18n/label :t/share-usage-data)
:button-one-props {:on-press on-share-usage}
:button-two-label (i18n/label (if settings? :t/do-not-share :t/not-now))
:button-two-props {:type :grey
:on-press on-do-not-share}}]])
+18 -2
View File
@@ -45,6 +45,11 @@
(def ^:const activity-center-membership-status-accepted 2)
(def ^:const activity-center-membership-status-declined 3)
;; Choose the maximum number of notifications that *usually/safely* fit on
;; most screens, so that the UI doesn't have to needlessly render
;; notifications.
(def ^:const notifications-per-page 7)
(def ^:const mute-for-15-mins-type 1)
(def ^:const mute-for-1-hour-type 2)
(def ^:const mute-for-8-hours-type 3)
@@ -276,16 +281,21 @@
#{wallet-connect-personal-sign-method
wallet-connect-eth-sign-method
wallet-connect-eth-send-transaction-method
wallet-connect-eth-sign-transaction-method
;; NOTE: disabled, as we have no clear use cases for it and other wallets don't support it
;; wallet-connect-eth-sign-transaction-method
wallet-connect-eth-sign-typed-method
wallet-connect-eth-sign-typed-v4-method})
(def ^:const wallet-connect-supported-events #{"accountsChanged" "chainChanged"})
(def ^:const wallet-connect-session-proposal-event "session_proposal")
(def ^:const wallet-connect-session-request-event "session_request")
(def ^:const wallet-connect-session-delete-event "session_delete")
(def ^:const wallet-connect-user-rejected-error-key "USER_REJECTED")
(def ^:const wallet-connect-user-disconnected-reason-key "USER_DISCONNECTED")
(def ^:const transaction-pending-type-wallet-connect-transfer "WalletConnectTransfer")
(def ^:const token-sort-priority {"SNT" 1 "STT" 1 "ETH" 2 "DAI" 3})
(def ^:const dapp-permission-contact-code "contact-code")
(def ^:const dapp-permission-web3 "web3")
(def ^:const dapp-permission-qr-code "qr-code")
@@ -506,6 +516,9 @@
(def ^:const optimism-full-name "Optimism")
(def ^:const arbitrum-full-name "Arbitrum")
(def ^:const sepolia-full-name "Sepolia")
(def ^:const goerli-full-name "Goerli")
(def ^:const mainnet-network-name :mainnet)
(def ^:const ethereum-network-name :ethereum)
(def ^:const optimism-network-name :optimism)
@@ -567,5 +580,8 @@
(def ^:const max-recommended-slippage 5)
(def ^:const max-slippage-decimal-places 2)
(def ^:const swap-default-provider
{:name "Paraswap"
{:name :paraswap
:full-name "Paraswap"
:color :blue
:contract-address "0xdef171fe48cf0115b1d80b88dc8eab59176fee57"
:terms-and-conditions-url "https://files.paraswap.io/tos_v4.pdf"})
@@ -7,10 +7,17 @@
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(def ^:const user-confirmed-key :centralized-metrics/user-confirmed?)
(def ^:const enabled-key :centralized-metrics/enabled?)
(defn show-confirmation-modal?
[db]
(not (user-confirmed-key db)))
(defn push-event?
[db]
(or (not (:centralized-metrics/user-confirmed? db))
(:centralized-metrics/enabled? db)))
(or (not (user-confirmed-key db))
(enabled-key db)))
(defn centralized-metrics-interceptor
[context]
@@ -29,5 +36,16 @@
(fn [{:keys [db]} [enabled?]]
{:fx [[:effects.centralized-metrics/toggle-metrics enabled?]]
:db (assoc db
:centralized-metrics/user-confirmed? true
:centralized-metrics/enabled? enabled?)}))
user-confirmed-key
true
enabled-key
enabled?)}))
(rf/reg-event-fx :centralized-metrics/check-modal
(fn [{:keys [db]} [modal-view]]
(when (show-confirmation-modal? db)
{:fx [[:dispatch
[:show-bottom-sheet
{:content (fn [] [modal-view])
:shell? true}]]]})))
@@ -6,6 +6,11 @@
[status-im.contexts.centralized-metrics.tracking :as tracking]
[test-helpers.unit :as h]))
(deftest show-confirmation-modal-test
(testing "returns true if the user confirmed"
(is (false? (events/show-confirmation-modal? {events/user-confirmed-key true})))
(is (true? (events/show-confirmation-modal? {})))))
(deftest push-event-test
(testing "returns correct boolean value"
(is (true? (events/push-event? {:centralized-metrics/user-confirmed? false})))
@@ -27,7 +27,7 @@
:on-error #(p-reject (str "failed to sign data\n" %))}))))
(defn- edit-shared-addresses-for-community
[community-id signatures addresses-to-reveal airdrop-address]
[community-id signatures addresses-to-reveal airdrop-address _share-future-addresses?]
(promesa/create
(fn [p-resolve p-reject]
(rpc/call
@@ -41,15 +41,16 @@
:on-error p-reject}))))
(defn- request-to-join
[community-id signatures addresses-to-reveal airdrop-address]
[community-id signatures addresses-to-reveal airdrop-address share-future-addresses?]
(promesa/create
(fn [p-resolve p-reject]
(rpc/call
{:method :wakuext_requestToJoinCommunity
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress airdrop-address}]
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress airdrop-address
:shareFutureAddresses share-future-addresses?}]
:js-response true
:on-success p-resolve
:on-error p-reject}))))
@@ -64,15 +65,18 @@
(defn- sign-and-call-endpoint
[{:keys [community-id password pub-key
addresses-to-reveal airdrop-address
addresses-to-reveal airdrop-address share-future-addresses?
on-success on-error
callback]}]
(-> (promesa/let [sign-params (generate-requests-for-signing pub-key community-id addresses-to-reveal)
(-> (promesa/let [sign-params (generate-requests-for-signing pub-key
community-id
addresses-to-reveal)
signatures (sign-data sign-params password)
result (callback community-id
signatures
addresses-to-reveal
airdrop-address)]
airdrop-address
share-future-addresses?)]
(run-callback-or-event on-success result))
(promesa/catch #(run-callback-or-event on-error %))))
@@ -87,6 +91,7 @@
[:or [:set string?]
[:sequential string?]]]
[:airdrop-address string?]
[:share-future-addresses? boolean?]
[:on-success [:or fn? :schema.re-frame/event]]
[:on-error [:or fn? :schema.re-frame/event]]
[:callback fn?]]]
@@ -17,13 +17,12 @@
(rf/reg-event-fx :communities/initialize-permission-addresses initialize-permission-addresses)
(defn do-init-permission-addresses
[{:keys [db]} [community-id revealed-accounts]]
[{:keys [db]} [community-id revealed-accounts share-future-addresses?]]
(let [wallet-accounts (utils/sorted-operable-non-watch-only-accounts db)
addresses-to-reveal (if (seq revealed-accounts)
(set (keys revealed-accounts))
;; Reveal all addresses as fallback.
(set (map :address wallet-accounts)))
;; When there are no revealed addresses, such as when joining a
;; community, use first address for airdrops.
airdrop-address (or (->> revealed-accounts
@@ -35,10 +34,7 @@
first
:address))]
{:db (-> db
;; Set to false by default while we don't persist the user's choice
;; in status-go, otherwise whenever the view is mounted, the choice
;; of selected addresses won't be respected.
(assoc-in [:communities/selected-share-all-addresses community-id] false)
(assoc-in [:communities/selected-share-all-addresses community-id] share-future-addresses?)
(assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal)
(assoc-in [:communities/all-airdrop-addresses community-id] airdrop-address))
:fx [[:dispatch
@@ -60,36 +56,44 @@
is selecting an airdrop address, we must submit to status-go the current
choice of addresses to share, and vice-versa. If we omit addresses to share,
status-go will default to all available."
[{:keys [db]} [{:keys [community-id password on-success addresses airdrop-address]}]]
(let [pub-key (get-in db [:profile/profile :public-key])
wallet-accounts (utils/sorted-operable-non-watch-only-accounts db)
addresses-to-reveal (if (seq addresses)
(set addresses)
(get-in db [:communities/all-addresses-to-reveal community-id]))
new-airdrop-address (if (contains? addresses-to-reveal airdrop-address)
airdrop-address
(->> wallet-accounts
(filter #(contains? addresses-to-reveal (:address %)))
first
:address))]
[{:keys [db]}
[{:keys [community-id password on-success addresses airdrop-address share-future-addresses?]}]]
(let [pub-key (get-in db [:profile/profile :public-key])
wallet-accounts (utils/sorted-operable-non-watch-only-accounts db)
addresses-to-reveal (if (seq addresses)
(set addresses)
(get-in db [:communities/all-addresses-to-reveal community-id]))
new-airdrop-address (if (contains? addresses-to-reveal airdrop-address)
airdrop-address
(->> wallet-accounts
(filter #(contains? addresses-to-reveal (:address %)))
first
:address))
share-future-addresses? (if (nil? share-future-addresses?)
(get-in db [:communities/selected-share-all-addresses community-id])
share-future-addresses?)]
{:fx [[:effects.community/edit-shared-addresses
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal addresses-to-reveal
:airdrop-address new-airdrop-address
:on-success (fn []
(when (fn? on-success)
(on-success addresses-to-reveal new-airdrop-address))
(rf/dispatch [:communities/edit-shared-addresses-success
community-id addresses-to-reveal airdrop-address]))
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}))
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal addresses-to-reveal
:share-future-addresses? share-future-addresses?
:airdrop-address new-airdrop-address
:on-success (fn []
(when (fn? on-success)
(on-success addresses-to-reveal
new-airdrop-address
share-future-addresses?))
(rf/dispatch [:communities/edit-shared-addresses-success
community-id addresses-to-reveal airdrop-address]))
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}))
(rf/reg-event-fx :communities/edit-shared-addresses edit-shared-addresses)
(rf/reg-event-fx :communities/edit-shared-addresses-success
(fn [_ [community-id addresses-to-reveal airdrop-address]]
(fn [_ [community-id addresses-to-reveal airdrop-address share-future-addresses?]]
{:fx [[:dispatch [:communities/set-airdrop-address community-id airdrop-address]]
[:dispatch [:communities/set-share-all-addresses community-id share-future-addresses?]]
[:dispatch [:communities/set-addresses-to-reveal community-id addresses-to-reveal]]]}))
(rf/reg-event-fx :communities/edit-shared-addresses-failure
@@ -70,7 +70,7 @@
(is
(match?
{:db (-> (:db cofx)
(assoc-in [:communities/selected-share-all-addresses community-id] false)
(assoc-in [:communities/selected-share-all-addresses community-id] true)
(assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal)
(assoc-in [:communities/all-airdrop-addresses community-id] airdrop-address))
:fx [[:dispatch
@@ -82,7 +82,7 @@
[:dispatch
[:communities/check-permissions-to-join-during-selection community-id
addresses-to-reveal]]]}
(sut/do-init-permission-addresses cofx [community-id revealed-accounts])))))
(sut/do-init-permission-addresses cofx [community-id revealed-accounts true])))))
;; Expect to mark all addresses to be revealed and first one to receive
;; airdrops when no addresses were previously revealed.
@@ -93,7 +93,7 @@
(is
(match?
{:db (-> (:db cofx)
(assoc-in [:communities/selected-share-all-addresses community-id] false)
(assoc-in [:communities/selected-share-all-addresses community-id] true)
(assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal))
:fx [[:dispatch
[:communities/check-permissions-to-join-community
@@ -101,7 +101,7 @@
[:dispatch
[:communities/check-permissions-to-join-during-selection community-id
addresses-to-reveal]]]}
(sut/do-init-permission-addresses cofx [community-id revealed-accounts]))))))
(sut/do-init-permission-addresses cofx [community-id revealed-accounts true]))))))
(deftest edit-shared-addresses-test
(testing
@@ -109,6 +109,7 @@
fallback to all wallet addresses"
(let [pub-key "abcdef"
revealed-addresses #{"0xB" "0xC"}
share-future-addresses? true
cofx {:db {:profile/profile {:public-key pub-key}
:communities/all-addresses-to-reveal {community-id revealed-addresses}}}
airdrop-address "0xB"
@@ -116,22 +117,24 @@
actual
(sut/edit-shared-addresses
cofx
[{:community-id community-id
:password password
:airdrop-address airdrop-address
:on-success (fn [new-addresses-to-reveal]
(is (match? revealed-addresses new-addresses-to-reveal)))}])
[{:community-id community-id
:password password
:airdrop-address airdrop-address
:share-future-addresses? share-future-addresses?
:on-success (fn [new-addresses-to-reveal]
(is (match? revealed-addresses new-addresses-to-reveal)))}])
on-success-wrapper (-> actual :fx first second :on-success)]
(is (match?
{:fx [[:effects.community/edit-shared-addresses
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal revealed-addresses
:airdrop-address airdrop-address
:on-success fn?
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal revealed-addresses
:share-future-addresses? share-future-addresses?
:airdrop-address airdrop-address
:on-success fn?
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}
actual))
(on-success-wrapper)))
@@ -203,7 +203,9 @@
:addresses-for-permissions])
(rf/dispatch [:hide-bottom-sheet]))}]))}])
(rf/dispatch [:communities/set-share-all-addresses id flag-share-all-addresses]))
(rf/dispatch [:communities/set-addresses-to-reveal id addresses-to-reveal])))
(do
(rf/dispatch [:communities/set-share-all-addresses id flag-share-all-addresses])
(rf/dispatch [:communities/set-addresses-to-reveal id addresses-to-reveal]))))
highest-role (rf/sub [:communities/highest-role-for-selection id])
[unmodified-role _] (rn/use-state highest-role)]
@@ -261,6 +263,7 @@
can-edit-addresses? (rf/sub [:communities/can-edit-shared-addresses? id])
wallet-accounts (rf/sub [:wallet/operable-accounts-without-watched-accounts])
joined (rf/sub [:communities/community-joined id])
unmodified-addresses-to-reveal (rf/sub [:communities/addresses-to-reveal id])
[addresses-to-reveal set-addresses-to-reveal] (rn/use-state unmodified-addresses-to-reveal)
@@ -285,7 +288,6 @@
(set-flag-share-all-addresses new-value)
(when new-value
(set-addresses-to-reveal (set (map :address wallet-accounts))))))]
(rn/use-mount
(fn []
(when-not flag-share-all-addresses
@@ -310,6 +312,7 @@
flag-share-all-addresses]
:header [quo/page-setting
{:checked? flag-share-all-addresses
:disabled? joined
:customization-color color
:on-change toggle-flag-share-all-addresses
:setting-text (i18n/label
@@ -47,12 +47,11 @@
:label (i18n/label :t/view-token-gating)}))
(defn- action-mark-as-read
[]
(when config/show-not-implemented-features?
{:icon :i/mark-as-read
:accessibility-label :chat-mark-as-read
:on-press not-implemented/alert
:label (i18n/label :t/mark-as-read)}))
[chat-id]
{:icon :i/mark-as-read
:accessibility-label :chat-mark-as-read
:on-press #(hide-sheet-and-dispatch [:chat.ui/mark-all-read-pressed chat-id])
:label (i18n/label :t/mark-as-read)})
(defn- action-toggle-muted
[id muted? muted-till chat-type]
@@ -130,7 +129,7 @@
(and (not inside-chat?) (not locked?))
[quo/action-drawer
[[(when-not hide-view-members? (action-view-members-and-details community-id chat-id))
(action-mark-as-read)
(action-mark-as-read chat-id)
(action-toggle-muted chat-id muted muted-till chat-type)
(action-notification-settings)
(action-pinned-messages chat-id)
@@ -143,7 +142,7 @@
[[(action-view-members-and-details community-id chat-id)
(when token-gated?
(action-token-requirements))
(action-mark-as-read)
(action-mark-as-read chat-id)
(action-toggle-muted chat-id muted muted-till chat-type)
(action-notification-settings)
(when config/fetch-messages-enabled?
+14 -18
View File
@@ -16,6 +16,7 @@
[status-im.navigation.events :as navigation]
[status-im.navigation.transitions :as transitions]
[taoensso.timbre :as log]
[utils.collection :as collection-utils]
[utils.re-frame :as rf]))
(defn handle-community
@@ -372,15 +373,14 @@
(when (and community joined (not fetching-revealed-accounts))
{:db (assoc-in db [:communities community-id :fetching-revealed-accounts] true)
:json-rpc/call
[{:method "wakuext_getRevealedAccounts"
:params [community-id (get-in db [:profile/profile :public-key])]
:js-response true
:on-success [:communities/get-revealed-accounts-success community-id on-success]
:on-error (fn [err]
(log/error {:message "failed to fetch revealed accounts"
:community-id community-id
:err err})
(rf/dispatch [:communities/get-revealed-accounts-failed community-id]))}]})))
[{:method "wakuext_latestRequestToJoinForCommunity"
:params [community-id]
:on-success [:communities/get-revealed-accounts-success community-id on-success]
:on-error (fn [err]
(log/error {:message "failed to fetch revealed accounts"
:community-id community-id
:err err})
(rf/dispatch [:communities/get-revealed-accounts-failed community-id]))}]})))
(rf/reg-event-fx :communities/get-revealed-accounts get-revealed-accounts)
@@ -399,22 +399,18 @@
[:json-rpc/call :schema.common/rpc-call]]]])
(rf/reg-event-fx :communities/get-revealed-accounts-success
(fn [{:keys [db]} [community-id on-success revealed-accounts-js]]
(fn [{:keys [db]} [community-id on-success request-to-join]]
(when-let [community (get-in db [:communities community-id])]
(let [revealed-accounts
(reduce
(fn [acc {:keys [address] :as revealed-account}]
(assoc acc address revealed-account))
{}
(data-store.communities/<-revealed-accounts-rpc revealed-accounts-js))
(let [revealed-accounts (collection-utils/index-by :address (:revealedAccounts request-to-join))
share-future-addresses? (:shareFutureAddresses request-to-join)
community-with-revealed-accounts
(-> community
(assoc :revealed-accounts revealed-accounts)
(assoc :share-future-addresses? share-future-addresses?)
(dissoc :fetching-revealed-accounts))]
{:db (assoc-in db [:communities community-id] community-with-revealed-accounts)
:fx [(when (vector? on-success)
[:dispatch (conj on-success revealed-accounts)])]}))))
[:dispatch (conj on-success revealed-accounts share-future-addresses?)])]}))))
(rf/reg-event-fx :communities/get-revealed-accounts-failed
(fn [{:keys [db]} [community-id]]
@@ -146,8 +146,8 @@
effects (events/get-revealed-accounts {:db db} [community-id])]
(is (match? (assoc-in db [:communities community-id :fetching-revealed-accounts] true)
(:db effects)))
(is (match? {:method "wakuext_getRevealedAccounts"
:params [community-id "profile-public-key"]}
(is (match? {:method "wakuext_latestRequestToJoinForCommunity"
:params [community-id]}
(-> effects :json-rpc/call first (select-keys [:method :params]))))))))
(deftest handle-community-test
@@ -117,13 +117,14 @@
{:community community-name})}]]]})))
(defn request-to-join-with-signatures
[_ [community-id addresses-to-reveal signatures]]
[_ [community-id addresses-to-reveal signatures share-future-addresses?]]
{:fx [[:json-rpc/call
[{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress (first addresses-to-reveal)}]
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:shareFutureAddresses share-future-addresses?
:airdropAddress (first addresses-to-reveal)}]
:js-response true
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error community-id]}]]]})
@@ -153,16 +154,18 @@
(defn request-to-join-with-addresses
[{:keys [db]}
[{:keys [community-id password]}]]
(let [pub-key (get-in db [:profile/profile :public-key])
addresses-to-reveal (get-in db [:communities/all-addresses-to-reveal community-id])
airdrop-address (get-in db [:communities/all-airdrop-addresses community-id])]
(let [pub-key (get-in db [:profile/profile :public-key])
addresses-to-reveal (get-in db [:communities/all-addresses-to-reveal community-id])
share-future-addresses? (get-in db [:communities/selected-share-all-addresses community-id])
airdrop-address (get-in db [:communities/all-airdrop-addresses community-id])]
{:fx [[:effects.community/request-to-join
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal addresses-to-reveal
:airdrop-address airdrop-address
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error community-id]}]]}))
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal addresses-to-reveal
:airdrop-address airdrop-address
:share-future-addresses? share-future-addresses?
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error community-id]}]]}))
(rf/reg-event-fx :communities/request-to-join-with-addresses request-to-join-with-addresses)
@@ -37,19 +37,22 @@
(sut/sign-data cofx [community-id password sign-params])))))
(deftest request-to-join-with-signatures-test
(let [cofx {:db {}}
addresses-to-reveal [account-pub-key "0x2"]
signatures ["11111" "222222"]
expected {:fx [[:json-rpc/call
[{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress "0x1"}]
:js-response true
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error
community-id]}]]]}]
(let [cofx {:db {}}
addresses-to-reveal [account-pub-key "0x2"]
share-future-addresses? true
signatures ["11111" "222222"]
expected {:fx [[:json-rpc/call
[{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:shareFutureAddresses share-future-addresses?
:airdropAddress "0x1"}]
:js-response true
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error
community-id]}]]]}]
(is (match? expected
(sut/request-to-join-with-signatures cofx
[community-id addresses-to-reveal signatures])))))
[community-id addresses-to-reveal signatures
share-future-addresses?])))))
@@ -5,13 +5,8 @@
(def background-container
{:background-color colors/neutral-100
:flex-direction :row
:position :absolute
:overflow :hidden
:top 0
:bottom 0
:left 0
:right 0})
:flex 1
:overflow :hidden})
(def background-blur-overlay
{:position :absolute
@@ -25,13 +25,12 @@
:sub-text (i18n/label :t/explore-the-decentralized-web)}])
(defn background-image
[content-width]
[image-view-width]
[rn/image
{:resize-mode :stretch
:resize-method :scale
:style {:top 138
:width content-width}
:source (resources/get-image :onboarding-illustration)}])
{:resize-mode :stretch
:style {:flex 1
:width image-view-width}
:source (resources/get-image :onboarding-illustration)}])
(defonce progress (atom nil))
(defonce paused? (atom nil))
@@ -5,12 +5,9 @@
(defn header-container
[status-bar-height content-width index header-background]
{:position :absolute
:top 0
:left (* content-width index)
{:margin-left (* content-width index)
:padding-top (+ 30 status-bar-height)
:width content-width
:height (+ 96 status-bar-height)
:flex-direction :row
:background-color (when header-background colors/onboarding-header-black)})
@@ -62,9 +59,9 @@
:top (+ 12 status-bar-height)})
(defn carousel-container
[left animate?]
(cond->> {:position :absolute
:top 0
:flex-direction :row
:left left}
animate? (reanimated/apply-animations-to-style {:left left})))
[left animate? width]
(cond->> {:flex-direction :row
:flex 1
:width width
:margin-left left}
animate? (reanimated/apply-animations-to-style {:margin-left left})))
@@ -18,19 +18,18 @@
:size :heading-2}
(get-in header-text [index :text])]
[quo/text
{:style style/carousel-sub-text
:size :paragraph-1}
{:style style/carousel-sub-text}
(get-in header-text [index :sub-text])]])
(defn content-view
[{:keys [window-width status-bar-height index header-text header-background]} content]
(let [content-width (* 4 window-width)]
[:<>
(when content content)
[rn/view {:style {:flex 1}}
[rn/view {:style (style/header-container status-bar-height content-width index header-background)}
(for [index (range 4)]
^{:key index}
[header-text-view index window-width header-text])]]))
[header-text-view index window-width header-text])]
(when content content)]))
(defn progress-bar
[{:keys [static? progress-bar-width]}]
@@ -64,10 +63,10 @@
:swipeable (animation/composed-gestures progress paused? is-dragging? drag-amount)
:tappable (animation/tap-gesture progress paused?)
nil)]
[:<>
[rn/view {:style {:flex 1}}
[gesture/gesture-detector {:gesture identified-gesture}
[container-view {:style (style/carousel-container carousel-left animate?)}
(for [index (range 2)]
[container-view {:style (style/carousel-container carousel-left animate? (* 4 window-width))}
(for [index (range 1)]
^{:key index}
[content-view
{:window-width window-width
@@ -5,6 +5,7 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.check-before-syncing.view :as check-before-syncing]
[status-im.common.metrics-confirmation-modal.view :as metrics-modal]
[status-im.common.not-implemented :as not-implemented]
[status-im.common.resources :as resources]
[status-im.config :as config]
@@ -126,6 +127,7 @@
(defn- internal-view
[sign-in-type]
(rn/use-mount #(rf/dispatch [:centralized-metrics/check-modal metrics-modal/view]))
(let [{:keys [top]} (safe-area/get-insets)]
[rn/view {:style style/content-container}
[quo/page-nav
@@ -4,20 +4,20 @@
(def title-container
{:margin-horizontal 20
:padding-vertical 12})
:padding-bottom 20
:padding-top 12})
(defn page-container
[insets]
{:flex 1
:justify-content :space-between
:padding-top (+ (:top insets) 56)})
{:flex 1
:padding-top (+ (:top insets) 56)})
(defn page-illustration
[width]
{:flex 1
:padding-top 12
:padding-bottom 10
:width width})
{:flex 1
:margin-top 12
:margin-bottom 10
:width width})
(defn buttons
[insets]
@@ -2,11 +2,16 @@
(defn page-container
[insets]
{:flex 1
:justify-content :space-between
:padding-top (:top insets)})
{:flex 1
:padding-top (:top insets)})
(defn page-illustration
[width]
{:flex 1
:width width})
(def title-style
{:position :absolute
:left 0
:right 0
:top 0})
@@ -17,19 +17,22 @@
(defn generate-keys-title
[]
[quo/text-combinations
{:container-style {:margin-horizontal 20}
{:container-style {:margin-horizontal 20
:margin-vertical 12}
:title (i18n/label :t/generating-keys)}])
(defn saving-keys-title
[]
[quo/text-combinations
{:container-style {:margin-horizontal 20}
{:container-style {:margin-horizontal 20
:margin-vertical 12}
:title (i18n/label :t/saving-keys-to-device)}])
(defn keys-saved-title
[]
[quo/text-combinations
{:container-style {:margin-horizontal 20}
{:container-style {:margin-horizontal 20
:margin-vertical 12}
:title (i18n/label :t/keys-saved)}])
(defn sequence-animation
@@ -61,46 +64,42 @@
reanimated/easings))))))
(defn title
[insets]
(let [top-insets (+ (if rn/small-screen? 62 112) (:insets insets))
generate-keys-opacity (reanimated/use-shared-value 1)
[]
(let [generate-keys-opacity (reanimated/use-shared-value 1)
saving-keys-opacity (reanimated/use-shared-value 0)
keys-saved-opacity (reanimated/use-shared-value 0)]
(sequence-animation generate-keys-opacity saving-keys-opacity keys-saved-opacity)
[rn/view
{:position :absolute
:top top-insets}
{:style {:margin-top 56
:height 56}}
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:opacity generate-keys-opacity}
{:position :absolute})}
style/title-style)}
[generate-keys-title]]
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:opacity saving-keys-opacity}
{:position :absolute})}
style/title-style)}
[saving-keys-title]]
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:opacity keys-saved-opacity}
{:position :absolute})}
style/title-style)}
[keys-saved-title]]]))
(defn content
[]
(let [width (:width (rn/get-window))]
[rn/view
{:top 156
:position :absolute}
[rn/image
{:resize-mode :contain
:style (style/page-illustration width)
:source (resources/get-image :generate-keys1)}]]))
[rn/image
{:resize-mode :contain
:style (style/page-illustration width)
:source (resources/get-image :generate-keys1)}]))
(defn view
[]
(let [insets (safe-area/get-insets)]
[rn/view {:style (style/page-container insets)}
[:<>
[title insets]
[title]
[content]]]))
@@ -7,9 +7,8 @@
:justify-content :flex-end})
(def text-container
{:flex 1
:text-align :center
:margin-top 16
{:text-align :center
:margin-top 4
:flex-wrap :wrap})
(def plain-text
@@ -19,9 +18,8 @@
{:flex 1
:color colors/white})
(def bottom-actions-container
{:position :absolute
:background-color colors/onboarding-header-black
:left 0
:right 0
:padding-bottom 40})
(defn bottom-actions-container
[bottom-insets]
{:background-color colors/onboarding-header-black
:flex-shrink 1
:padding-bottom (+ 20 bottom-insets)})
@@ -2,6 +2,7 @@
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.contexts.onboarding.common.background.view :as background]
[status-im.contexts.onboarding.common.overlay.view :as overlay]
[status-im.contexts.onboarding.intro.style :as style]
@@ -14,7 +15,7 @@
[rn/view {:style style/page-container}
[background/view false]
[quo/bottom-actions
{:container-style style/bottom-actions-container
{:container-style (style/bottom-actions-container (safe-area/get-bottom))
:actions :two-vertical-actions
:description :bottom
:description-text [quo/text
@@ -212,6 +212,7 @@
[status-im.contexts.preview.quo.wallet.progress-bar :as progress-bar]
[status-im.contexts.preview.quo.wallet.required-tokens :as required-tokens]
[status-im.contexts.preview.quo.wallet.summary-info :as summary-info]
[status-im.contexts.preview.quo.wallet.swap-input :as swap-input]
[status-im.contexts.preview.quo.wallet.token-input :as token-input]
[status-im.contexts.preview.quo.wallet.transaction-progress :as transaction-progress]
[status-im.contexts.preview.quo.wallet.transaction-summary :as
@@ -570,6 +571,7 @@
{:name :required-tokens
:component required-tokens/view}
{:name :summary-info :component summary-info/view}
{:name :swap-input :component swap-input/view}
{:name :token-input :component token-input/view}
{:name :wallet-activity :component wallet-activity/view}
{:name :transaction-progress :component transaction-progress/view}
@@ -0,0 +1,68 @@
(ns status-im.contexts.preview.quo.wallet.swap-input
(:require
[quo.core :as quo]
[quo.foundations.resources :as resources]
[react-native.core :as rn]
[status-im.contexts.preview.quo.preview :as preview]))
(def descriptor
[{:type :select
:key :type
:options [{:key :pay}
{:key :receive}]}
{:type :select
:key :status
:options [{:key :default}
{:key :disabled}
{:key :loading}]}
{:type :select
:key :value
:options [{:key :token}
{:key :fiat}]}
{:type :boolean
:key :error?}
{:type :boolean
:key :enable-swap?}
{:type :boolean
:key :show-approval-label?}
(preview/customization-color-option)])
(defn view
[]
(let [[state set-state] (rn/use-state {:type :pay
:error? false
:token "SNT"
:customization-color :blue
:show-approval-label? false
:enable-swap? true
:status :default
:currency-symbol "€"})
[value set-value] (rn/use-state "")
on-press (fn [v] (set-value (str value v)))
delete (fn [] (set-value #(subs % 0 (dec (count %)))))]
[preview/preview-container
{:state state
:set-state set-state
:descriptor descriptor}
[quo/swap-input
(assoc state
:on-swap-press #(js/alert "Swap Pressed")
:on-token-press #(js/alert "Token Pressed")
:on-max-press #(js/alert "Max Pressed")
:value value
:fiat-value (str (.toFixed (* value 0.3) 2))
:container-style {:margin-bottom 20}
:network-tag-props {:title "Max: 200 SNT"
:networks [{:source (resources/get-network :ethereum)}]}
:approval-label-props
{:status :approve
:token-value "10"
:button-props {:on-press
#(js/alert "Approve Pressed")}
:customization-color (:customization-color state)
:token-symbol "SNT"})]
[quo/numbered-keyboard
{:left-action :dot
:delete-key? true
:on-press on-press
:on-delete delete}]]))
@@ -7,6 +7,7 @@
[react-native.safe-area :as safe-area]
[status-im.common.check-before-syncing.view :as check-before-syncing]
[status-im.common.confirmation-drawer.view :as confirmation-drawer]
[status-im.common.metrics-confirmation-modal.view :as metrics-modal]
[status-im.common.standard-authentication.core :as standard-authentication]
[status-im.config :as config]
[status-im.constants :as constants]
@@ -254,6 +255,7 @@
(defn view
[]
(rn/use-mount #(rf/dispatch [:centralized-metrics/check-modal metrics-modal/view]))
(let [[show-profiles? set-show-profiles] (rn/use-state false)
show-profiles (rn/use-callback #(set-show-profiles true))
hide-profiles (rn/use-callback #(set-show-profiles false))]
@@ -113,7 +113,8 @@
(defn view
[]
(let [profile-color (rf/sub [:profile/customization-color])
(let [view-id (rf/sub [:view-id])
profile-color (rf/sub [:profile/customization-color])
accounts-addresses (rf/sub [:wallet/addresses])
saved-addresses-addresses (rf/sub [:wallet/saved-addresses-addresses])
[address-or-ens set-address-or-ens] (rn/use-state "")
@@ -175,11 +176,12 @@
:on-press navigate-back
:margin-top (safe-area/get-top)
:accessibility-label :add-address-to-save-page-nav}]
:footer [quo/button
{:customization-color profile-color
:disabled? button-disabled?
:on-press on-press-continue}
(i18n/label :t/continue)]}
:footer (when (= view-id :screen/settings.add-address-to-save)
[quo/button
{:customization-color profile-color
:disabled? button-disabled?
:on-press on-press-continue}
(i18n/label :t/continue)])}
[quo/page-top
{:container-style style/header-container
:blur? true
@@ -121,7 +121,8 @@
:on-press on-press-save}
(i18n/label :t/save-address)]
:customization-color address-color
:gradient-cover? true}
:gradient-cover? true
:shell-overlay? true}
[quo/wallet-user-avatar
{:full-name (if (string/blank? address-label)
placeholder
@@ -0,0 +1,18 @@
(ns status-im.contexts.shell.activity-center.context
(:require
["react" :as react]
[oops.core :as oops]
[react-native.core :as rn]))
(defonce ^:private context
(react/createContext {}))
(defn provider
[state & children]
(into [:> (oops/oget context :Provider) {:value state}]
children))
(defn use-context
[]
(let [ctx (rn/use-context context)]
{:active-swipeable (oops/oget ctx :activeSwipeable)}))
@@ -15,10 +15,7 @@
(def defaults
{:filter-status :unread
:filter-type types/no-type
;; Choose the maximum number of notifications that *usually/safely* fit on
;; most screens, so that the UI doesn't have to needlessly render
;; notifications.
:notifications-per-page 8})
:notifications-per-page constants/notifications-per-page})
;;;; Navigation
@@ -1,6 +1,7 @@
(ns status-im.contexts.shell.activity-center.notification.admin.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.shell.activity-center.notification.common.style :as common-style]
[status-im.contexts.shell.activity-center.notification.common.view :as common]
@@ -23,42 +24,50 @@
:text (i18n/label :t/decline)}])
(defn- swipeable
[{:keys [active-swipeable notification extra-fn]} child]
(let [{:keys [community-id id membership-status]} notification]
[{:keys [notification extra-fn]} child]
(let [{:keys [community-id id
membership-status]} notification
accept (rn/use-callback
(fn []
(rf/dispatch [:communities/accept-request-to-join-pressed
community-id id]))
[community-id id])
decline (rn/use-callback
(fn []
(rf/dispatch [:communities/decline-request-to-join-pressed
community-id id]))
[community-id id])]
(cond
(#{constants/activity-center-membership-status-accepted
constants/activity-center-membership-status-declined}
membership-status)
[common/swipeable
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:extra-fn extra-fn}
child]
(= membership-status constants/activity-center-membership-status-pending)
[common/swipeable
{:left-button swipe-button-accept
:left-on-press #(rf/dispatch [:communities/accept-request-to-join-pressed community-id id])
:right-button swipe-button-decline
:right-on-press #(rf/dispatch [:communities/decline-request-to-join-pressed community-id
id])
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button swipe-button-accept
:left-on-press accept
:right-button swipe-button-decline
:right-on-press decline
:extra-fn extra-fn}
child]
:else
child)))
(defn view
[{:keys [notification set-swipeable-height customization-color] :as props}]
[{:keys [notification] :as props}]
(let [{:keys [author community-id id membership-status
read timestamp]} notification
community (rf/sub [:communities/community community-id])
community-name (:name community)
community-image (get-in community [:images :thumbnail :uri])]
community-name (rf/sub [:communities/name community-id])
community-logo (rf/sub [:communities/logo community-id])
customization-color (rf/sub [:profile/customization-color])]
[swipeable props
[quo/activity-log
{:title (i18n/label :t/join-request)
@@ -66,14 +75,13 @@
:icon :i/add-user
:timestamp (datetime/timestamp->relative timestamp)
:unread? (not read)
:on-layout set-swipeable-height
:context [[common/user-avatar-tag author]
(i18n/label :t/wants-to-join)
[quo/context-tag
{:type :community
:size 24
:blur? true
:community-logo community-image
:community-logo community-logo
:community-name community-name}]]
:items (condp = membership-status
constants/activity-center-membership-status-accepted
@@ -4,6 +4,7 @@
(def swipe-action-width 80)
(def swipe-button-border-radius 16)
(def swipe-button-margin 8)
(def user-avatar-tag
{:background-color colors/white-opa-10})
@@ -1,10 +1,12 @@
(ns status-im.contexts.shell.activity-center.notification.common.view
(:require
[oops.core :as oops]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[status-im.contexts.profile.utils :as profile.utils]
[status-im.contexts.shell.activity-center.context :as ac.context]
[status-im.contexts.shell.activity-center.notification.common.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -14,7 +16,7 @@
(let [profile (rf/sub [:contacts/contact-by-identity user-id])]
[rn/view
{:on-start-should-set-responder
(fn [_event]
(fn []
(rf/dispatch [:navigate-back])
(rf/dispatch [:chat.ui/show-profile user-id])
true)}
@@ -33,35 +35,27 @@
swipe-button
swipeable-ref
style]}]
(fn [_ ^js drag-x]
(let [{:keys [height] :as extra} (extra-fn)
opacity (.interpolate drag-x interpolation-opacity)
translate-x (.interpolate drag-x interpolation-translate-x)]
(fn [_ drag-x]
(let [extra (extra-fn)
opacity (oops/ocall drag-x :interpolate interpolation-opacity)
translate-x (oops/ocall drag-x :interpolate interpolation-translate-x)]
[gesture/rect-button
{:style (merge {:border-radius style/swipe-button-border-radius} style)
:accessibility-label :notification-swipe-action-button
:on-press (fn []
(when @swipeable-ref
(.close ^js @swipeable-ref)
(oops/ocall @swipeable-ref :close)
(reset! active-swipeable nil))
(on-press extra))}
[swipe-button
{:style {:opacity opacity
:transform [{:translateX translate-x}]
:height height
:flex 1
:width style/swipe-action-width}}
extra]])))
(defn- close-active-swipeable
[active-swipeable swipeable]
(fn [_]
(when (and @active-swipeable
(not= @active-swipeable @swipeable))
(.close ^js @active-swipeable))
(reset! active-swipeable @swipeable)))
(defn swipe-button-container
[{:keys [style icon text]} _]
[{:keys [style icon text]}]
[rn/animated-view
{:accessibility-label :notification-swipe
:style style}
@@ -103,47 +97,54 @@
(rf/dispatch [:activity-center.notifications/delete (:id notification)]))
(defn swipeable
[_]
(let [swipeable-ref (atom nil)]
(fn [{:keys [active-swipeable
extra-fn
left-button
left-on-press
right-button
right-on-press]}
& children]
(into
[gesture/swipeable
(merge
{:ref #(reset! swipeable-ref %)
:accessibility-label :notification-swipeable
:friction 2
:on-swipeable-will-open (close-active-swipeable active-swipeable swipeable-ref)
:children-container-style {:padding-horizontal 20}}
(when left-button
{:overshoot-left false
:left-threshold style/swipe-action-width
:render-left-actions (render-swipe-action
{:active-swipeable active-swipeable
:extra-fn extra-fn
:interpolation-opacity style/left-swipe-opacity-interpolation-js
:interpolation-translate-x
style/left-swipe-translate-x-interpolation-js
:on-press left-on-press
:swipe-button left-button
:swipeable-ref swipeable-ref
:style {:left 8}})})
(when right-button
{:overshoot-right false
:right-threshold style/swipe-action-width
:render-right-actions (render-swipe-action
{:active-swipeable active-swipeable
:extra-fn extra-fn
:interpolation-opacity style/right-swipe-opacity-interpolation-js
:interpolation-translate-x
style/right-swipe-translate-x-interpolation-js
:on-press right-on-press
:swipe-button right-button
:swipeable-ref swipeable-ref
:style {:right -8}})}))]
children))))
[{:keys [extra-fn
left-button
left-on-press
right-button
right-on-press]}
child]
(let [{:keys [active-swipeable]} (ac.context/use-context)
this-swipeable (rn/use-ref-atom nil)
set-this-swipeable (rn/use-callback #(reset! this-swipeable %)
[this-swipeable])
on-swipeable-will-open (rn/use-callback
(fn []
(when (and @active-swipeable
(not= @active-swipeable @this-swipeable))
(oops/ocall @active-swipeable :close))
(reset! active-swipeable @this-swipeable))
[@active-swipeable @this-swipeable])]
[gesture/swipeable
(cond-> {:ref set-this-swipeable
:accessibility-label :notification-swipeable
:friction 2
:on-swipeable-will-open on-swipeable-will-open
:children-container-style {:padding-horizontal 20}}
left-button
(assoc :overshoot-left false
:left-threshold style/swipe-action-width
:render-left-actions (render-swipe-action
{:active-swipeable active-swipeable
:extra-fn extra-fn
:interpolation-opacity style/left-swipe-opacity-interpolation-js
:interpolation-translate-x
style/left-swipe-translate-x-interpolation-js
:on-press left-on-press
:swipe-button left-button
:swipeable-ref this-swipeable
:style {:left style/swipe-button-margin}}))
right-button
(assoc :overshoot-right false
:right-threshold style/swipe-action-width
:render-right-actions (render-swipe-action
{:active-swipeable active-swipeable
:extra-fn extra-fn
:interpolation-opacity style/right-swipe-opacity-interpolation-js
:interpolation-translate-x
style/right-swipe-translate-x-interpolation-js
:on-press right-on-press
:swipe-button right-button
:swipeable-ref this-swipeable
:style {:right (- style/swipe-button-margin)}})))
child]))
@@ -1,6 +1,7 @@
(ns status-im.contexts.shell.activity-center.notification.community-kicked.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[status-im.contexts.shell.activity-center.notification.common.style :as common-style]
[status-im.contexts.shell.activity-center.notification.common.view :as common]
@@ -9,33 +10,34 @@
[utils.re-frame :as rf]))
(defn- swipeable
[{:keys [active-swipeable extra-fn]} child]
[{:keys [extra-fn]} child]
[common/swipeable
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:extra-fn extra-fn}
child])
(defn view
[{:keys [notification set-swipeable-height customization-color] :as props}]
(let [{:keys [community-id read
[{:keys [notification extra-fn]}]
(let [{:keys [id community-id read
timestamp]} notification
community (rf/sub [:communities/community community-id])
community-name (:name community)
community-image (get-in community [:images :thumbnail :uri])]
[swipeable props
[gesture/touchable-without-feedback
{:on-press (fn []
(rf/dispatch [:navigate-back])
(rf/dispatch [:activity-center.notifications/mark-as-read (:id notification)]))}
community-image (get-in community [:images :thumbnail :uri])
customization-color (rf/sub [:profile/customization-color])
on-press (rn/use-callback
(fn []
(rf/dispatch [:navigate-back])
(rf/dispatch [:activity-center.notifications/mark-as-read id]))
[id])]
[swipeable {:extra-fn extra-fn}
[gesture/touchable-without-feedback {:on-press on-press}
[quo/activity-log
{:title (i18n/label :t/community-kicked-heading)
:customization-color customization-color
:icon :i/placeholder
:on-layout set-swipeable-height
:timestamp (datetime/timestamp->relative timestamp)
:unread? (not read)
:context [[quo/text {:style common-style/user-avatar-tag-text}
@@ -1,6 +1,7 @@
(ns status-im.contexts.shell.activity-center.notification.community-request.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[status-im.constants :as constants]
[status-im.contexts.shell.activity-center.notification.common.style :as common-style]
@@ -10,27 +11,23 @@
[utils.re-frame :as rf]))
(defn- swipeable
[{:keys [active-swipeable extra-fn]} child]
[{:keys [extra-fn]} child]
[common/swipeable
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:extra-fn extra-fn}
child])
(defn- get-header-text-and-context
[community membership-status]
(let [community-name (:name community)
permissions (:permissions community)
open? (not= 3 (:access permissions))
community-image (get-in community [:images :thumbnail :uri])
[community-logo community-name community-permissions membership-status]
(let [open? (not= 3 (:access community-permissions))
community-context-tag [quo/context-tag
{:type :community
:size 24
:blur? true
:community-logo community-image
:community-logo community-logo
:community-name community-name}]]
(cond
(= membership-status constants/activity-center-membership-status-idle)
@@ -56,27 +53,33 @@
:t/joined-community
:t/community-request-accepted-body-text)
(when open? {:community community-name}))]
community-context-tag]}
:else nil)))
community-context-tag]})))
(defn view
[{:keys [notification set-swipeable-height customization-color] :as props}]
[{:keys [notification extra-fn]}]
(let [{:keys [community-id membership-status read
timestamp]} notification
community (rf/sub [:communities/community community-id])
{:keys [header-text context]} (get-header-text-and-context community
membership-status)]
[swipeable props
[gesture/touchable-without-feedback
{:on-press (fn []
(rf/dispatch [:navigate-back])
(rf/dispatch [:communities/navigate-to-community-overview community-id]))}
timestamp]} notification
community-name (rf/sub [:communities/name community-id])
community-logo (rf/sub [:communities/logo community-id])
community-permissions (rf/sub [:communities/permissions community-id])
customization-color (rf/sub [:profile/customization-color])
{:keys [header-text
context]} (get-header-text-and-context community-logo
community-name
community-permissions
membership-status)
on-press (rn/use-callback
(fn []
(rf/dispatch [:navigate-back])
(rf/dispatch [:communities/navigate-to-community-overview
community-id]))
[community-id])]
[swipeable {:extra-fn extra-fn}
[gesture/touchable-without-feedback {:on-press on-press}
[quo/activity-log
{:title header-text
:customization-color customization-color
:icon :i/communities
:on-layout set-swipeable-height
:timestamp (datetime/timestamp->relative timestamp)
:unread? (not read)
:context context}]]]))
@@ -1,6 +1,7 @@
(ns status-im.contexts.shell.activity-center.notification.contact-requests.view
(:require
[quo.core :as quo]
quo.theme
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.shell.activity-center.notification.common.style :as common-style]
@@ -24,48 +25,52 @@
:text (i18n/label :t/decline)}])
(defn- swipeable
[{:keys [active-swipeable extra-fn notification]} child]
[{:keys [extra-fn notification]} child]
(let [{:keys [id author message]} notification
{:keys [contact-request-state]} message
{:keys [public-key]} (rf/sub [:multiaccount/contact])
outgoing? (= public-key author)]
outgoing? (= public-key author)
accept (rn/use-callback
#(rf/dispatch [:activity-center.contact-requests/accept id])
[id])
decline (rn/use-callback
#(rf/dispatch [:activity-center.contact-requests/decline id])
[id])]
(cond
(or (#{constants/contact-request-message-state-accepted
constants/contact-request-message-state-declined}
contact-request-state)
(and outgoing? (= contact-request-state constants/contact-request-message-state-pending)))
[common/swipeable
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:extra-fn extra-fn}
child]
(and (= contact-request-state constants/contact-request-message-state-pending)
(not outgoing?))
[common/swipeable
{:left-button swipe-button-accept
:left-on-press #(rf/dispatch [:activity-center.contact-requests/accept id])
:right-button swipe-button-decline
:right-on-press #(rf/dispatch [:activity-center.contact-requests/decline id])
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button swipe-button-accept
:left-on-press accept
:right-button swipe-button-decline
:right-on-press decline
:extra-fn extra-fn}
child]
:else
child)))
(defn- outgoing-contact-request-view
[{:keys [notification set-swipeable-height customization-color]} theme]
[{:keys [notification]} theme]
(let [{:keys [chat-id message last-message accepted]} notification
{:keys [contact-request-state] :as message} (or message last-message)]
{:keys [contact-request-state] :as message} (or message last-message)
customization-color (rf/sub [:profile/customization-color])]
(if accepted
[quo/activity-log
{:title (i18n/label :t/contact-request-was-accepted)
:customization-color customization-color
:on-layout set-swipeable-height
:icon :i/add-user
:timestamp (datetime/timestamp->relative (:timestamp notification))
:unread? (not (:read notification))
@@ -76,7 +81,6 @@
[quo/activity-log
{:title (i18n/label :t/contact-request)
:customization-color customization-color
:on-layout set-swipeable-height
:icon :i/add-user
:timestamp (datetime/timestamp->relative (:timestamp notification))
:unread? (not (:read notification))
@@ -102,13 +106,22 @@
nil)}])))
(defn- incoming-contact-request-view
[{:keys [notification set-swipeable-height customization-color]} theme]
(let [{:keys [id author message last-message]} notification
message (or message last-message)]
[{:keys [notification]} theme]
(let [{:keys [id author message
last-message]} notification
customization-color (rf/sub [:profile/customization-color])
message (or message last-message)
accept (rn/use-callback
(fn []
(rf/dispatch [:activity-center.contact-requests/accept id]))
[id])
decline (rn/use-callback
(fn []
(rf/dispatch [:activity-center.contact-requests/decline id]))
[id])]
[quo/activity-log
{:title (i18n/label :t/contact-request)
:customization-color customization-color
:on-layout set-swipeable-height
:icon :i/add-user
:timestamp (datetime/timestamp->relative (:timestamp notification))
:unread? (not (:read notification))
@@ -140,13 +153,13 @@
:key :button-decline
:label (i18n/label :t/decline)
:accessibility-label :decline-contact-request
:on-press #(rf/dispatch [:activity-center.contact-requests/decline id])}
:on-press decline}
{:type :button
:subtype :positive
:key :button-accept
:label (i18n/label :t/accept)
:accessibility-label :accept-contact-request
:on-press #(rf/dispatch [:activity-center.contact-requests/accept id])}]
:on-press accept}]
nil)}]))
@@ -155,16 +168,16 @@
(let [{:keys [author message last-message]} notification
{:keys [public-key]} (rf/sub [:multiaccount/contact])
{:keys [contact-request-state]} (or message last-message)
app-theme (rf/sub [:theme])]
theme (quo.theme/use-theme)]
[swipeable props
(cond
(= public-key author)
[outgoing-contact-request-view props app-theme]
[outgoing-contact-request-view props theme]
(= contact-request-state constants/contact-request-message-state-accepted)
[rn/pressable
{:on-press #(rf/dispatch [:chat.ui/start-chat author])}
[incoming-contact-request-view props app-theme]]
[incoming-contact-request-view props theme]]
:else
[incoming-contact-request-view props app-theme])]))
[incoming-contact-request-view props theme])]))
@@ -105,7 +105,7 @@
(rf/dispatch [:activity-center.notifications/mark-as-read id]))
(defn- swipeable
[{:keys [active-swipeable extra-fn notification replying?] :as props} child]
[{:keys [extra-fn notification replying?] :as props} child]
(let [{:keys [id message
contact-verification-status]} notification
challenger? (:outgoing message)]
@@ -116,23 +116,21 @@
(and (not challenger?)
(= contact-verification-status constants/contact-verification-status-pending))
[common/swipeable
{:left-button swipe-button-reply
:left-on-press #(prepare-challenge-reply props)
:right-button swipe-button-decline
:right-on-press #(decline-challenge id)
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button swipe-button-reply
:left-on-press #(prepare-challenge-reply props)
:right-button swipe-button-decline
:right-on-press #(decline-challenge id)
:extra-fn extra-fn}
child]
(and challenger?
(= contact-verification-status constants/contact-verification-status-accepted))
[common/swipeable
{:left-button swipe-button-trust
:left-on-press #(mark-challenge-trusted id)
:right-button swipe-button-untrustworthy
:right-on-press #(mark-challenge-untrustworthy id)
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button swipe-button-trust
:left-on-press #(mark-challenge-trusted id)
:right-button swipe-button-untrustworthy
:right-on-press #(mark-challenge-untrustworthy id)
:extra-fn extra-fn}
child]
(#{constants/contact-verification-status-accepted
@@ -140,12 +138,11 @@
constants/contact-verification-status-trusted}
contact-verification-status)
[common/swipeable
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:extra-fn extra-fn}
child]
:else
@@ -154,8 +151,9 @@
(defn view
[_]
(let [reply (atom "")]
(fn [{:keys [notification set-swipeable-height replying? customization-color] :as props}]
(let [{:keys [id message
(fn [{:keys [notification replying?] :as props}]
(let [customization-color (rf/sub [:profile/customization-color])
{:keys [id message
contact-verification-status]} notification
challenger? (:outgoing message)]
;; TODO(@ilmotta): Declined challenges should only be displayed for the challengee, not the
@@ -165,86 +163,84 @@
(= contact-verification-status constants/contact-verification-status-declined))
[swipeable props
[quo/activity-log
(merge
(when-not replying?
{:on-layout set-swipeable-height})
{:title (i18n/label :t/identity-verification-request)
:customization-color customization-color
:icon :i/friend
:timestamp (datetime/timestamp->relative (:timestamp notification))
:unread? (not (:read notification))
:on-update-reply #(reset! reply %)
:replying? replying?
:max-reply-length max-reply-length
:valid-reply? valid-reply?
:context (context-tags challenger? notification)
:message (activity-message challenger? notification)
:items
(cond-> []
(and challenger?
(= contact-verification-status constants/contact-verification-status-accepted))
(concat
[{:type :button
:subtype :danger
:key :button-mark-as-untrustworthy
:label (i18n/label :t/untrustworthy)
:accessibility-label :mark-contact-verification-as-untrustworthy
:on-press #(mark-challenge-untrustworthy id)}
{:type :button
:subtype :positive
:key :button-accept
:label (i18n/label :t/accept)
:accessibility-label :mark-contact-verification-as-trusted
:on-press #(mark-challenge-trusted id)}])
{:title (i18n/label :t/identity-verification-request)
:customization-color customization-color
:icon :i/friend
:timestamp (datetime/timestamp->relative (:timestamp notification))
:unread? (not (:read notification))
:on-update-reply #(reset! reply %)
:replying? replying?
:max-reply-length max-reply-length
:valid-reply? valid-reply?
:context (context-tags challenger? notification)
:message (activity-message challenger? notification)
:items
(cond-> []
(and challenger?
(= contact-verification-status constants/contact-verification-status-accepted))
(concat
[{:type :button
:subtype :danger
:key :button-mark-as-untrustworthy
:label (i18n/label :t/untrustworthy)
:accessibility-label :mark-contact-verification-as-untrustworthy
:on-press #(mark-challenge-untrustworthy id)}
{:type :button
:subtype :positive
:key :button-accept
:label (i18n/label :t/accept)
:accessibility-label :mark-contact-verification-as-trusted
:on-press #(mark-challenge-trusted id)}])
(and challenger?
(= contact-verification-status constants/contact-verification-status-trusted))
(concat [{:type :status
:subtype :positive
:key :status-trusted
:label (i18n/label :t/status-confirmed)}])
(and challenger?
(= contact-verification-status constants/contact-verification-status-trusted))
(concat [{:type :status
:subtype :positive
:key :status-trusted
:label (i18n/label :t/status-confirmed)}])
(and challenger?
(= contact-verification-status constants/contact-verification-status-untrustworthy))
(concat [{:type :status
:subtype :negative
:key :status-untrustworthy
:label (i18n/label :t/untrustworthy)}])
(and challenger?
(= contact-verification-status
constants/contact-verification-status-untrustworthy))
(concat [{:type :status
:subtype :negative
:key :status-untrustworthy
:label (i18n/label :t/untrustworthy)}])
(and (not challenger?)
(= contact-verification-status constants/contact-verification-status-accepted))
(concat [{:type :status
:subtype :positive
:key :status-accepted
:label (i18n/label :t/replied)}])
(and (not challenger?)
(= contact-verification-status constants/contact-verification-status-accepted))
(concat [{:type :status
:subtype :positive
:key :status-accepted
:label (i18n/label :t/replied)}])
(and (not challenger?)
(= contact-verification-status constants/contact-verification-status-declined))
(concat [{:type :status
:subtype :negative
:key :status-declined
:label (i18n/label :t/declined)}])
(and (not challenger?)
(= contact-verification-status constants/contact-verification-status-declined))
(concat [{:type :status
:subtype :negative
:key :status-declined
:label (i18n/label :t/declined)}])
(and (not challenger?)
(= contact-verification-status constants/contact-verification-status-pending))
(concat
[{:type :button
:subtype :danger
:key :button-decline
:label (i18n/label :t/decline)
:accessibility-label :decline-contact-verification
:on-press #(decline-challenge id)}
(if replying?
{:type :button
:subtype :primary
:key :button-reply
:label (i18n/label :t/send-reply)
:accessibility-label :reply-to-contact-verification
:disable-when invalid-reply?
:on-press #(send-challenge-reply id @reply)}
{:type :button
:subtype :primary
:key :button-send-reply
:label (i18n/label :t/message-reply)
:accessibility-label :send-reply-to-contact-verification
:on-press #(prepare-challenge-reply props)})]))})]])))))
(and (not challenger?)
(= contact-verification-status constants/contact-verification-status-pending))
(concat
[{:type :button
:subtype :danger
:key :button-decline
:label (i18n/label :t/decline)
:accessibility-label :decline-contact-verification
:on-press #(decline-challenge id)}
(if replying?
{:type :button
:subtype :primary
:key :button-reply
:label (i18n/label :t/send-reply)
:accessibility-label :reply-to-contact-verification
:disable-when invalid-reply?
:on-press #(send-challenge-reply id @reply)}
{:type :button
:subtype :primary
:key :button-send-reply
:label (i18n/label :t/message-reply)
:accessibility-label :send-reply-to-contact-verification
:on-press #(prepare-challenge-reply props)})]))}]])))))
@@ -1,6 +1,7 @@
(ns status-im.contexts.shell.activity-center.notification.membership.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[status-im.contexts.shell.activity-center.notification.common.style :as common-style]
[status-im.contexts.shell.activity-center.notification.common.view :as common]
@@ -33,35 +34,50 @@
:text (i18n/label :t/decline)}])
(defn- swipeable
[{:keys [active-swipeable notification extra-fn]} child]
(let [{:keys [accepted dismissed id]} notification]
[{:keys [notification extra-fn]} child]
(let [{:keys [accepted dismissed
id]} notification
accept (rn/use-callback
(fn [] (rf/dispatch [:activity-center.notifications/accept id]))
[id])
dismiss (rn/use-callback
(fn [] (rf/dispatch [:activity-center.notifications/dismiss id]))
[id])]
(if (or accepted dismissed)
[common/swipeable
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:extra-fn extra-fn}
child]
[common/swipeable
{:left-button swipe-button-accept
:left-on-press #(rf/dispatch [:activity-center.notifications/accept id])
:right-button swipe-button-decline
:right-on-press #(rf/dispatch [:activity-center.notifications/dismiss id])
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button swipe-button-accept
:left-on-press accept
:right-button swipe-button-decline
:right-on-press dismiss
:extra-fn extra-fn}
child])))
(defn view
[{:keys [notification set-swipeable-height customization-color] :as props}]
(let [{:keys [id accepted dismissed author read timestamp chat-name chat-id]} notification]
[{:keys [notification] :as props}]
(let [{:keys [id accepted dismissed author read
timestamp chat-name
chat-id]} notification
customization-color (rf/sub [:profile/customization-color])
accept (rn/use-callback
(fn []
(rf/dispatch [:activity-center.notifications/accept id]))
[id])
dismiss (rn/use-callback
(fn []
(rf/dispatch [:activity-center.notifications/dismiss id]))
[id])]
[swipeable props
[pressable {:accepted accepted :chat-id chat-id}
[quo/activity-log
{:title (i18n/label :t/added-to-group-chat)
:customization-color customization-color
:on-layout set-swipeable-height
:icon :i/add-user
:timestamp (datetime/timestamp->relative timestamp)
:unread? (not read)
@@ -78,13 +94,10 @@
:key :button-accept
:label (i18n/label :t/accept)
:accessibility-label :accept-group-chat-invitation
:on-press #(rf/dispatch
[:activity-center.notifications/accept id])}
:on-press accept}
{:type :button
:subtype :danger
:key :button-decline
:label (i18n/label :t/decline)
:accessibility-label :decline-group-chat-invitation
:on-press #(rf/dispatch
[:activity-center.notifications/dismiss
id])}])}]]]))
:on-press dismiss}])}]]]))
@@ -30,25 +30,24 @@
parsed-text-children))))
(defn- swipeable
[{:keys [active-swipeable extra-fn]} child]
[{:keys [extra-fn]} child]
[common/swipeable
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:extra-fn extra-fn}
child])
(defn view
[{:keys [notification set-swipeable-height customization-color] :as props}]
[{:keys [notification extra-fn]}]
(let [{:keys [author chat-name community-id chat-id
message read timestamp]} notification
community-chat? (not (string/blank? community-id))
community (rf/sub [:communities/community community-id])
community-name (:name community)
community-image (get-in community [:images :thumbnail :uri])]
[swipeable props
community-name (rf/sub [:communities/name community-id])
community-logo (rf/sub [:communities/logo community-id])
customization-color (rf/sub [:profile/customization-color])]
[swipeable {:extra-fn extra-fn}
[gesture/touchable-without-feedback
{:on-press (fn []
(rf/dispatch [:hide-popover])
@@ -56,7 +55,6 @@
[quo/activity-log
{:title (i18n/label :t/mention)
:customization-color customization-color
:on-layout set-swipeable-height
:icon :i/mention
:timestamp (datetime/timestamp->relative timestamp)
:unread? (not read)
@@ -68,7 +66,7 @@
{:type :channel
:blur? true
:size 24
:community-logo community-image
:community-logo community-logo
:community-name community-name
:channel-name chat-name}]
[quo/context-tag
@@ -51,26 +51,26 @@
nil))
(defn- swipeable
[{:keys [active-swipeable extra-fn]} child]
[{:keys [extra-fn]} child]
[common/swipeable
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable
:extra-fn extra-fn}
{:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete
:extra-fn extra-fn}
child])
(defn view
[{:keys [notification set-swipeable-height customization-color] :as props}]
[{:keys [notification extra-fn]}]
(let [{:keys [author chat-name community-id chat-id
message read timestamp album-messages]} notification
community-chat? (not (string/blank? community-id))
community (rf/sub [:communities/community community-id])
community-name (:name community)
community-image (get-in community [:images :thumbnail :uri])
media-server-port (rf/sub [:mediaserver/port])]
[swipeable props
message read timestamp
album-messages]} notification
community-chat? (not (string/blank? community-id))
community-name (rf/sub [:communities/name community-id])
community-logo (rf/sub [:communities/logo community-id])
customization-color (rf/sub [:profile/customization-color])
media-server-port (rf/sub [:mediaserver/port])]
[swipeable {:extra-fn extra-fn}
[gesture/touchable-without-feedback
{:on-press (fn []
(rf/dispatch [:hide-popover])
@@ -78,7 +78,6 @@
[quo/activity-log
{:title (i18n/label :t/message-reply)
:customization-color customization-color
:on-layout set-swipeable-height
:icon :i/reply
:timestamp (datetime/timestamp->relative timestamp)
:unread? (not read)
@@ -89,7 +88,7 @@
{:type :channel
:blur? true
:size 24
:community-logo community-image
:community-logo community-logo
:community-name community-name
:channel-name chat-name}]
[quo/context-tag
@@ -113,10 +112,7 @@
(= (:content-type message)
constants/content-type-gif)
:gif
:else
nil)
:gif)
:body (get-message-content message
album-messages
media-server-port)}}]]]))
@@ -1,9 +1,10 @@
(ns status-im.contexts.shell.activity-center.view
(:require
[oops.core :as oops]
[quo.core :as quo]
[react-native.core :as rn]
[react-native.navigation :as navigation]
[status-im.constants :as constants]
[status-im.contexts.shell.activity-center.context :as ac.context]
[status-im.contexts.shell.activity-center.header.view :as header]
[status-im.contexts.shell.activity-center.notification-types :as types]
[status-im.contexts.shell.activity-center.notification.admin.view :as admin]
@@ -23,61 +24,68 @@
[utils.re-frame :as rf]))
(defn notification-component
[{:keys [type] :as notification} index]
(let [extra-fn (rn/use-callback
(fn []
{:notification notification})
[notification])
props {:notification notification
:extra-fn extra-fn}]
;; Notifications are expensive to render. Without `delay-render` the opening
;; animation of the Activity Center can be clunky and the time to open the
;; AC after pressing the bell icon can be high.
[rn/view {:style (style/notification-container index)}
(cond
(= type types/contact-verification)
[contact-verification/view props]
(= type types/contact-request)
[contact-requests/view props]
(= type types/mention)
[mentions/view props]
(= type types/reply)
[reply/view props]
(= type types/admin)
[admin/view props]
(some types/membership [type])
(condp = type
types/private-group-chat [membership/view props]
types/community-request [community-request/view props]
types/community-kicked [community-kicked/view props]
nil))]))
(defn- fetch-next-page
[]
(let [height (atom 0)
set-swipeable-height #(reset! height (oops/oget % "nativeEvent.layout.height"))]
(fn [{:keys [type] :as notification} index _ {:keys [active-swipeable customization-color]}]
(let [props {:height height
:customization-color customization-color
:active-swipeable active-swipeable
:set-swipeable-height set-swipeable-height
:notification notification
:extra-fn (fn [] {:height @height :notification notification})}]
[rn/view {:style (style/notification-container index)}
(cond
(= type types/contact-verification)
[contact-verification/view props]
(= type types/contact-request)
[contact-requests/view props]
(= type types/mention)
[mentions/view props]
(= type types/reply)
[reply/view props]
(= type types/admin)
[admin/view props]
(some types/membership [type])
(condp = type
types/private-group-chat [membership/view props]
types/community-request [community-request/view props]
types/community-kicked [community-kicked/view props]
nil)
:else
nil)]))))
(rf/dispatch [:activity-center.notifications/fetch-next-page]))
(defn view
[]
(let [active-swipeable (atom nil)]
(rf/dispatch [:activity-center.notifications/fetch-first-page])
(fn []
(let [notifications (rf/sub [:activity-center/notifications])
customization-color (rf/sub [:profile/customization-color])]
[quo/overlay {:type :shell}
[rn/view {:flex 1 :padding-top (navigation/status-bar-height)}
[header/header]
[rn/flat-list
{:data notifications
:render-data {:active-swipeable active-swipeable
:customization-color customization-color}
:content-container-style {:flex-grow 1}
:empty-component [empty-tab/empty-tab]
:key-fn :id
:on-scroll-to-index-failed identity
:on-end-reached #(rf/dispatch [:activity-center.notifications/fetch-next-page])
:render-fn notification-component}]]
]))))
(let [notifications (rf/sub [:activity-center/notifications])
;; We globally control the active swipeable for all notifications
;; because when a swipe left/right gesture initiates, the previously
;; active swiped notification (if any) must be removed & closed with
;; animation.
active-swipeable (rn/use-ref-atom nil)]
(rn/use-mount
(fn []
(rf/dispatch [:activity-center.notifications/fetch-first-page])))
[ac.context/provider {:active-swipeable active-swipeable}
[quo/overlay {:type :shell}
[rn/view {:style {:flex 1 :padding-top (navigation/status-bar-height)}}
[header/header]
(rn/delay-render
[rn/flat-list
{:data notifications
:initial-num-to-render constants/notifications-per-page
:content-container-style {:flex-grow 1}
:empty-component [empty-tab/empty-tab]
:key-fn :id
:on-scroll-to-index-failed identity
:on-end-reached fetch-next-page
:render-fn notification-component}])]]]))
@@ -117,13 +117,14 @@
(defn- f-internal-view
[]
(let [{:keys [keyboard-shown]} (hooks/use-keyboard)]
[:<>
(when keyboard-shown
(rn/dismiss-keyboard!))
[scan-qr-code/view
{:title (i18n/label :t/scan-qr)
:share-button? true
:on-success-scan on-qr-code-scanned}]]))
(rn/use-mount
(fn []
(when keyboard-shown
(rn/dismiss-keyboard!))))
[scan-qr-code/view
{:title (i18n/label :t/scan-qr)
:share-button? true
:on-success-scan on-qr-code-scanned}]))
(defn view
[]
@@ -4,12 +4,19 @@
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
(defonce ^:private request-id-atom (atom 0))
(defn- get-unique-request-id
[]
(swap! request-id-atom inc)
@request-id-atom)
(rf/reg-event-fx
:wallet/fetch-activities-for-current-account
(fn [{:keys [db]}]
(let [address (-> db :wallet :current-viewing-account-address)
chain-ids (chain/chain-ids db)
request-id 0
request-id (get-unique-request-id)
filters {:period {:startTimestamp 0
:endTimestamp 0}
:types []
@@ -20,9 +27,10 @@
:filterOutAssets false
:filterOutCollectibles false}
offset 0
limit 35
limit 50
request-params [request-id [address] chain-ids filters offset limit]]
{:fx [[:json-rpc/call
{:db (assoc-in db [:wallet :ui :activity-tab :request request-id] address)
:fx [[:json-rpc/call
[{;; This method is deprecated and will be replaced by
;; "wallet_startActivityFilterSession"
;; https://github.com/status-im/status-mobile/issues/19864
@@ -32,13 +40,31 @@
{:event :wallet/fetch-activities-for-current-account
:params request-params}]}]]]})))
(def ^:private activity-transaction-id (comp hash :transaction))
(rf/reg-event-fx
:wallet/activity-filtering-for-current-account-done
(fn [{:keys [db]} [{:keys [message]}]]
(let [address (-> db :wallet :current-viewing-account-address)
activities (->> message
(transforms/json->clj)
(:activities)
(cske/transform-keys transforms/->kebab-case-keyword))
sorted-activities (sort :timestamp activities)]
{:db (assoc-in db [:wallet :activities address] sorted-activities)})))
(fn [{:keys [db]} [{:keys [message requestId]}]]
(let [address (get-in db [:wallet :ui :activity-tab :request requestId])
activities (->> message
(transforms/json->clj)
(:activities)
(cske/transform-keys transforms/->kebab-case-keyword))
activities-indexed (zipmap (map activity-transaction-id activities)
activities)]
{:db (assoc-in db [:wallet :activities address] activities-indexed)})))
(def ^:private nested-merge (partial merge-with merge))
(rf/reg-event-fx
:wallet/activities-filtering-entries-updated
(fn [{:keys [db]} [{:keys [message requestId]}]]
(let [address (get-in db [:wallet :ui :activity-tab :request requestId])
activities (->> message
(transforms/json->clj)
(cske/transform-keys transforms/->kebab-case-keyword))
activities-indexed (zipmap (map activity-transaction-id activities)
activities)]
{:db (-> db
(update-in [:wallet :ui :activity-tab :request] dissoc requestId)
(update-in [:wallet :activities address] nested-merge activities-indexed))})))
@@ -11,31 +11,81 @@
(defn send-and-receive-activity
[{:keys [transaction relative-date status sender recipient token amount network-name
network-logo]}]
[quo/wallet-activity
{:transaction transaction
:timestamp relative-date
:status status
:counter 1
:first-tag {:size 24
:type :token
:token token
:amount amount}
:second-tag-prefix :t/from
:second-tag {:type :address :address sender}
:third-tag-prefix :t/to
:third-tag {:type :address :address recipient}
:fourth-tag-prefix :t/via
:fourth-tag {:size 24
:type :network
:network-name network-name
:network-logo network-logo}
:blur? false}])
network-logo token-id nft-url nft-name]}]
(if token-id
[quo/wallet-activity
{:transaction transaction
:timestamp relative-date
:status status
:counter 1
:first-tag {:size 24
:type :collectible
:collectible nft-url
:collectible-name (if (> amount 1)
(str amount " " nft-name)
nft-name)
:collectible-number token-id}
:second-tag-prefix :t/from
:second-tag {:type :address :address sender}
:third-tag-prefix :t/to
:third-tag {:type :address :address recipient}
:fourth-tag-prefix :t/via
:fourth-tag {:size 24
:type :network
:network-name network-name
:network-logo network-logo}
:blur? false}]
[quo/wallet-activity
{:transaction transaction
:timestamp relative-date
:status status
:counter 1
:first-tag {:size 24
:type :token
:token token
:amount amount}
:second-tag-prefix :t/from
:second-tag {:type :address :address sender}
:third-tag-prefix :t/to
:third-tag {:type :address :address recipient}
:fourth-tag-prefix :t/via
:fourth-tag {:size 24
:type :network
:network-name network-name
:network-logo network-logo}
:blur? false}]))
;; WIP to add the mint activity.
;(defn mint-activity
; [{:keys [transaction relative-date status recipient network-name
; network-logo nft-name nft-url token-id]}]
; [quo/wallet-activity
; {:transaction transaction
; :timestamp relative-date
; :status status
; :counter 1
; :first-tag {:size 24
; :type :collectible
; :collectible nft-url
; :collectible-name nft-name
; :collectible-number token-id}
; :second-tag-prefix :t/at
; :second-tag {:type :address :address recipient}
; :third-tag-prefix :t/to
; :third-tag {:type :address :address recipient}
; :fourth-tag-prefix :t/via
; :fourth-tag {:size 24
; :type :network
; :network-name network-name
; :network-logo network-logo}
; :blur? false}])
(defn activity-item
[{:keys [transaction] :as activity}]
(case transaction
(:send :receive) [send-and-receive-activity activity]
;; WIP to add the mint activity.
;; :mint [mint-activity activity]
nil))
(defn view
@@ -307,10 +307,14 @@
:color color
:currency currency
:currency-symbol currency-symbol}))
calculated-tokens (map calculate-token tokens)
token-priority {"SNT" 1 "STT" 1 "ETH" 2 "DAI" 3}]
calculated-tokens (map calculate-token tokens)]
(sort-by (fn [token]
(let [fiat-value (get-in token [:values :fiat-unformatted-value])
priority (get token-priority (:token token) 999)]
priority (get constants/token-sort-priority (:token token) ##Inf)]
[(- fiat-value) priority]))
calculated-tokens)))
(defn sort-tokens
[tokens]
(let [priority #(get constants/token-sort-priority (:symbol %) ##Inf)]
(sort-by (juxt (comp - :balance) priority) tokens)))
@@ -172,17 +172,20 @@
(defn get-network-details
[chain-id]
(condp contains? chain-id
#{constants/ethereum-mainnet-chain-id constants/ethereum-goerli-chain-id
constants/ethereum-sepolia-chain-id}
mainnet-network-details
(as-> chain-id $
(condp contains? $
#{constants/ethereum-mainnet-chain-id constants/ethereum-goerli-chain-id
constants/ethereum-sepolia-chain-id}
mainnet-network-details
#{constants/arbitrum-mainnet-chain-id constants/arbitrum-goerli-chain-id
constants/arbitrum-sepolia-chain-id}
arbitrum-network-details
#{constants/arbitrum-mainnet-chain-id constants/arbitrum-goerli-chain-id
constants/arbitrum-sepolia-chain-id}
arbitrum-network-details
#{constants/optimism-mainnet-chain-id constants/optimism-goerli-chain-id
constants/optimism-sepolia-chain-id}
optimism-network-details
#{constants/optimism-mainnet-chain-id constants/optimism-goerli-chain-id
constants/optimism-sepolia-chain-id}
optimism-network-details
nil))
nil)
(when $
(assoc $ :chain-id chain-id))))
@@ -149,5 +149,14 @@
expected-order ["DAI" "ETH" "SNT"]]
(is (= expected-order sorted-tokens))))))))
(deftest sort-tokens-test
(testing "sort-tokens function"
(let [mock-tokens [{:symbol "ETH" :balance 5}
{:symbol "DAI" :balance 10}
{:symbol "SNT" :balance 1}]
sorted-tokens (map :symbol (utils/sort-tokens mock-tokens))
expected-order ["DAI" "ETH" "SNT"]]
(is (= expected-order sorted-tokens)))))
@@ -1,16 +1,16 @@
(ns status-im.contexts.wallet.connected-dapps.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.plus-button.view :as plus-button]
[status-im.common.resources :as resources]
[status-im.contexts.wallet.connected-dapps.disconnect-dapp.view :as disconnect-dapp]
[status-im.contexts.wallet.connected-dapps.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.plus-button.view :as plus-button]
[status-im.common.resources :as resources]
[status-im.contexts.wallet.connected-dapps.disconnect-dapp.view :as disconnect-dapp]
[status-im.contexts.wallet.connected-dapps.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- on-disconnect
[wallet-account {:keys [name topic]}]
@@ -19,7 +19,6 @@
[:wallet-connect/disconnect-dapp
{:topic topic
:on-success (fn []
(rf/dispatch [:wallet-connect/remove-pairing-by-topic topic])
(rf/dispatch [:toasts/upsert
{:id :dapp-disconnect-success
:type :positive
@@ -83,7 +82,8 @@
[]
(let [{:keys [bottom]} (safe-area/get-insets)
{:keys [color] :as wallet-account} (rf/sub [:wallet/current-viewing-account])
pairings (rf/sub [:wallet-connect/pairings])
sessions (rf/sub
[:wallet-connect/sessions-for-current-account])
theme (quo.theme/use-theme)]
[rn/view {:flex 1}
[header
@@ -91,7 +91,7 @@
:wallet-account wallet-account
:on-close #(rf/dispatch [:navigate-back])
:on-add #(rf/dispatch [:navigate-to :screen/wallet.scan-dapp])}]
(if (empty? pairings)
(if (empty? sessions)
[quo/empty-state
{:title (i18n/label :t/no-dapps)
:description (i18n/label :t/no-dapps-description)
@@ -99,16 +99,16 @@
:container-style style/empty-container-style}]
[rn/view (style/dapps-container bottom)
[rn/flat-list
{:data pairings
{:data sessions
:always-bounce-vertical false
:content-container-style (style/dapps-list theme)
:render-fn (fn [{:keys [topic]
{:keys [icons name url]} :peerMetadata}]
:render-fn (fn [{:keys [topic pairingTopic name url iconUrl]}]
[quo/dapp
{:dapp {:avatar (get icons 0)
:name name
:value url
:topic topic}
{:dapp {:avatar iconUrl
:name name
:value url
:topic topic
:pairing-topic pairingTopic}
:accessibility-label (str "dapp-" topic)
:state :default
:action :icon
@@ -187,15 +187,8 @@
(rf/reg-event-fx
:wallet/update-receiver-networks
(fn [{:keys [db]} [selected-networks]]
(let [amount (get-in db [:wallet :ui :send :amount])
disabled-from-chain-ids (get-in db [:wallet :ui :send :disabled-from-chain-ids])
filtered-disabled-from-chain-ids (filter (fn [chain-id]
(some #(= chain-id %)
selected-networks))
disabled-from-chain-ids)]
{:db (-> db
(assoc-in [:wallet :ui :send :receiver-networks] selected-networks)
(assoc-in [:wallet :ui :send :disabled-from-chain-ids] filtered-disabled-from-chain-ids))
(let [amount (get-in db [:wallet :ui :send :amount])]
{:db (assoc-in db [:wallet :ui :send :receiver-networks] selected-networks)
:fx [[:dispatch [:wallet/get-suggested-routes {:amount amount}]]]})))
(rf/reg-event-fx
@@ -23,19 +23,6 @@
selected-networks-after [:ethereum :optimism]
expected-db {:wallet {:ui {:send {:receiver-networks selected-networks-after}}}}]
(reset! rf-db/app-db {:wallet {:ui {:send {:receiver-networks selected-networks-before}}}})
(is (match? expected-db (:db (dispatch [event-id selected-networks-after]))))))
(testing "if receiver network removed, it is also removed from disabled ones"
(let [selected-networks-before [:ethereum :optimism :arbitrum]
selected-networks-after [:ethereum :optimism]
disabled-from-chain-ids-before [:optimism :arbitrum]
disabled-from-chain-ids-after [:optimism]
expected-db {:wallet {:ui {:send {:receiver-networks selected-networks-after
:disabled-from-chain-ids
disabled-from-chain-ids-after}}}}]
(reset! rf-db/app-db {:wallet {:ui {:send {:receiver-networks selected-networks-before
:disabled-from-chain-ids
disabled-from-chain-ids-before}}}})
(is (match? expected-db (:db (dispatch [event-id selected-networks-after])))))))
(h/deftest-event :wallet/set-token-to-send
@@ -120,7 +120,7 @@
[]
[quo/alert-banner
{:action? true
:text (i18n/label :t/not-enough-assets)
:text (i18n/label :t/not-enough-assets-to-pay-gas-fees)
:button-text (i18n/label :t/buy-eth)
:on-button-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])}])
@@ -154,11 +154,9 @@
[input-state set-input-state] (rn/use-state controlled-input/init-state)
[just-toggled-mode? set-just-toggled-mode?] (rn/use-state false)
clear-input! #(set-input-state controlled-input/delete-all)
handle-on-confirm (fn []
handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount
(controlled-input/input-value
input-state)
{:amount amount
:stack-id current-screen-id}]))
{fiat-currency :currency} (rf/sub [:profile/profile])
{token-symbol :symbol
@@ -157,7 +157,6 @@
[quo/data-item
{:container-style style/detail-item
:blur? false
:description :default
:card? false
:status :default
:size :small
+35 -18
View File
@@ -23,22 +23,39 @@
:block-number blockNumber
:accounts accounts})
(case event-type
"pending-transaction-status-changed" {:fx
[[:dispatch
[:wallet/pending-transaction-status-changed-received
(transforms/js->clj event-js)]]]}
"wallet-owned-collectibles-filtering-done" {:fx [[:dispatch
[:wallet/owned-collectibles-filtering-done
(transforms/js->clj event-js)]]]}
"wallet-get-collectibles-details-done" {:fx [[:dispatch
[:wallet/get-collectible-details-done
(transforms/js->clj event-js)]]]}
"wallet-tick-reload" {:fx [[:dispatch [:wallet/reload]]]}
"wallet-blockchain-status-changed" {:fx [[:dispatch
[:wallet/blockchain-status-changed
(transforms/js->clj event-js)]]]}
"wallet-activity-filtering-done" {:fx
[[:dispatch
[:wallet/activity-filtering-for-current-account-done
(transforms/js->clj event-js)]]]}
"pending-transaction-status-changed"
{:fx
[[:dispatch
[:wallet/pending-transaction-status-changed-received
(transforms/js->clj event-js)]]]}
"wallet-owned-collectibles-filtering-done"
{:fx [[:dispatch
[:wallet/owned-collectibles-filtering-done
(transforms/js->clj event-js)]]]}
"wallet-get-collectibles-details-done"
{:fx [[:dispatch
[:wallet/get-collectible-details-done
(transforms/js->clj event-js)]]]}
"wallet-tick-reload"
{:fx [[:dispatch [:wallet/reload]]]}
"wallet-blockchain-status-changed"
{:fx [[:dispatch
[:wallet/blockchain-status-changed
(transforms/js->clj event-js)]]]}
"wallet-activity-filtering-done"
{:fx
[[:dispatch
[:wallet/activity-filtering-for-current-account-done
(transforms/js->clj event-js)]]]}
"wallet-activity-filtering-entries-updated"
{:fx [[:dispatch
[:wallet/activities-filtering-entries-updated
(transforms/js->clj event-js)]]]}
(log/debug ::unknown-wallet-event :type event-type)))))
@@ -0,0 +1,40 @@
(ns status-im.contexts.wallet.swap.set-spending-cap.style
(:require [quo.foundations.colors :as colors]))
(def container
{:flex 1
:margin-top -20})
(def detail-item
{:flex 1
:height 36
:background-color :transparent})
(def content-container
{:padding-top 12
:padding-horizontal 20
:padding-bottom 32})
(def title-container
{:margin-horizontal 4})
(def title-line-with-margin-top
{:flex-direction :row
:margin-top 4})
(def details-container
{:flex-direction :row
:justify-content :space-between
:height 52
:padding-top 7
:padding-horizontal 1
:margin-bottom 8})
(def summary-section-container
{:padding-horizontal 20
:padding-bottom 16})
(defn section-label
[theme]
{:margin-bottom 8
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
@@ -0,0 +1,253 @@
(ns status-im.contexts.wallet.swap.set-spending-cap.view
(:require
[quo.core :as quo]
[quo.foundations.resources :as resources]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.common.standard-authentication.core :as standard-auth]
[status-im.contexts.wallet.common.utils.external-links :as external-links]
[status-im.contexts.wallet.swap.set-spending-cap.style :as style]
[utils.address :as address-utils]
[utils.i18n :as i18n]
[utils.navigation :as navigation]
[utils.re-frame :as rf]))
(defn- swap-title
[{:keys [pay-token-symbol pay-amount account provider]}]
[rn/view {:style style/content-container}
[rn/view {:style {:flex-direction :row}}
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :set-spending-cap-of}
(i18n/label :t/set-spending-cap-of)]]
[rn/view {:style style/title-line-with-margin-top}
[quo/summary-tag
{:token pay-token-symbol
:label (str pay-amount " " pay-token-symbol)
:type :token}]
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :for}
(i18n/label :t/for)]]
[rn/view {:style style/title-line-with-margin-top}
[quo/summary-tag
{:label (:full-name provider)
:type :network
:image-source (resources/get-network (:name provider))
:customization-color (:color provider)}]
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :on}
(i18n/label :t/on)]]
[rn/view {:style style/title-line-with-margin-top}
[quo/summary-tag
{:label (:name account)
:type :account
:emoji (:emoji account)
:customization-color (:color account)}]]])
(defn- spending-cap-section
[{:keys [theme amount token-symbol]}]
[rn/view {:style style/summary-section-container}
[quo/text
{:size :paragraph-2
:weight :medium
:style (style/section-label theme)
:accessibility-label :spending-cap-label}
(i18n/label :t/spending-cap)]
[quo/approval-info
{:type :spending-cap
:unlimited-icon? false
:label (str amount " " token-symbol)
:avatar-props {:token token-symbol}}]])
(defn- account-section
[{:keys [theme account pay-token-symbol pay-token-amount]}]
[rn/view {:style style/summary-section-container}
[quo/text
{:size :paragraph-2
:weight :medium
:style (style/section-label theme)
:accessibility-label :account-label}
(i18n/label :t/account)]
[quo/approval-info
{:type :account
:unlimited-icon? false
:label (:name account)
:description (address-utils/get-short-wallet-address (:address account))
:tag-label (str pay-token-amount " " pay-token-symbol)
:avatar-props {:emoji (:emoji account)
:customization-color (:color account)}}]])
(defn- on-option-press
[{:keys [chain-id contract-address]}]
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[quo/action-drawer
[[{:icon :i/link
:accessibility-label :view-on-etherscan
:on-press (fn []
(rf/dispatch
[:wallet/navigate-to-chain-explorer-from-bottom-sheet
(external-links/get-explorer-url-by-chain-id chain-id)
contract-address]))
:label (i18n/label :t/view-on-eth)
:right-icon :i/external}]]])}]))
(defn- token-section
[{:keys [theme token-address token-symbol network-chain-id]}]
[rn/view {:style style/summary-section-container}
[quo/text
{:size :paragraph-2
:weight :medium
:style (style/section-label theme)
:accessibility-label :token-label}
(i18n/label :t/token)]
[quo/approval-info
{:type :token-contract
:option-icon :i/options
:on-option-press #(on-option-press {:chain-id network-chain-id
:contract-address token-address})
:unlimited-icon? false
:label token-symbol
:description (address-utils/get-short-wallet-address token-address)
:avatar-props {:token token-symbol}}]])
(defn- spender-contract-section
[{:keys [theme provider network-chain-id]}]
[rn/view {:style style/summary-section-container}
[quo/text
{:size :paragraph-2
:weight :medium
:style (style/section-label theme)
:accessibility-label :spender-contract-label}
(i18n/label :t/spender-contract)]
[quo/approval-info
{:type :token-contract
:option-icon :i/options
:on-option-press #(on-option-press {:chain-id network-chain-id
:contract-address (:contract-address provider)})
:unlimited-icon? false
:label (:full-name provider)
:description (address-utils/get-short-wallet-address (:contract-address provider))
:avatar-props {:image (resources/get-network (:name provider))}}]])
(defn- data-item
[{:keys [network-image title subtitle size loading?]}]
[quo/data-item
{:container-style style/detail-item
:blur? false
:card? false
:network-image network-image
:subtitle-type (if network-image :network :default)
:status (if loading? :loading :default)
:title title
:subtitle subtitle
:size size}])
(defn- transaction-details
[{:keys [estimated-time-min max-fees network loading-fees?]}]
[rn/view {:style style/details-container}
[:<>
[data-item
{:title (i18n/label :t/network)
:subtitle (:full-name network)
:network-image (:source network)}]
[data-item
{:title (i18n/label :t/est-time)
:subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time-min)})}]
[data-item
{:title (i18n/label :t/max-fees)
:subtitle max-fees
:loading? loading-fees?
:size :small}]]])
(defn footer
[{:keys [estimated-time-min native-currency-symbol network theme account-color loading-fees?]}]
(let [native-token (when native-currency-symbol
(rf/sub [:wallet/token-by-symbol
native-currency-symbol]))
fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted
native-token])
on-auth-success (rn/use-callback #(js/alert "Not implemented yet"))]
[rn/view {:style {:margin-bottom -10}}
[transaction-details
{:estimated-time-min estimated-time-min
:max-fees fee-formatted
:network network
:loading-fees? loading-fees?
:theme theme}]
[standard-auth/slide-button
{:size :size-48
:track-text (i18n/label :t/slide-to-swap)
:container-style {:z-index 2}
:customization-color account-color
:disabled? loading-fees?
:on-auth-success on-auth-success
:auth-button-label (i18n/label :t/confirm)}]]))
(defn view
[]
(let [theme (quo.theme/use-theme)
swap-transaction-data (rf/sub [:wallet/swap])
{:keys [asset-to-pay network pay-amount
providers swap-proposal
loading-fees?]} swap-transaction-data
estimated-time-min (:estimated-time swap-proposal)
pay-token-symbol (:symbol asset-to-pay)
pay-token-address (:address asset-to-pay)
native-currency-symbol (get-in swap-proposal [:from :native-currency-symbol])
account (rf/sub [:wallet/current-viewing-account])
account-color (:color account)
provider (first providers)]
[rn/view {:style style/container}
[floating-button-page/view
{:footer-container-padding 0
:header [quo/page-nav
{:icon-name :i/close
:on-press navigation/navigate-back
:margin-top 8
:background :blur
:accessibility-label :top-bar}]
:footer [footer
{:estimated-time-min estimated-time-min
:native-currency-symbol native-currency-symbol
:network network
:account-color account-color
:provider provider
:loading-fees? loading-fees?
:theme theme}]
:gradient-cover? true
:customization-color account-color}
[:<>
[swap-title
{:pay-token-symbol pay-token-symbol
:pay-amount pay-amount
:account account
:provider provider}]
[spending-cap-section
{:token-symbol pay-token-symbol
:amount pay-amount
:theme theme}]
[account-section
{:account account
:pay-token-symbol pay-token-symbol
:pay-token-amount pay-amount
:theme theme}]
[token-section
{:token-symbol pay-token-symbol
:token-address pay-token-address
:network-chain-id (:chain-id network)
:theme theme}]
[spender-contract-section
{:provider provider
:network-chain-id (:chain-id network)
:theme theme}]]]]))
@@ -16,4 +16,7 @@
[quo/button
{:on-press #(rf/dispatch [:navigate-to-within-stack
[:screen/wallet.swap-confirmation :screen/wallet.swap-propasal]])}
"Swap confirmation"]]))
"Swap confirmation"]
[quo/button
{:on-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap])}
"Set spending cap"]]))
@@ -85,12 +85,61 @@
(defn required-networks-supported?
[proposal supported-networks]
(let [required-networks (get-in proposal [:params :requiredNamespaces :eip155 :chains])
supported-eip155 (set (map chain-id->eip155 supported-networks))]
(every? #(contains? supported-eip155 %) required-networks)))
(let [supported-namespaces #{:eip155}
required-namespaces (get-in proposal [:params :requiredNamespaces])]
(when (every? #(contains? supported-namespaces %)
(keys required-namespaces))
(let [required-networks (get-in required-namespaces [:eip155 :chains])
supported-eip155 (set (map chain-id->eip155 supported-networks))]
(every? #(contains? supported-eip155 %)
required-networks)))))
(defn get-networks-by-mode
[db]
(let [test-mode? (get-in db [:profile/profile :test-networks-enabled?])
networks (get-in db [:wallet :networks (if test-mode? :test :prod)])]
(mapv #(-> % :chain-id) networks)))
(defn add-full-testnet-name
"Updates the `:full-name` key with the full testnet name if using testnet `:chain-id`.\n
e.g. `{:full-name \"Mainnet\"}` -> `{:full-name \"Mainnet Sepolia\"`}`"
[network]
(let [add-testnet-name (fn [testnet-name]
(update network :full-name #(str % " " testnet-name)))]
(condp #(contains? %1 %2) (:chain-id network)
constants/sepolia-chain-ids (add-testnet-name constants/sepolia-full-name)
constants/goerli-chain-ids (add-testnet-name constants/goerli-full-name)
network)))
(defn event-should-be-handled?
[db {:keys [topic]}]
(some #(= topic %)
(map :topic (:wallet-connect/sessions db))))
(defn sdk-session->db-session
[{:keys [topic expiry pairingTopic] :as session}]
{:topic topic
:expiry expiry
:sessionJson (transforms/clj->json session)
:pairingTopic pairingTopic
:name (get-in session [:peer :metadata :name])
:iconUrl (get-in session [:peer :metadata :icons 0])
:url (get-in session [:peer :metadata :url])
:accounts (get-in session [:namespaces :eip155 :accounts])
:disconnected false})
(defn filter-operable-accounts
[accounts]
(filter #(and (:operable? %)
(not (:watch-only? %)))
accounts))
(defn filter-sessions-for-account-addresses
[account-addresses sessions]
(filter (fn [{:keys [accounts]}]
(some (fn [account]
(some (fn [account-address]
(clojure.string/includes? account account-address))
account-addresses))
accounts))
sessions))
@@ -31,13 +31,6 @@
:event wc-event
:handler handler})))
(rf/reg-fx
:effects.wallet-connect/fetch-pairings
(fn [{:keys [web3-wallet on-success on-fail]}]
(-> (wallet-connect/get-pairings web3-wallet)
(promesa/then on-success)
(promesa/catch on-fail))))
(rf/reg-fx
:effects.wallet-connect/pair
(fn [{:keys [web3-wallet url on-success on-fail]}]
@@ -48,15 +41,10 @@
(rf/reg-fx
:effects.wallet-connect/disconnect
(fn [{:keys [web3-wallet topic on-success on-fail]}]
(-> (wallet-connect/core-pairing-disconnnect web3-wallet topic)
(promesa/then on-success)
(promesa/catch on-fail))))
(rf/reg-fx
:effects.wallet-connect/fetch-active-sessions
(fn [{:keys [web3-wallet on-success on-fail]}]
(-> (wallet-connect/get-active-sessions web3-wallet)
(fn [{:keys [web3-wallet topic reason on-success on-fail]}]
(-> (wallet-connect/disconnect-session {:web3-wallet web3-wallet
:topic topic
:reason reason})
(promesa/then on-success)
(promesa/catch on-fail))))
@@ -74,6 +62,13 @@
(promesa/then on-success)
(promesa/catch on-fail)))))
(rf/reg-fx
:effects.wallet-connect/fetch-active-sessions
(fn [{:keys [web3-wallet on-success on-fail]}]
(-> (wallet-connect/get-active-sessions web3-wallet)
(promesa/then on-success)
(promesa/catch on-fail))))
(rf/reg-fx
:effects.wallet-connect/sign-message
(fn [{:keys [password address data rpc-method on-success on-error]}]
@@ -90,22 +85,33 @@
(promesa/then on-success)
(promesa/catch on-error)))))
(rf/reg-fx
:effects.wallet-connect/prepare-transaction
(fn [{:keys [tx chain-id on-success on-error]}]
(-> (transactions/prepare-transaction tx
chain-id
transactions/default-tx-priority)
(promesa/then on-success)
(promesa/catch on-error))))
(rf/reg-fx
:effects.wallet-connect/sign-transaction
(fn [{:keys [password address chain-id tx on-success on-error]}]
(fn [{:keys [password address chain-id tx-hash tx-args on-success on-error]}]
(-> (transactions/sign-transaction (security/safe-unmask-data password)
address
tx
tx-hash
tx-args
chain-id)
(promesa/then on-success)
(promesa/catch on-error))))
(rf/reg-fx
:effects.wallet-connect/send-transaction
(fn [{:keys [password address chain-id tx on-success on-error]}]
(fn [{:keys [password address chain-id tx-hash tx-args on-success on-error]}]
(-> (transactions/send-transaction (security/safe-unmask-data password)
address
tx
tx-hash
tx-args
chain-id)
(promesa/then on-success)
(promesa/catch on-error))))
@@ -8,7 +8,8 @@
status-im.contexts.wallet.wallet-connect.responding-events
[status-im.contexts.wallet.wallet-connect.utils :as wc-utils]
[taoensso.timbre :as log]
[utils.i18n :as i18n]))
[utils.i18n :as i18n]
[utils.transforms :as types]))
(rf/reg-event-fx
:wallet-connect/init
@@ -22,12 +23,7 @@
(fn [{:keys [db]} [web3-wallet]]
{:db (assoc db :wallet-connect/web3-wallet web3-wallet)
:fx [[:dispatch [:wallet-connect/register-event-listeners]]
[:effects.wallet-connect/fetch-pairings
{:web3-wallet web3-wallet
:on-fail #(log/error "Failed to get dApp pairings" {:error %})
:on-success (fn [data]
(rf/dispatch [:wallet-connect/set-pairings
(js->clj data :keywordize-keys true)]))}]]}))
[:dispatch [:wallet-connect/fetch-persisted-sessions]]]}))
(rf/reg-event-fx
:wallet-connect/register-event-listeners
@@ -40,7 +36,11 @@
[:effects.wallet-connect/register-event-listener
[web3-wallet
constants/wallet-connect-session-request-event
#(rf/dispatch [:wallet-connect/on-session-request %])]]]})))
#(rf/dispatch [:wallet-connect/on-session-request %])]]
[:effects.wallet-connect/register-event-listener
[web3-wallet
constants/wallet-connect-session-delete-event
#(rf/dispatch [:wallet-connect/on-session-delete %])]]]})))
(rf/reg-event-fx
:wallet-connect/on-init-fail
@@ -54,20 +54,22 @@
(fn [{:keys [db]} [proposal]]
(log/info "Received Wallet Connect session proposal: " {:id (:id proposal)})
(let [accounts (get-in db [:wallet :accounts])
without-watched (remove :watch-only? (vals accounts))
current-viewing-address (get-in db [:wallet :current-viewing-account-address])
available-accounts (wallet-connect-core/filter-operable-accounts (vals accounts))
networks (wallet-connect-core/get-networks-by-mode db)
session-networks (wallet-connect-core/proposal-networks-intersection proposal
networks)
required-networks-supported? (wallet-connect-core/required-networks-supported? proposal
networks)]
(if required-networks-supported?
(if (and (not-empty session-networks) required-networks-supported?)
{:db (update db
:wallet-connect/current-proposal assoc
:request proposal
:session-networks session-networks
:address (-> without-watched
first
:address))
:address (or current-viewing-address
(-> available-accounts
first
:address)))
:fx [[:dispatch
[:open-modal :screen/wallet.wallet-connect-session-proposal]]]}
{:fx [[:dispatch
@@ -85,9 +87,16 @@
(rf/reg-event-fx
:wallet-connect/on-session-request
(fn [_ [event]]
(log/info "Received Wallet Connect session request: " event)
{:fx [[:dispatch [:wallet-connect/process-session-request event]]]}))
(fn [{:keys [db]} [event]]
(when (wallet-connect-core/event-should-be-handled? db event)
{:fx [[:dispatch [:wallet-connect/process-session-request event]]]})))
(rf/reg-event-fx
:wallet-connect/on-session-delete
(fn [{:keys [db]} [{:keys [topic] :as event}]]
(when (wallet-connect-core/event-should-be-handled? db event)
(log/info "Received Wallet Connect session delete: " event)
{:fx [[:dispatch [:wallet-connect/disconnect-session topic]]]})))
(rf/reg-event-fx
:wallet-connect/reset-current-session-proposal
@@ -104,19 +113,6 @@
(fn [{:keys [db]}]
{:db (dissoc db :wallet-connect/current-request)}))
(rf/reg-event-fx
:wallet-connect/set-pairings
(fn [{:keys [db]} [pairings]]
{:db (assoc db :wallet-connect/pairings pairings)}))
(rf/reg-event-fx
:wallet-connect/remove-pairing-by-topic
(fn [{:keys [db]} [topic]]
{:db (update db
:wallet-connect/pairings
(fn [pairings]
(remove #(= (:topic %) topic) pairings)))}))
(rf/reg-event-fx
:wallet-connect/disconnect-dapp
(fn [{:keys [db]} [{:keys [topic on-success on-fail]}]]
@@ -124,8 +120,13 @@
{:fx [[:effects.wallet-connect/disconnect
{:web3-wallet web3-wallet
:topic topic
:reason (wallet-connect/get-sdk-error
constants/wallet-connect-user-disconnected-reason-key)
:on-fail on-fail
:on-success on-success}]]})))
:on-success (fn []
(rf/dispatch [:wallet-connect/disconnect-session topic])
(when on-success
(on-success)))}]]})))
(rf/reg-event-fx
:wallet-connect/pair
@@ -137,15 +138,6 @@
:on-fail #(log/error "Failed to pair with dApp" {:error %})
:on-success #(log/info "dApp paired successfully")}]]})))
(rf/reg-event-fx
:wallet-connect/fetch-active-sessions
(fn [{:keys [db]}]
(let [web3-wallet (get db :wallet-connect/web3-wallet)]
{:fx [[:effects.wallet-connect/fetch-active-sessions
{:web3-wallet web3-wallet
:on-fail #(log/error "Failed to get active sessions" {:error %})
:on-success #(log/info "Got active sessions successfully" {:sessions %})}]]})))
(rf/reg-event-fx
:wallet-connect/approve-session
(fn [{:keys [db]}]
@@ -204,10 +196,63 @@
{:version version}))}]]]}
{:fx [[:dispatch [:wallet-connect/pair scanned-text]]]}))))
;; We first load sessions from database, then we initiate a call to Wallet Connect SDK and
;; then replace the list we have stored in the database with the one that came from the SDK.
;; In addition to that, we also update the backend state by marking sessions that are not
;; active anymore by calling `:wallet-connect/disconnect-session`.
(rf/reg-event-fx
:wallet-connect/fetch-active-sessions-success
(fn [{:keys [db now]} [sessions]]
(let [persisted-sessions (:wallet-connect/sessions db)
account-addresses (->> (get-in db [:wallet :accounts])
vals
wallet-connect-core/filter-operable-accounts
(map :address))
sessions (->> (js->clj sessions :keywordize-keys true)
vals
(map wallet-connect-core/sdk-session->db-session)
(wallet-connect-core/filter-sessions-for-account-addresses
account-addresses))
session-topics (set (map :topic sessions))
expired-sessions (filter
(fn [{:keys [expiry topic]}]
(or (< expiry (/ now 1000))
(not (contains? session-topics topic))))
persisted-sessions)]
{:fx (mapv (fn [{:keys [topic]}]
[:dispatch [:wallet-connect/disconnect-session topic]])
expired-sessions)
:db (assoc db :wallet-connect/sessions sessions)})))
(rf/reg-event-fx
:wallet-connect/fetch-active-sessions
(fn [{:keys [db]}]
(let [web3-wallet (get db :wallet-connect/web3-wallet)]
{:fx [[:effects.wallet-connect/fetch-active-sessions
{:web3-wallet web3-wallet
:on-fail #(log/error "Failed to get active sessions" {:error %})
:on-success #(rf/dispatch [:wallet-connect/fetch-active-sessions-success %])}]]})))
(rf/reg-event-fx
:wallet-connect/fetch-persisted-sessions-success
(fn [{:keys [db]} [sessions]]
{:db (assoc db :wallet-connect/persisted-sessions sessions)}))
(let [sessions' (mapv (fn [{:keys [sessionJson] :as session}]
(assoc session
:accounts
(-> sessionJson
types/json->clj
:namespaces
:eip155
:accounts)))
sessions)]
{:fx [[:dispatch [:wallet-connect/fetch-active-sessions]]]
:db (assoc db :wallet-connect/sessions sessions')})))
(rf/reg-event-fx
:wallet-connect/fetch-persisted-sessions-fail
(fn [_ [error]]
(log/info "Wallet Connect fetch persisted sessions failed" error)
{:fx [[:dispatch [:wallet-connect/fetch-active-sessions]]]}))
(rf/reg-event-fx
:wallet-connect/fetch-persisted-sessions
@@ -218,7 +263,7 @@
;; 0 means, return everything
:params [0]
:on-success [:wallet-connect/fetch-persisted-sessions-success]
:on-error #(log/info "Wallet Connect fetch persisted sessions failed" %)}]]]}))
:on-error [:wallet-connect/fetch-persisted-sessions-fail]}]]]}))
(rf/reg-event-fx
:wallet-connect/persist-session
@@ -226,5 +271,22 @@
{:fx [[:json-rpc/call
[{:method "wallet_addWalletConnectSession"
:params [(js/JSON.stringify session-info)]
:on-success #(log/info "Wallet Connect session persisted")
:on-success (fn []
(log/info "Wallet Connect session persisted")
(rf/dispatch [:wallet-connect/fetch-persisted-sessions]))
:on-error #(log/info "Wallet Connect session persistence failed" %)}]]]}))
(rf/reg-event-fx
:wallet-connect/disconnect-session
(fn [{:keys [db]} [topic]]
{:db (update db
:wallet-connect/sessions
(fn [sessions]
(->> sessions
(remove #(= (:topic %) topic))
(into []))))
:fx [[:json-rpc/call
[{:method "wallet_disconnectWalletConnectSession"
:params [topic]
:on-success #(log/info "Wallet Connect session disconnected")
:on-error #(log/info "Wallet Connect session persistence failed" %)}]]]}))
@@ -0,0 +1,31 @@
(ns status-im.contexts.wallet.wallet-connect.modals.common.fees-data-item.view
(:require [quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[status-im.contexts.wallet.wallet-connect.modals.common.style :as style]
[utils.i18n :as i18n]))
(defn- fees-subtitle
[{:keys [text error?]}]
(let [theme (quo.theme/use-theme)]
[quo/text
{:weight :medium
:size :paragraph-2
:style {:color (if error?
(colors/resolve-color :danger theme)
(colors/theme-colors colors/neutral-100
colors/white
theme))}}
text]))
(defn view
[{:keys [fees fees-error]}]
[quo/data-item
{:size :small
:status :default
:card? false
:container-style style/data-item
:title (i18n/label :t/max-fees)
:custom-subtitle (fn [] [fees-subtitle
{:text (or fees (i18n/label :t/no-fees))
:error? (= fees-error :not-enough-assets-to-pay-gas-fees)}])}])
@@ -13,7 +13,7 @@
(rf/dispatch [:wallet-connect/respond-current-session password]))
(defn view
[{:keys [warning-label slide-button-text disabed?]} & children]
[{:keys [warning-label slide-button-text disabled?]} & children]
(let [{:keys [customization-color]} (rf/sub [:wallet-connect/current-request-account-details])]
[rn/view {:style style/content-container}
(into [rn/view
@@ -23,7 +23,7 @@
[standard-authentication/slide-button
{:size :size-48
:track-text slide-button-text
:disabled? disabed?
:disabled? disabled?
:customization-color customization-color
:on-auth-success on-auth-success
:auth-button-label (i18n/label :t/confirm)}]]
@@ -1,10 +1,15 @@
(ns status-im.contexts.wallet.wallet-connect.modals.common.header.style)
(def header-container
{:padding-vertical 12})
{:padding-vertical 12
:flex-direction :column
:align-items :stretch})
(def header-dapp-name
{:margin-top -4})
{:flex-direction :row})
(def header-text
{:margin-vertical 4})
(def header-account-name
{:padding-top 4})
{:align-items :flex-start})
@@ -5,22 +5,23 @@
(defn view
[{:keys [label dapp account]}]
[rn/view
{:style style/header-container}
[quo/text
{:size :heading-1
:weight :semi-bold}
(let [{:keys [name icons]} (:peerMetadata dapp)]
[rn/view {:style style/header-dapp-name}
[rn/view {:style style/header-container}
(let [{:keys [name iconUrl]} dapp]
[rn/view {:style style/header-dapp-name}
[rn/view {:flex 1}
[quo/summary-tag
{:type :dapp
:label name
:image-source (first icons)}]])
(str " " label " ")
(let [{:keys [emoji customization-color name]} account]
[rn/view {:style style/header-account-name}
[quo/summary-tag
{:type :account
:emoji emoji
:label name
:customization-color customization-color}]])]])
:image-source iconUrl}]]])
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/header-text}
label]
(let [{:keys [emoji customization-color name]} account]
[rn/view {:style style/header-account-name}
[quo/summary-tag
{:type :account
:emoji emoji
:label name
:customization-color customization-color}]])])
@@ -1,8 +1,11 @@
(ns status-im.contexts.wallet.wallet-connect.modals.send-transaction.view
(:require [quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.contexts.wallet.wallet-connect.modals.common.data-block.view :as data-block]
[status-im.contexts.wallet.wallet-connect.modals.common.fees-data-item.view :as
fees-data-item]
[status-im.contexts.wallet.wallet-connect.modals.common.footer.view :as footer]
[status-im.contexts.wallet.wallet-connect.modals.common.header.view :as header]
[status-im.contexts.wallet.wallet-connect.modals.common.page-nav.view :as page-nav]
@@ -40,7 +43,7 @@
:not-enough-assets
:t/not-enough-assets))}])
[footer/view
{:warning-label (i18n/label :t/wallet-connect-send-transaction-warning)
{:warning-label (i18n/label :t/wallet-connect-sign-warning)
:slide-button-text (i18n/label :t/slide-to-send)
:disabled? error-state}
[quo/data-item
@@ -51,11 +54,7 @@
:subtitle-type :network
:network-image (:source network)
:subtitle (:full-name network)}]
[quo/data-item
{:size :small
:status :default
:card? false
:container-style style/data-item
:title (i18n/label :t/max-fees)
:subtitle (or max-fees-fiat-formatted (i18n/label :t/no-fees))}]]]]))
[fees-data-item/view
{:fees max-fees-fiat-formatted
:fees-error error-state}]]]]))
@@ -3,6 +3,8 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.contexts.wallet.wallet-connect.modals.common.data-block.view :as data-block]
[status-im.contexts.wallet.wallet-connect.modals.common.fees-data-item.view :as
fees-data-item]
[status-im.contexts.wallet.wallet-connect.modals.common.footer.view :as footer]
[status-im.contexts.wallet.wallet-connect.modals.common.header.view :as header]
[status-im.contexts.wallet.wallet-connect.modals.common.page-nav.view :as page-nav]
@@ -28,13 +30,7 @@
:account account}]
[data-block/view]]
[footer/view
{:warning-label (i18n/label :t/wallet-connect-sign-message-warning)
{:warning-label (i18n/label :t/wallet-connect-sign-warning)
:slide-button-text (i18n/label :t/slide-to-sign)}
[quo/data-item
{:size :small
:status :default
:card? false
:container-style style/data-item
:title (i18n/label :t/max-fees)
:subtitle (i18n/label :t/no-fees)}]]]]))
[fees-data-item/view]]]]))
@@ -3,6 +3,8 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.contexts.wallet.wallet-connect.modals.common.data-block.view :as data-block]
[status-im.contexts.wallet.wallet-connect.modals.common.fees-data-item.view :as
fees-data-item]
[status-im.contexts.wallet.wallet-connect.modals.common.footer.view :as footer]
[status-im.contexts.wallet.wallet-connect.modals.common.header.view :as header]
[status-im.contexts.wallet.wallet-connect.modals.common.page-nav.view :as page-nav]
@@ -40,7 +42,7 @@
:not-enough-assets
:t/not-enough-assets))}])
[footer/view
{:warning-label (i18n/label :t/wallet-connect-sign-transaction-warning)
{:warning-label (i18n/label :t/wallet-connect-sign-warning)
:slide-button-text (i18n/label :t/slide-to-sign)
:disabled? error-state}
[quo/data-item
@@ -51,11 +53,7 @@
:subtitle-type :network
:network-image (:source network)
:subtitle (:full-name network)}]
[quo/data-item
{:size :small
:status :default
:card? false
:container-style style/data-item
:title (i18n/label :t/max-fees)
:subtitle (or max-fees-fiat-formatted (i18n/label :t/no-fees))}]]]]))
[fees-data-item/view
{:fees max-fees-fiat-formatted
:fees-error error-state}]]]]))
@@ -1,18 +1,32 @@
(ns status-im.contexts.wallet.wallet-connect.processing-events
(:require [clojure.string :as string]
(:require [cljs-bean.core :as bean]
[clojure.string :as string]
[native-module.core :as native-module]
[re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core]
[status-im.contexts.wallet.wallet-connect.transactions :as transactions]
[taoensso.timbre :as log]
[utils.transforms :as transforms]))
(rf/reg-event-fx
:wallet-connect/process-session-request
(fn [{:keys [db]} [event]]
(let [method (wallet-connect-core/get-request-method event)
:wallet-connect/show-request-modal
(fn [{:keys [db]}]
(let [event (get-in db [:wallet-connect/current-request :event])
method (wallet-connect-core/get-request-method event)
screen (wallet-connect-core/method-to-screen method)]
(if screen
{:fx [[:dispatch [:open-modal screen]]]}
(log/error "Didn't find screen for Wallet Connect method"
{:method method
:event :wallet-connect/process-session-request})))))
(rf/reg-event-fx
:wallet-connect/process-session-request
(fn [{:keys [db]} [event]]
(let [method (wallet-connect-core/get-request-method event)
existing-event (get-in db [:wallet-connect/current-request :event])]
;; NOTE: make sure we don't show two requests at the same time
(when-not existing-event
{:db (assoc-in db [:wallet-connect/current-request :event] event)
:fx [(condp = method
constants/wallet-connect-eth-send-transaction-method
@@ -31,12 +45,7 @@
[:dispatch [:wallet-connect/process-sign-typed]]
constants/wallet-connect-personal-sign-method
[:dispatch [:wallet-connect/process-personal-sign]])
[:dispatch [:open-modal screen]]]}
(log/error "Didn't find screen for Wallet Connect method"
{:method method
:event :wallet-connect/process-session-request})))))
[:dispatch [:wallet-connect/process-personal-sign]])]}))))
(rf/reg-event-fx
:wallet-connect/process-personal-sign
@@ -48,7 +57,8 @@
assoc
:address (string/lower-case address)
:raw-data raw-data
:display-data (or parsed-data raw-data))})))
:display-data (or parsed-data raw-data))
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
(rf/reg-event-fx
:wallet-connect/process-eth-sign
@@ -60,44 +70,53 @@
assoc
:address (string/lower-case address)
:raw-data raw-data
:display-data (or parsed-data raw-data))})))
:display-data (or parsed-data raw-data))
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
(rf/reg-event-fx
:wallet-connect/prepare-transaction-success
(fn [{:keys [db]} [prepared-tx chain-id]]
(let [{:keys [tx-args]} prepared-tx
tx (bean/->clj tx-args)
address (-> tx :from string/lower-case)
display-data (transactions/beautify-transaction tx)]
{:db (update-in db
[:wallet-connect/current-request]
assoc
:address address
:raw-data prepared-tx
:transaction tx
:chain-id chain-id
:display-data display-data)
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
(rf/reg-event-fx
:wallet-connect/process-eth-send-transaction
(fn [{:keys [db]}]
(let [event (wallet-connect-core/get-db-current-request-event db)
display-data (-> event
clj->js
(js/JSON.stringify nil 2))
{:keys [from] :as tx} (-> event wallet-connect-core/get-request-params first)
chain-id (-> event
(get-in [:params :chainId])
wallet-connect-core/eip155->chain-id)]
{:db (update-in db
[:wallet-connect/current-request]
assoc
:address (string/lower-case from)
:raw-data tx
:chain-id chain-id
:display-data display-data)})))
(let [event (wallet-connect-core/get-db-current-request-event db)
tx (-> event wallet-connect-core/get-request-params first)
chain-id (-> event
(get-in [:params :chainId])
wallet-connect-core/eip155->chain-id)]
{:fx [[:effects.wallet-connect/prepare-transaction
{:tx tx
:chain-id chain-id
:on-success #(rf/dispatch [:wallet-connect/prepare-transaction-success % chain-id])
:on-error #(rf/dispatch [:wallet-connect/on-processing-error %])}]]})))
(rf/reg-event-fx
:wallet-connect/process-eth-sign-transaction
(fn [{:keys [db]}]
(let [event (wallet-connect-core/get-db-current-request-event db)
display-data (.stringify js/JSON (clj->js event) nil 2)
{:keys [from] :as tx} (-> event wallet-connect-core/get-request-params first)
chain-id (-> event
(get-in [:params :chainId])
wallet-connect-core/eip155->chain-id)]
{:db (update-in db
[:wallet-connect/current-request]
assoc
:address (string/lower-case from)
:raw-data tx
:chain-id chain-id
:display-data display-data)})))
(let [event (wallet-connect-core/get-db-current-request-event db)
tx (-> event wallet-connect-core/get-request-params first)
chain-id (-> event
(get-in [:params :chainId])
wallet-connect-core/eip155->chain-id)]
{:fx [[:effects.wallet-connect/prepare-transaction
{:tx tx
:chain-id chain-id
:on-success #(rf/dispatch [:wallet-connect/prepare-transaction-success % chain-id])
:on-error #(rf/dispatch [:wallet-connect/on-processing-error %])}]]})))
(rf/reg-event-fx
:wallet-connect/process-sign-typed
@@ -108,11 +127,31 @@
(transforms/js-dissoc :types :primaryType)
(transforms/js-stringify 2))
(catch js/Error _ nil))]
;; TODO: decide if we should proceed if the typed-data is invalid JSON or fail ahead of time
(when (nil? parsed-data) (log/error "Invalid typed data"))
{:db (update-in db
[:wallet-connect/current-request]
assoc
:address (string/lower-case address)
:display-data (or parsed-data raw-data)
:raw-data raw-data)})))
(if (nil? parsed-data)
{:fx [[:dispatch
[:wallet-connect/on-processing-error
(ex-info "Failed to parse JSON typed data" {:data raw-data})]]]}
{:db (update-in db
[:wallet-connect/current-request]
assoc
:address (string/lower-case address)
:display-data (or parsed-data raw-data)
:raw-data raw-data)
:fx [[:dispatch [:wallet-connect/show-request-modal]]]}))))
;; TODO: we should reject a request if processing fails
(rf/reg-event-fx
:wallet-connect/on-processing-error
(fn [{:keys [db]} [error]]
(let [{:keys [address event]} (get db :wallet-connect/current-request)
method (wallet-connect-core/get-request-method event)
screen (wallet-connect-core/method-to-screen method)]
(log/error "Failed to process Wallet Connect request"
{:error error
:address address
:method method
:wallet-connect-event event
:event :wallet-connect/on-processing-error})
{:fx [[:dispatch [:dismiss-modal screen]]
[:dispatch [:wallet-connect/reset-current-request]]]})))
@@ -58,34 +58,38 @@
(rf/reg-event-fx
:wallet-connect/respond-send-transaction-data
(fn [{:keys [db]} [password]]
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)]
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)
{:keys [tx-hash tx-args]} raw-data]
{:fx [[:effects.wallet-connect/send-transaction
{:password password
:address address
:chain-id chain-id
:tx raw-data
:tx-hash tx-hash
:tx-args tx-args
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]})))
(rf/reg-event-fx
:wallet-connect/respond-sign-transaction-data
(fn [{:keys [db]} [password]]
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)]
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)
{:keys [tx-hash tx-args]} raw-data]
{:fx [[:effects.wallet-connect/sign-transaction
{:password password
:address address
:chain-id chain-id
:tx raw-data
:tx-hash tx-hash
:tx-params tx-args
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]})))
;; TODO: should reject if "signing" fails
(rf/reg-event-fx
:wallet-connect/on-sign-error
(fn [{:keys [db]} [error]]
(let [event (get-in db [:wallet-connect/current-request :event])
{:keys [raw-data address]} (get db :wallet-connect/current-request)
method (wallet-connect-core/get-request-method event)
screen (wallet-connect-core/method-to-screen method)]
(let [{:keys [raw-data address event]} (get db :wallet-connect/current-request)
method (wallet-connect-core/get-request-method event)
screen (wallet-connect-core/method-to-screen method)]
(log/error "Failed to sign Wallet Connect request"
{:error error
:address address
@@ -132,7 +136,7 @@
:proposal current-proposal
:on-success #(log/info "Wallet Connect session proposal rejected")
:on-error #(log/error "Wallet Connect unable to reject session proposal")}]
[:dispatch [:wallet-connect/reset-current-session]]]})))
[:dispatch [:wallet-connect/reset-current-session-proposal]]]})))
;; NOTE: Currently we only reject a session if the user rejected it
;; But this needs to be solidified to ensure other cases:
@@ -59,3 +59,8 @@
password
chain-id
legacy?))
(defn wallet-get-suggested-fees
[chain-id]
(-> (call-rpc "wallet_getSuggestedFees" chain-id)
(promesa/then transforms/js->clj)))

Some files were not shown because too many files have changed in this diff Show More