Compare commits

...
Author SHA1 Message Date
Richard Ramos 63d341277e fix: sync history regardless of using a data plan or wifi 2024-02-02 13:15:48 -04:00
Mohamed Javid 4dded72878 Wallet - Add support for Sepolia testnet and Rarible collectible provider (#18519)
This commit adds support for the Sepolia test network and Rarible collectible/collection provider.

---------

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-02 19:44:00 +05:30
Ulises Manuel c7bf9f00b5 [#18459] - Add cursor-based input to token-input (#18594) 2024-02-01 20:02:00 -06:00
Ulises Manuel b6ddd8b5ff [#18495] Fix token not ready for next screen (#18532)
* Fix exception thrown re-frame don't have the subs' value ready

* Make Token malli schema more lenient

*  Make `get-standard-crypto-format` able to work with `nil` values

*  Refactor token screen to move subscriptions inside render function

* Make token input reactive to on-swap

* Remove `crypto-currency?` atom to properly react to state changes

* Fix component tests
2024-02-01 17:51:12 -06:00
Yevheniia Berdnyk 3f9cd3a688 e2e: fixes 01.02 2024-02-01 20:36:51 +02:00
Mohsen 675897c9ab [#18313] feat: implement change profile picture (#18440) 2024-02-01 19:45:58 +03:30
Siddarth Kumar df76881c90 chore: disable hermes via gradle project env var (#18675)
fixes #18493

We enabled `hermes` for android in the `react-native` upgrade to `0.72.5`
Although things seemed fine but developers were seeing frequent crashes in their local environment.

After some investigation the crashes were traced to max native call stack depth in `hermes` engine.
Disabling `hermes` for local debug builds helps fix that issue.

This commit disables `hermes` by default with the help of a exporting an environment variable in the `make run-android` command.
It is annoying that this also modifies `android/gradle.properties` so we keep `hermesEnabled` as `false` there as well.
We also enable `hermes` when generating release builds so that we can take advantage of `hermes` engine in release builds.

We also add a log to print whether `hermes` is enabled or not. I think its helpful to have this so that we know whether `hermes` is enabled or not.
2024-02-01 20:56:18 +05:30
Brian Sztamfater e54932057d fix: reintroduce changes for sending erc20 token and collectibles (#18679)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-02-01 12:03:04 -03:00
Volodymyr Kozieiev a95f2a0f52 Implement menu on collectible preview page (#18651) 2024-02-01 12:48:48 +00:00
Icaro Motta 38dbd6687e Disable flag ACCOUNT_SELECTION_ENABLED by default (#18690) 2024-01-31 23:58:37 -03:00
Ajay SivanandIcaro Motta 4633fc22ed Show token permissions only for role permissions (#18549)
Token requirements that are not role permissions (currently channel permissions)
should not be displayed to the user while they see permissions to join.

Admin, Master, Owner or minted token permissions are not membership permissions,
but they still should be displayed so the user understand the role they will
assume after joining.

Co-authored-by: Icaro Motta <icaro.ldm@gmail.com>
2024-01-31 21:03:54 -03:00
Ibrahem Khalil 7e31e028fa Bring back enable-notifications onboarding screen static image (#18654) 2024-01-31 16:01:30 +02:00
Mohamed Javid 067b2d5afb [Fix] Wallet - Route not found (#18682)
This commit fixes the route not fetched bug on the Wallet send flow.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-01-31 16:45:21 +05:30
BalogunofAfrica e4ba0d3062 fix: android shadow (#18504) 2024-01-31 12:02:27 +01:00
Icaro Motta 11e49c0cc3 Fix community logo/name not rendered correctly (#18633) 2024-01-30 19:37:45 -03:00
68 changed files with 1061 additions and 594 deletions
+3
View File
@@ -273,6 +273,9 @@ run-re-frisk: ##@run Start re-frisk server
# TODO: Migrate this to a Nix recipe, much the same way as nix/mobile/android/targets/release-android.nix
run-android: export TARGET := android
# Disabled for debug builds to avoid 'maximum call stack exceeded' errors.
# https://github.com/status-im/status-mobile/issues/18493
run-android: export ORG_GRADLE_PROJECT_hermesEnabled := false
# INFO: If it's empty (no devices attached, parsing issues, script error) - for Nix it's the same as not set.
run-android: export ANDROID_ABI_INCLUDE ?= $(shell ./scripts/adb_devices_abis.sh)
run-android: ##@run Build Android APK and start it on the device
+3 -1
View File
@@ -57,4 +57,6 @@ newArchEnabled=false
# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
hermesEnabled=true
# Disabled for debug builds to avoid 'maximum call stack exceeded' errors.
# https://github.com/status-im/status-mobile/issues/18493
hermesEnabled=false
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.4'
library 'status-jenkins-lib@v1.8.5'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.4'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent { label 'linux' }
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.4'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent { label 'linux' }
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.4'
library 'status-jenkins-lib@v1.8.5'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.4'
library 'status-jenkins-lib@v1.8.5'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.15'
library 'status-jenkins-lib@v1.8.5'
pipeline {
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.15'
library 'status-jenkins-lib@v1.8.5'
pipeline {
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.15'
library 'status-jenkins-lib@v1.8.5'
pipeline {
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.15'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent { label 'macos' }
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.15'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent { label params.AGENT_LABEL }
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.15'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent { label 'linux' }
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.7.15'
library 'status-jenkins-lib@v1.8.5'
pipeline {
agent {
+3
View File
@@ -79,6 +79,9 @@ in stdenv.mkDerivation rec {
STATUS_GO_SRC_OVERRIDE = statusGoSrcOverride;
ANDROID_ABI_SPLIT = androidAbiSplit;
ANDROID_ABI_INCLUDE = androidAbiInclude;
# Disabled for debug builds to avoid 'maximum call stack exceeded' errors.
# https://github.com/status-im/status-mobile/issues/18493
ORG_GRADLE_PROJECT_hermesEnabled = true;
# Fix for ERR_OSSL_EVP_UNSUPPORTED error.
NODE_OPTIONS = "--openssl-legacy-provider";
+7
View File
@@ -33,10 +33,17 @@ trap "rm -vf ${SECRETS_FILE_PATH}" EXIT ERR INT QUIT
# Names of variables containing secrets to save in env file.
SECRETS_ENV_VARS=(
'ALCHEMY_ETHEREUM_MAINNET_TOKEN'
'ALCHEMY_ETHEREUM_GOERLI_TOKEN'
'ALCHEMY_ETHEREUM_SEPOLIA_TOKEN'
'ALCHEMY_ARBITRUM_GOERLI_TOKEN'
'ALCHEMY_ARBITRUM_MAINNET_TOKEN'
'ALCHEMY_ARBITRUM_SEPOLIA_TOKEN'
'ALCHEMY_OPTIMISM_GOERLI_TOKEN'
'ALCHEMY_OPTIMISM_MAINNET_TOKEN'
'ALCHEMY_OPTIMISM_SEPOLIA_TOKEN'
'RARIBLE_MAINNET_API_KEY'
'RARIBLE_TESTNET_API_KEY'
'INFURA_TOKEN'
'INFURA_TOKEN_SECRET'
'OPENSEA_API_KEY'
+27 -6
View File
@@ -63,10 +63,17 @@
{status-im.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
status-im.config/INFURA_TOKEN #shadow/env "INFURA_TOKEN"
status-im.config/OPENSEA_API_KEY #shadow/env "OPENSEA_API_KEY"
status-im.config/RARIBLE_MAINNET_API_KEY #shadow/env "RARIBLE_MAINNET_API_KEY"
status-im.config/RARIBLE_TESTNET_API_KEY #shadow/env "RARIBLE_TESTNET_API_KEY"
status-im.config/ALCHEMY_ETHEREUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ETHEREUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ETHEREUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ETHEREUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ARBITRUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ARBITRUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_GOERLI_TOKEN #shadow/env "ALCHEMY_OPTIMISM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ARBITRUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_MAINNET_TOKEN #shadow/env "ALCHEMY_OPTIMISM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ARBITRUM_MAINNET_TOKEN"}
status-im.config/ALCHEMY_OPTIMISM_GOERLI_TOKEN #shadow/env "ALCHEMY_OPTIMISM_GOERLI_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_OPTIMISM_SEPOLIA_TOKEN"}
:compiler-options {:output-feature-set :es5
;; We disable `:fn-deprecated` warnings because we
;; are managing deprecation via clj-kondo and we
@@ -90,10 +97,17 @@
{status-im.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
status-im.config/INFURA_TOKEN #shadow/env "INFURA_TOKEN"
status-im.config/OPENSEA_API_KEY #shadow/env "OPENSEA_API_KEY"
status-im.config/RARIBLE_MAINNET_API_KEY #shadow/env "RARIBLE_MAINNET_API_KEY"
status-im.config/RARIBLE_TESTNET_API_KEY #shadow/env "RARIBLE_TESTNET_API_KEY"
status-im.config/ALCHEMY_ETHEREUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ETHEREUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ETHEREUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ETHEREUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ARBITRUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ARBITRUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_GOERLI_TOKEN #shadow/env "ALCHEMY_OPTIMISM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ARBITRUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_MAINNET_TOKEN #shadow/env "ALCHEMY_OPTIMISM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ARBITRUM_MAINNET_TOKEN"}
status-im.config/ALCHEMY_OPTIMISM_GOERLI_TOKEN #shadow/env "ALCHEMY_OPTIMISM_GOERLI_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_OPTIMISM_SEPOLIA_TOKEN"}
:compiler-options {:output-feature-set :es6
;;disable for android build as there
;;is an intermittent warning with deftype
@@ -123,10 +137,17 @@
status-im.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
status-im.config/INFURA_TOKEN #shadow/env "INFURA_TOKEN"
status-im.config/OPENSEA_API_KEY #shadow/env "OPENSEA_API_KEY"
status-im.config/RARIBLE_MAINNET_API_KEY #shadow/env "RARIBLE_MAINNET_API_KEY"
status-im.config/RARIBLE_TESTNET_API_KEY #shadow/env "RARIBLE_TESTNET_API_KEY"
status-im.config/ALCHEMY_ETHEREUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ETHEREUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ETHEREUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ETHEREUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ARBITRUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ARBITRUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_GOERLI_TOKEN #shadow/env "ALCHEMY_OPTIMISM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ARBITRUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_MAINNET_TOKEN #shadow/env "ALCHEMY_OPTIMISM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ARBITRUM_MAINNET_TOKEN"}
status-im.config/ALCHEMY_OPTIMISM_GOERLI_TOKEN #shadow/env "ALCHEMY_OPTIMISM_GOERLI_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_OPTIMISM_SEPOLIA_TOKEN"}
:compiler-options
{;; needed because we override require and it
;; messes with source-map which reports callstack
@@ -44,22 +44,37 @@
[categ]
(reduce-kv #(assoc %1 (name %2) %3) {} categ))
(defn role-permission?
[token-permission]
(contains? constants/community-role-permissions (:type token-permission)))
(defn membership-permission?
[token-permission]
(= (:type token-permission) constants/community-token-permission-become-member))
(defn <-rpc
[c]
(-> c
(set/rename-keys {:canRequestAccess :can-request-access?
:canManageUsers :can-manage-users?
:canDeleteMessageForEveryone :can-delete-message-for-everyone?
:canJoin :can-join?
:requestedToJoinAt :requested-to-join-at
:isMember :is-member?
:adminSettings :admin-settings
:tokenPermissions :token-permissions
:communityTokensMetadata :tokens-metadata
:introMessage :intro-message
:muteTill :muted-till
:lastOpenedAt :last-opened-at
:joinedAt :joined-at})
(set/rename-keys
{:canRequestAccess :can-request-access?
:canManageUsers :can-manage-users?
:canDeleteMessageForEveryone :can-delete-message-for-everyone?
;; This flag is misleading based on its name alone
;; because it should not be used to decide if the user
;; is *allowed* to join. Allowance is based on token
;; permissions. Still, the flag can be used to know
;; whether or not the user will have to wait until an
;; admin approves a join request.
:canJoin :can-join?
:requestedToJoinAt :requested-to-join-at
:isMember :is-member?
:adminSettings :admin-settings
:tokenPermissions :token-permissions
:communityTokensMetadata :tokens-metadata
:introMessage :intro-message
:muteTill :muted-till
:lastOpenedAt :last-opened-at
:joinedAt :joined-at})
(update :admin-settings
set/rename-keys
{:pinMessageAllMembersEnabled :pin-message-all-members-enabled?})
@@ -67,9 +82,16 @@
(update :chats <-chats-rpc)
(update :token-permissions seq)
(update :categories <-categories-rpc)
(assoc :role-permissions?
(->> c
:tokenPermissions
vals
(some role-permission?)))
(assoc :membership-permissions?
(some #(= (:type %) constants/community-token-permission-become-member)
(vals (:tokenPermissions c))))
(->> c
:tokenPermissions
vals
(some membership-permission?)))
(assoc :token-images
(reduce (fn [acc {sym :symbol image :image}]
(assoc acc sym image))
@@ -19,7 +19,8 @@
transactions-management-enabled?
current-fleet
webview-debug
test-networks-enabled?]}]
test-networks-enabled?
is-sepolia-enabled?]}]
(keep
identity
[{:size :small
@@ -110,6 +111,14 @@
#(re-frame/dispatch [:profile.settings/toggle-test-networks])
:accessory :switch
:active test-networks-enabled?}
{:size :small
:title "Enable Sepolia as test network"
:accessibility-label :enable-sepolia-as-test-network
:container-margin-bottom 8
:on-press
#(re-frame/dispatch [:profile.settings/toggle-sepolia-test-network])
:accessory :switch
:active is-sepolia-enabled?}
{:size :small
:title (i18n/label :t/set-currency)
:accessibility-label :wallet-change-currency
@@ -130,6 +139,7 @@
(views/defview advanced-settings
[]
(views/letsubs [test-networks-enabled? [:profile/test-networks-enabled?]
is-sepolia-enabled? [:profile/is-sepolia-enabled?]
light-client-enabled? [:profile/light-client-enabled?]
webview-debug [:profile/webview-debug]
network-name [:network-name]
@@ -145,6 +155,7 @@
:current-fleet current-fleet
:dev-mode? false
:webview-debug webview-debug
:test-networks-enabled? test-networks-enabled?})
:test-networks-enabled? test-networks-enabled?
:is-sepolia-enabled? is-sepolia-enabled?})
:key-fn (fn [_ i] (str i))
:render-fn render-item}]))
@@ -3,9 +3,11 @@
[quo.foundations.colors :as colors]))
(defn divider
[theme]
[theme blur?]
{:border-top-width 1
:border-top-color (colors/theme-colors colors/neutral-10 colors/neutral-90 theme)
:border-top-color (if blur?
colors/white-opa-5
(colors/theme-colors colors/neutral-10 colors/neutral-90 theme))
:margin-top 8
:margin-bottom 7
:align-items :center
@@ -14,9 +14,9 @@
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme)))
(defn- divider
[theme]
[theme blur?]
[rn/view
{:style (style/divider theme)
{:style (style/divider theme blur?)
:accessible true
:accessibility-label :divider}])
@@ -45,7 +45,7 @@
blur?]}]
[:<>
(when add-divider?
[divider theme])
[divider theme blur?])
[maybe-pressable disabled?
{:accessibility-label accessibility-label
:style (style/container {:sub-label sub-label
@@ -182,7 +182,7 @@
(defn- view-internal
[{:keys [title title-icon type theme description blur? community-name community-logo button-icon
account-name emoji
account-name emoji context-tag-type
on-button-press
on-button-long-press
button-disabled? account-avatar-emoji account-avatar-type customization-color icon-avatar
@@ -214,6 +214,7 @@
:description description
:community-name community-name
:community-logo community-logo
:context-tag-type context-tag-type
:customization-color customization-color
:account-name account-name
:emoji emoji}]]
+1 -1
View File
@@ -12,7 +12,7 @@
[:cat
[:map {:closed true}
[:size {:optional true :default 32} [:or keyword? pos-int?]]
[:token {:optional true} [:or keyword? string?]]
[:token {:optional true} [:maybe [:or keyword? string?]]]
[:style {:optional true} map?]
;; Ignores `token` and uses this as parameter to `rn/image`'s source.
[:image-source {:optional true} [:maybe [:or :schema.common/image-source :string]]]]]
+50 -41
View File
@@ -1,6 +1,7 @@
(ns quo.components.wallet.token-input.view
(:require
[clojure.string :as string]
[oops.core :as oops]
[quo.components.buttons.button.view :as button]
[quo.components.dividers.divider-line.view :as divider-line]
[quo.components.markdown.text :as text]
@@ -72,18 +73,25 @@
[token-name-text theme text]])
(defn input-section
[{:keys [on-change-text value value-atom]}]
(let [input-ref (atom nil)
set-ref #(reset! input-ref %)
focus-input #(when-let [ref ^js @input-ref]
(.focus ref))
controlled-input? (some? value)
handle-on-change-text (fn [v]
(when-not controlled-input?
(reset! value-atom v))
(when on-change-text
(on-change-text v)))]
(fn [{:keys [theme token customization-color show-keyboard? crypto? currency value error?]
[{:keys [on-change-text value value-atom on-selection-change]}]
(let [input-ref (atom nil)
set-ref #(reset! input-ref %)
focus-input #(when-let [ref ^js @input-ref]
(.focus ref))
controlled-input? (some? value)
handle-on-change-text (fn [v]
(when-not controlled-input?
(reset! value-atom v))
(when on-change-text
(on-change-text v)))
handle-selection-change (fn [^js e]
(when on-selection-change
(-> e
(oops/oget "nativeEvent.selection")
(js->clj :keywordize-keys true)
(on-selection-change))))]
(fn [{:keys [theme token customization-color show-keyboard? crypto? currency value error?
selection]
:or {show-keyboard? true}}]
[rn/pressable
{:on-press focus-input
@@ -102,7 +110,9 @@
:max-length 12
:on-change-text handle-on-change-text
:selection-color customization-color
:show-soft-input-on-focus show-keyboard?}
:show-soft-input-on-focus show-keyboard?
:on-selection-change handle-selection-change
:selection (clj->js selection)}
controlled-input? (assoc :value value)
(not controlled-input?) (assoc :default-value @value-atom))]]
[token-label
@@ -111,33 +121,32 @@
:value (if controlled-input? value @value-atom)}]])))
(defn- view-internal
[{:keys [on-swap]}]
(let [width (:width (rn/get-window))
value-atom (reagent/atom nil)
crypto? (reagent/atom true)
handle-on-swap (fn []
(swap! crypto? not)
(when on-swap
(on-swap @crypto?)))]
(fn [{:keys [theme container-style value] :as props}]
[rn/view {:style (merge (style/main-container width) container-style)}
[rn/view {:style style/amount-container}
[input-section
(assoc props
:value-atom value-atom
:crypto? @crypto?)]
[button/button
{:icon true
:icon-only? true
:size 32
:on-press handle-on-swap
:type :outline
:accessibility-label :reorder}
:i/reorder]]
[divider-line/view {:container-style (style/divider theme)}]
[data-info
(assoc props
:crypto? @crypto?
:amount (or value @value-atom))]])))
[]
(let [width (:width (rn/get-window))
value-atom (reagent/atom nil)
crypto? (reagent/atom true)]
(fn [{:keys [theme container-style value on-swap] :as props}]
(let [handle-on-swap (fn []
(swap! crypto? not)
(when on-swap (on-swap @crypto?)))]
[rn/view {:style (merge (style/main-container width) container-style)}
[rn/view {:style style/amount-container}
[input-section
(assoc props
:value-atom value-atom
:crypto? @crypto?)]
[button/button
{:icon true
:icon-only? true
:size 32
:on-press handle-on-swap
:type :outline
:accessibility-label :reorder}
:i/reorder]]
[divider-line/view {:container-style (style/divider theme)}]
[data-info
(assoc props
:crypto? @crypto?
:amount (or value @value-atom))]]))))
(def view (quo.theme/with-theme view-internal))
+56 -49
View File
@@ -3,62 +3,69 @@
(:require
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.platform :as platform]))
[react-native.platform :as platform]
[utils.number]))
;; Set radius coefficient based on Android Version.
;; A good way to know is to check if shadow color is supported i.e API level 28 (Android 9) above
(def ^:private shadow-radius-coefficient (if platform/is-shadow-color-supported? 0.8 0.1))
(defn- get-shadow-color
"Calculates the shadow color based on a given color and opacity, with an optional coefficient.
- Arity [color opacity]: Uses a default coefficient value of 5 (empirically determined) to compute the shadow color.
- Arity [color opacity coefficient]: Uses the specified coefficient to compute the shadow color.
On Android platforms, the shadow color's alpha is adjusted based on the product of opacity and
coefficient, constrained between 0 and 1. Returns a map with :shadow-color and :shadow-opacity keys."
([color opacity]
(get-shadow-color color opacity 5))
([color opacity coefficient]
{:shadow-color (if platform/android?
(colors/alpha color (utils.number/value-in-range (* opacity coefficient) 0 1))
color)
:shadow-opacity opacity}))
(defn- get-shadow-radius
"Computes shadow radius and elevation properties based on a given radius, with an optional coefficient.
- Arity [radius]: Uses a default coefficient of 0.8 for API level 28 and above and 0.1 otherwise (empirically determined) to compute the elevation.
- Arity [radius coefficient]: Uses the specified coefficient to compute the elevation.
Returns a map with :elevation, computed as the product of radius and coefficient, and :shadow-radius keys."
([radius]
(get-shadow-radius radius shadow-radius-coefficient))
([radius coefficient]
{:elevation (* radius coefficient)
:shadow-radius radius}))
(def ^:private shadows
(let [dark-normal {1 {:shadow-color (when platform/ios?
(colors/alpha colors/neutral-100 0.5))
:shadow-offset {:width 0 :height 4}
:elevation 3
:shadow-opacity 1
:shadow-radius 20}
2 {:shadow-color (when platform/ios?
(colors/alpha colors/neutral-100 0.64))
:shadow-offset {:width 0 :height 4}
:elevation 4
:shadow-opacity 1
:shadow-radius 20}
3 {:shadow-color (when platform/ios?
(colors/alpha colors/neutral-100 0.64))
:shadow-offset {:width 0 :height 12}
:elevation 8
:shadow-opacity 1
:shadow-radius 20}
4 {:shadow-color (when platform/ios?
(colors/alpha colors/neutral-100 0.72))
:shadow-offset {:width 0 :height 16}
:shadow-opacity 1
:shadow-radius 20
:elevation 15}}
(let [dark-normal {1 (merge {:shadow-offset {:width 0 :height 4}}
(get-shadow-color colors/neutral-100 0.5)
(get-shadow-radius 20))
2 (merge {:shadow-offset {:width 0 :height 4}}
(get-shadow-color colors/neutral-100 0.64)
(get-shadow-radius 20))
3 (merge {:shadow-offset {:width 0 :height 12}}
(get-shadow-color colors/neutral-100 0.64)
(get-shadow-radius 20))
4 (merge {:shadow-offset {:width 0 :height 16}}
(get-shadow-color colors/neutral-100 0.72)
(get-shadow-radius 20))}
dark-normal-inverted (-> dark-normal
(update-in [:soft :shadow-offset :height] -)
(update-in [:medium :shadow-offset :height] -)
(update-in [:intense :shadow-offset :height] -)
(update-in [:strong :shadow-offset :height] -))
light-normal {1 {:shadow-color (when platform/ios?
(colors/alpha colors/neutral-100 0.04))
:shadow-offset {:width 0 :height 4}
:elevation 1
:shadow-opacity 1
:shadow-radius 16}
2 {:shadow-color (when platform/ios?
(colors/alpha colors/neutral-100 0.08))
:shadow-offset {:width 0 :height 4}
:elevation 6
:shadow-opacity 1
:shadow-radius 16}
3 {:shadow-color (when platform/ios?
(colors/alpha colors/neutral-100 0.12))
:shadow-offset {:width 0 :height 12}
:elevation 8
:shadow-opacity 1
:shadow-radius 16}
4 {:shadow-color (when platform/ios?
(colors/alpha colors/neutral-100 0.16))
:shadow-offset {:width 0 :height 16}
:shadow-opacity 1
:shadow-radius 16
:elevation 13}}
light-normal {1 (merge {:shadow-offset {:width 0 :height 4}}
(get-shadow-color colors/neutral-100 0.04)
(get-shadow-radius 16))
2 (merge {:shadow-offset {:width 0 :height 4}}
(get-shadow-color colors/neutral-100 0.08)
(get-shadow-radius 16))
3 (merge {:shadow-offset {:width 0 :height 12}}
(get-shadow-color colors/neutral-100 0.12)
(get-shadow-radius 16))
4 (merge {:shadow-offset {:width 0 :height 16}}
(get-shadow-color colors/neutral-100 0.16)
(get-shadow-radius 16))}
light-normal-inverted (-> light-normal
(update-in [:soft :shadow-offset :height] -)
(update-in [:medium :shadow-offset :height] -)
+2
View File
@@ -14,3 +14,5 @@
(def low-device? (and android? (< version 29)))
(def is-below-android-13? (and android? (< version 33)))
(def is-shadow-color-supported? (and android? (> version 27)))
+2 -23
View File
@@ -9,7 +9,6 @@
[status-im.common.lightbox.animations :as anim]
[status-im.common.lightbox.constants :as constants]
[status-im.common.lightbox.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.url :as url]))
@@ -40,25 +39,6 @@
(anim/animate top-view-width screen-width)
(anim/animate top-view-bg colors/neutral-100-opa-0)))))
(defn drawer
[images index]
(let [{:keys [image]} (nth images index)
uri (url/replace-port image (rf/sub [:mediaserver/port]))]
[quo/action-drawer
[[{:icon :i/save
:accessibility-label :save-image
:label (i18n/label :t/save-image-library)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:lightbox/save-image-to-gallery
uri
#(rf/dispatch [:toasts/upsert
{:id :random-id
:type :positive
:container-style {:bottom (when platform/android? 20)}
:text (i18n/label :t/photo-saved)}])]))}]]]))
(defn share-image
[images index]
(let [{:keys [image]} (nth images index)
@@ -66,7 +46,7 @@
(rf/dispatch [:lightbox/share-image uri])))
(defn top-view
[images insets index animations derived landscape? screen-width]
[images insets index animations derived landscape? screen-width on-options-press]
(let [{:keys [description header]} (nth images @index)
bg-color (if landscape?
colors/neutral-100-opa-70
@@ -113,7 +93,6 @@
[rn/touchable-opacity
{:active-opacity 1
:accessibility-label :image-options
:on-press #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [drawer images @index])}])
:on-press #(on-options-press images @index)
:style style/close-container}
[quo/icon :options {:size 20 :color colors/white}]]]]))
+7 -4
View File
@@ -44,7 +44,8 @@
[rn/view {:style {:width constants/separator-width}}]])
(defn lightbox-content
[{:keys [props state animations derived images index handle-items-changed bottom-text-component]}]
[{:keys [props state animations derived images index handle-items-changed bottom-text-component
on-options-press]}]
(let [{:keys [data transparent? scroll-index
set-full-height?]} state
insets (safe-area/get-insets)
@@ -69,7 +70,7 @@
{:height screen-height})}
(when-not @transparent?
[:f> top-view/top-view images insets scroll-index animations derived landscape?
screen-width])
screen-width on-options-press])
[gesture/gesture-detector
{:gesture (utils/drag-gesture animations (and landscape? platform/ios?) set-full-height?)}
[reanimated/view
@@ -136,7 +137,8 @@
(defn- f-lightbox
[]
(let [{:keys [images index bottom-text-component]} (rf/sub [:get-screen-params])
(let [{:keys [images index bottom-text-component on-options-press]}
(rf/sub [:get-screen-params])
props
(utils/init-props)
state
@@ -160,7 +162,8 @@
:images images
:index index
:handle-items-changed handle-items-changed
:bottom-text-component bottom-text-component}]))))
:bottom-text-component bottom-text-component
:on-options-press on-options-press}]))))
(defn lightbox
[]
@@ -1,4 +1,4 @@
(ns status-im.contexts.onboarding.select-photo.method-menu.view
(ns status-im.common.profile-picture-picker.view
(:require
["react-native-image-crop-picker" :default image-picker]
[quo.core :as quo]
@@ -59,7 +59,7 @@
crop-opts))
(defn view
[update-profile-pic-callback]
[{:keys [update-profile-pic-callback has-picture?]}]
[quo/action-drawer
[[{:icon :i/camera
:accessibility-label :take-photo-button
@@ -86,5 +86,15 @@
:on-allowed (fn [] (pick-pic update-profile-pic-callback))
:on-denied (fn []
(log/info
"user has denied permissions to select picture"))}))}]]])
"user has denied permissions to select picture"))}))}
(when has-picture?
{:accessibility-label :remove-profile-picture
:add-divider? true
:danger? true
:blur? true
:icon :i/delete
:label (i18n/label :t/profile-pic-remove)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(update-profile-pic-callback nil))})]]])
+2 -1
View File
@@ -26,7 +26,8 @@
:discover (js/require "../resources/images/ui2/discover.png")
:invite-friends (js/require "../resources/images/ui2/invite-friends.png")
:transaction-progress (js/require "../resources/images/ui2/transaction-progress.png")
:welcome-illustration (js/require "../resources/images/ui2/welcome_illustration.png")})
:welcome-illustration (js/require "../resources/images/ui2/welcome_illustration.png")
:notifications (js/require "../resources/images/ui2/notifications.png")})
(def ui-themed
{:angry-man
+12 -5
View File
@@ -11,11 +11,18 @@
(goog-define INFURA_TOKEN "")
(goog-define POKT_TOKEN "3ef2018191814b7e1009b8d9")
(goog-define ALCHEMY_ARBITRUM_GOERLI_TOKEN "")
(goog-define ALCHEMY_OPTIMISM_GOERLI_TOKEN "")
(goog-define ALCHEMY_OPTIMISM_MAINNET_TOKEN "")
(goog-define ALCHEMY_ARBITRUM_MAINNET_TOKEN "")
(goog-define OPENSEA_API_KEY "")
(goog-define RARIBLE_MAINNET_API_KEY "")
(goog-define RARIBLE_TESTNET_API_KEY "")
(goog-define ALCHEMY_ETHEREUM_MAINNET_TOKEN "")
(goog-define ALCHEMY_ETHEREUM_GOERLI_TOKEN "")
(goog-define ALCHEMY_ETHEREUM_SEPOLIA_TOKEN "")
(goog-define ALCHEMY_ARBITRUM_MAINNET_TOKEN "")
(goog-define ALCHEMY_ARBITRUM_GOERLI_TOKEN "")
(goog-define ALCHEMY_ARBITRUM_SEPOLIA_TOKEN "")
(goog-define ALCHEMY_OPTIMISM_MAINNET_TOKEN "")
(goog-define ALCHEMY_OPTIMISM_GOERLI_TOKEN "")
(goog-define ALCHEMY_OPTIMISM_SEPOLIA_TOKEN "")
(def mainnet-rpc-url (str "https://eth-archival.gateway.pokt.network/v1/lb/" POKT_TOKEN))
(def goerli-rpc-url (str "https://goerli-archival.gateway.pokt.network/v1/lb/" POKT_TOKEN))
@@ -160,7 +167,7 @@
(def default-kdf-iterations 3200)
(def community-accounts-selection-enabled? false)
(def community-accounts-selection-enabled? (enabled? (get-config :ACCOUNT_SELECTION_ENABLED "0")))
(def fetch-messages-enabled? (enabled? (get-config :FETCH_MESSAGES_ENABLED "1")))
(def wallet-feature-flags
+14 -6
View File
@@ -153,6 +153,11 @@
(def ^:const community-token-permission-can-view-and-post-channel 4)
(def ^:const community-token-permission-become-token-master 5)
(def ^:const community-token-permission-become-token-owner 6)
(def ^:const community-role-permissions
#{community-token-permission-become-admin
community-token-permission-become-member
community-token-permission-become-token-master
community-token-permission-become-token-owner})
;; Community rules for joining
(def ^:const community-rule-ens-only "ens-only")
@@ -405,12 +410,15 @@
(def ^:const ens-action-type-set-pub-key 2)
;; wallet
(def ^:const mainnet-chain-id 1)
(def ^:const optimism-chain-id 10)
(def ^:const optimism-test-chain-id 420)
(def ^:const arbitrum-chain-id 42161)
(def ^:const arbitrum-test-chain-id 421613)
(def ^:const goerli-chain-id 5)
(def ^:const ethereum-mainnet-chain-id 1)
(def ^:const ethereum-goerli-chain-id 5)
(def ^:const ethereum-sepolia-chain-id 11155111)
(def ^:const arbitrum-mainnet-chain-id 42161)
(def ^:const arbitrum-goerli-chain-id 421613)
(def ^:const arbitrum-sepolia-chain-id 421614)
(def ^:const optimism-mainnet-chain-id 10)
(def ^:const optimism-goerli-chain-id 420)
(def ^:const optimism-sepolia-chain-id 11155420)
(def ^:const mainnet-short-name "eth")
(def ^:const optimism-short-name "opt")
@@ -66,7 +66,12 @@
album-messages))
:bottom-text-component
[lightbox/bottom-text-for-lightbox
first-image]}])}
first-image]
:on-options-press (fn [images index]
(rf/dispatch [:show-bottom-sheet
{:content (fn [] [lightbox/drawer
images
index])}]))}])}
[fast-image/fast-image
{:style (style/image dimensions index portrait? images-count)
:source {:uri (url/replace-port (:image (:content item)) media-server-port)}
@@ -57,7 +57,13 @@
:index 0
:insets insets
:bottom-text-component
[lightbox/bottom-text-for-lightbox message]}])}
[lightbox/bottom-text-for-lightbox message]
:on-options-press (fn [images index]
(rf/dispatch [:show-bottom-sheet
{:content (fn []
[lightbox/drawer
images
index])}]))}])}
[fast-image/fast-image
{:source {:uri image-local-url}
:style (merge dimensions {:border-radius 12})
@@ -1,10 +1,15 @@
(ns status-im.contexts.chat.messenger.messages.content.lightbox.view
(:require
[oops.core :as oops]
[quo.core :as quo]
[react-native.platform :as platform]
[reagent.core :as reagent]
[status-im.common.lightbox.constants :as constants]
[status-im.contexts.chat.messenger.messages.content.lightbox.style :as style]
[status-im.contexts.chat.messenger.messages.content.text.view :as message-view]))
[status-im.contexts.chat.messenger.messages.content.text.view :as message-view]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.url :as url]))
(defn bottom-text-for-lightbox
[_]
@@ -16,3 +21,22 @@
:chat-id chat-id
:style-override (style/bottom-text expandable-text?)
:on-layout #(reset! text-height (oops/oget % "nativeEvent.layout.height"))}]))))
(defn drawer
[images index]
(let [{:keys [image]} (nth images index)
uri (url/replace-port image (rf/sub [:mediaserver/port]))]
[quo/action-drawer
[[{:icon :i/save
:accessibility-label :save-image
:label (i18n/label :t/save-image-library)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:lightbox/save-image-to-gallery
uri
#(rf/dispatch [:toasts/upsert
{:id :random-id
:type :positive
:container-style {:bottom (when platform/android? 20)}
:text (i18n/label :t/photo-saved)}])]))}]]]))
@@ -35,6 +35,7 @@
[quo/drawer-top
{:type :context-tag
:title (i18n/label :t/addresses-for-permissions)
:context-tag-type :community
:community-name name
:button-icon :i/info
:on-button-press not-implemented/alert
@@ -26,6 +26,7 @@
[:<>
[quo/drawer-top
{:type :context-tag
:context-tag-type :community
:title (i18n/label :t/airdrop-addresses)
:community-name name
:button-icon :i/info
@@ -23,10 +23,8 @@
(defn- view-internal
[{:keys [theme]}]
(fn []
(let [{:keys [name
id
images]} (rf/sub [:get-screen-params])
{:keys [color]} (rf/sub [:communities/community id])]
(let [{:keys [id]} (rf/sub [:get-screen-params])
{:keys [color name images]} (rf/sub [:communities/community id])]
[rn/safe-area-view {:flex 1}
[gesture/scroll-view {:style style/container}
[rn/view style/page-container
@@ -7,7 +7,6 @@
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.common.home.actions.view :as actions]
[status-im.common.password-authentication.view :as password-authentication]
[status-im.common.scroll-page.style :as scroll-page.style]
[status-im.common.scroll-page.view :as scroll-page]
[status-im.config :as config]
@@ -15,38 +14,20 @@
[status-im.contexts.communities.actions.chat.view :as chat-actions]
[status-im.contexts.communities.actions.community-options.view :as options]
[status-im.contexts.communities.overview.style :as style]
[status-im.contexts.communities.overview.utils :as utils]
[status-im.contexts.communities.utils :as communities.utils]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn preview-user-list
[user-list]
(when (seq user-list)
[rn/view style/preview-user
[quo/preview-list
{:type :user
:number (count user-list)
:size :size-24}
user-list]
[quo/text
{:accessibility-label :communities-screen-title
:style {:margin-left 8}
:size :label}
(utils/join-existing-users-string user-list)]]))
(defn add-category-height
(defn- add-category-height
[categories-heights category height]
(swap! categories-heights
(fn [heights]
(assoc heights category height))))
(swap! categories-heights assoc category height))
(defn collapse-category
(defn- collapse-category
[community-id category-id collapsed?]
(rf/dispatch [:communities/toggle-collapsed-category community-id category-id (not collapsed?)]))
(defn layout-y
(defn- layout-y
[event]
(oops/oget event "nativeEvent.layout.y"))
@@ -77,7 +58,7 @@
{:on-press on-press
:on-long-press #(rf/dispatch [:show-bottom-sheet channel-sheet-data])})]]))
(defn channel-list-component
(defn- channel-list-component
[{:keys [on-category-layout community-id community-color on-first-channel-height-changed]}
channels-list]
[rn/view
@@ -104,7 +85,7 @@
^{:key (:id chat)}
[channel-chat-item community-id community-color chat])])])])
(defn get-access-type
(defn- get-access-type
[access]
(condp = access
constants/community-no-membership-access :open
@@ -112,17 +93,9 @@
constants/community-on-request-access :request-access
:unknown-access))
(defn join-gated-community
[id]
(rf/dispatch [:password-authentication/show
{:content (fn [] [password-authentication/view])}
{:label (i18n/label :t/join-open-community)
:on-press #(rf/dispatch [:communities/request-to-join
{:community-id id :password %}])}]))
(defn info-button
(defn- info-button
[]
[rn/touchable-without-feedback
[rn/pressable
{:on-press
#(rf/dispatch
[:show-bottom-sheet
@@ -137,119 +110,116 @@
[rn/view
[quo/icon :i/info {:no-color true}]]])
(defn token-gates
[]
(fn [{:keys [id color]}]
(let [{:keys [can-request-access?
number-of-hold-tokens tokens
highest-permission-role]} (rf/sub [:community/token-gated-overview id])
highest-role-text
(i18n/label
(communities.utils/role->translation-key highest-permission-role :t/member))]
[rn/view {:style (style/token-gated-container)}
[rn/view
{:style {:padding-horizontal 12
:flex-direction :row
:align-items :center
:justify-content :space-between
:flex 1}}
[quo/text {:weight :medium}
(if can-request-access?
(i18n/label :t/you-eligible-to-join-as {:role highest-role-text})
(i18n/label :t/you-not-eligible-to-join))]
[info-button]]
(when (pos? number-of-hold-tokens)
[quo/text {:style {:padding-horizontal 12 :padding-bottom 18} :size :paragraph-2}
(if can-request-access?
(i18n/label :t/you-hold-number-of-hold-tokens-of-these
{:number-of-hold-tokens number-of-hold-tokens})
(i18n/label :t/you-must-hold))])
[quo/token-requirement-list
{:tokens tokens
:padding? true}]
[quo/button
{:on-press
(if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection {:community-id id}])
#(join-gated-community id))
:accessibility-label :join-community-button
:customization-color color
:container-style {:margin-horizontal 12 :margin-top 12 :margin-bottom 12}
:disabled? (not can-request-access?)
:icon-left (if can-request-access? :i/unlocked :i/locked)}
(i18n/label :t/join-open-community)]])))
(defn- token-requirements
[{:keys [id color]}]
(let [{:keys [can-request-access?
number-of-hold-tokens tokens
highest-permission-role]} (rf/sub [:community/token-gated-overview id])
highest-role-text
(i18n/label
(communities.utils/role->translation-key highest-permission-role :t/member))]
[rn/view {:style (style/token-gated-container)}
[rn/view
{:style {:padding-horizontal 12
:flex-direction :row
:align-items :center
:justify-content :space-between
:flex 1}}
[quo/text {:weight :medium}
(if can-request-access?
(i18n/label :t/you-eligible-to-join-as {:role highest-role-text})
(i18n/label :t/you-not-eligible-to-join))]
[info-button]]
(when (pos? number-of-hold-tokens)
[quo/text {:style {:padding-horizontal 12 :padding-bottom 18} :size :paragraph-2}
(if can-request-access?
(i18n/label :t/you-hold-number-of-hold-tokens-of-these
{:number-of-hold-tokens number-of-hold-tokens})
(i18n/label :t/you-must-hold))])
[quo/token-requirement-list
{:tokens tokens
:padding? true}]
[quo/button
{:on-press (if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection
{:community-id id}])
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
:accessibility-label :join-community-button
:customization-color color
:container-style {:margin-horizontal 12 :margin-top 12 :margin-bottom 12}
:disabled? (not can-request-access?)
:icon-left (if can-request-access? :i/unlocked :i/locked)}
(i18n/label :t/join-open-community)]]))
(defn join-community
[{:keys [joined color permissions token-permissions membership-permissions? id] :as community}
pending?]
(let [access-type (get-access-type (:access permissions))
(defn- join-community
[{:keys [id color joined permissions role-permissions? can-join?] :as community}]
(let [pending? (rf/sub [:communities/my-pending-request-to-join id])
access-type (get-access-type (:access permissions))
unknown-access? (= access-type :unknown-access)
invite-only? (= access-type :invite-only)]
[:<>
(when-not (or joined pending? invite-only? unknown-access?)
(if membership-permissions?
[token-gates community]
(if role-permissions?
[token-requirements community]
[quo/button
{:on-press
(if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection {:community-id id}])
#(rf/dispatch [:open-modal :community-requests-to-join community]))
{:on-press (if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection
{:community-id id}])
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
:accessibility-label :show-request-to-join-screen-button
:customization-color color
:icon-left :i/communities}
:icon-left :i/communities}
(i18n/label :t/request-to-join)]))
(when (not (or joined pending? token-permissions))
(when (not (or pending? role-permissions? can-join?))
[quo/text
{:size :paragraph-2
:weight :regular
:style style/review-notice}
(i18n/label :t/community-admins-will-review-your-request)])]))
(defn status-tag
[pending? joined]
(when (or pending? joined)
[rn/view {:style {:position :absolute :top 12 :right 12}}
[quo/status-tag
{:status {:type (if joined :positive :pending)}
:label (if joined
(i18n/label :t/joined)
(i18n/label :t/pending))}]]))
(defn- status-tag
[community-id joined]
(let [pending? (rf/sub [:communities/my-pending-request-to-join community-id])]
(when (or pending? joined)
[rn/view {:style {:position :absolute :top 12 :right 12}}
[quo/status-tag
{:status {:type (if joined :positive :pending)}
:label (if joined
(i18n/label :t/joined)
(i18n/label :t/pending))}]])))
(defn add-handlers
(defn- add-handlers
[community-id
joined-or-spectated
{:keys [id locked?]
:or {locked? false}
:as chat}]
(merge
chat
(when (and (not locked?) id)
{:on-press (when joined-or-spectated
(fn []
(rf/dispatch [:dismiss-keyboard])
(debounce/dispatch-and-chill
[:communities/navigate-to-community-chat (str community-id id)]
1000)))
:on-long-press #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[chat-actions/actions chat false])}])
:community-id community-id})))
(cond-> chat
(and (not locked?) id)
(assoc :on-press (when joined-or-spectated
(fn []
(rf/dispatch [:dismiss-keyboard])
(debounce/dispatch-and-chill
[:communities/navigate-to-community-chat (str community-id id)]
1000)))
:on-long-press #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[chat-actions/actions chat false])}])
:community-id community-id)))
(defn add-handlers-to-chats
(defn- add-handlers-to-chats
[community-id joined-or-spectated chats]
(mapv (partial add-handlers community-id joined-or-spectated) chats))
(defn add-handlers-to-categorized-chats
(defn- add-handlers-to-categorized-chats
[community-id categorized-chats joined-or-spectated]
(let [add-on-press (partial add-handlers-to-chats community-id joined-or-spectated)]
(map (fn [[category v]]
[category (update v :chats add-on-press)])
categorized-chats)))
(defn community-header
(defn- community-header
[title logo description]
[quo/text-combinations
{:container-style
@@ -266,11 +236,11 @@
:title-accessibility-label :community-title
:description-accessibility-label :community-description}])
(defn community-content
[community]
(rf/dispatch [:communities/check-all-community-channels-permissions (:id community)])
(fn [{:keys [name description joined spectated images tags color id token-permissions] :as community}
pending?
(defn- community-content
[{:keys [id]}]
(rf/dispatch [:communities/check-all-community-channels-permissions id])
(fn [{:keys [name description joined spectated images tags color id membership-permissions?]
:as community}
{:keys [on-category-layout
collapsed?
on-first-channel-height-changed]}]
@@ -279,7 +249,7 @@
[:<>
[rn/view {:style style/community-content-container}
(when-not collapsed?
[status-tag pending? joined])
[status-tag id joined])
[community-header name (when collapsed? (get-in images [:thumbnail :uri]))
(when-not collapsed? description)]
(when (and (seq tags) (not collapsed?))
@@ -287,9 +257,8 @@
{:tags tags
:last-item-style style/last-community-tag
:container-style style/community-tag-container}])
[join-community community pending?]]
(when (or (and (seq token-permissions) joined)
(empty? token-permissions))
[join-community community]]
(when (or joined (not membership-permissions?))
[channel-list-component
{:on-category-layout on-category-layout
:community-id id
@@ -297,7 +266,7 @@
:on-first-channel-height-changed on-first-channel-height-changed}
(add-handlers-to-categorized-chats id chats-by-category joined-or-spectated)])])))
(defn sticky-category-header
(defn- sticky-category-header
[_]
(fn [{:keys [enabled label]}]
(when enabled
@@ -310,7 +279,7 @@
{:chevron :left}
label]])))
(defn page-nav-right-section-buttons
(defn- page-nav-right-section-buttons
[id]
[{:icon-name :i/options
:accessibility-label :community-options-for-community
@@ -318,7 +287,7 @@
[:show-bottom-sheet
{:content (fn [] [options/community-options-bottom-sheet id])}])}])
(defn pick-first-category-by-height
(defn- pick-first-category-by-height
[scroll-height first-channel-height categories-heights]
(->> categories-heights
(sort-by (comp - second))
@@ -326,7 +295,7 @@
(and (>= scroll-height (+ height first-channel-height))
category)))))
(defn community-scroll-page
(defn- community-scroll-page
[{:keys [joined]}]
(let [scroll-height (reagent/atom 0)
categories-heights (reagent/atom {})
@@ -336,7 +305,7 @@
;; from not collapsed to collapsed if the
;; user is on this page
initial-joined? joined]
(fn [{:keys [id name images] :as community} pending?]
(fn [{:keys [id name images] :as community}]
(let [cover {:uri (get-in images [:banner :uri])}
logo {:uri (get-in images [:thumbnail :uri])}
collapsed? (and initial-joined? (:joined community))
@@ -363,7 +332,6 @@
@categories-heights)}]}
[community-content
community
pending?
{:on-category-layout (partial add-category-height categories-heights)
:collapsed? collapsed?
:on-first-channel-height-changed
@@ -373,17 +341,16 @@
(swap! categories-heights select-keys categories)
(reset! first-channel-height height))}]]))))
(defn community-card-page-view
(defn- community-card-page-view
[id]
(let [{:keys [id joined]
:as community} (rf/sub [:communities/community id])
pending? (rf/sub [:communities/my-pending-request-to-join id])]
:as community} (rf/sub [:communities/community id])]
(when community
(when joined
(rf/dispatch [:activity-center.notifications/dismiss-community-overview id]))
[community-scroll-page community pending?])))
[community-scroll-page community])))
(defn overview
(defn view
[id]
(let [id (or id (rf/sub [:get-screen-params :community-overview]))
customization-color (rf/sub [:profile/customization-color])]
@@ -10,10 +10,10 @@
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.common.profile-picture-picker.view :as profile-picture-picker]
[status-im.common.validation.profile :as profile-validator]
[status-im.constants :as c]
[status-im.contexts.onboarding.create-profile.style :as style]
[status-im.contexts.onboarding.select-photo.method-menu.view :as method-menu]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.responsiveness :as responsiveness]))
@@ -144,7 +144,9 @@
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[method-menu/view on-change-profile-pic])
[profile-picture-picker/view
{:update-profile-pic-callback on-change-profile-pic
:has-picture? false}])
:shell? true}]))
:image-picker-props {:profile-picture @profile-pic
:full-name (if (seq @full-name)
+8 -1
View File
@@ -13,10 +13,17 @@
:openseaAPIKey config/opensea-api-key
:poktToken config/POKT_TOKEN
:infuraToken config/INFURA_TOKEN
:raribleMainnetAPIKey config/RARIBLE_MAINNET_API_KEY
:raribleTestnetAPIKey config/RARIBLE_TESTNET_API_KEY
:alchemyEthereumMainnetToken config/ALCHEMY_ETHEREUM_MAINNET_TOKEN
:alchemyEthereumGoerliToken config/ALCHEMY_ETHEREUM_GOERLI_TOKEN
:alchemyEthereumSepoliaToken config/ALCHEMY_ETHEREUM_SEPOLIA_TOKEN
:alchemyOptimismMainnetToken config/ALCHEMY_OPTIMISM_MAINNET_TOKEN
:alchemyOptimismGoerliToken config/ALCHEMY_OPTIMISM_GOERLI_TOKEN
:alchemyOptimismSepoliaToken config/ALCHEMY_OPTIMISM_SEPOLIA_TOKEN
:alchemyArbitrumMainnetToken config/ALCHEMY_ARBITRUM_MAINNET_TOKEN
:alchemyArbitrumGoerliToken config/ALCHEMY_ARBITRUM_GOERLI_TOKEN})
:alchemyArbitrumGoerliToken config/ALCHEMY_ARBITRUM_GOERLI_TOKEN
:alchemyArbitrumSepoliaToken config/ALCHEMY_ARBITRUM_SEPOLIA_TOKEN})
(defn create
[]
@@ -0,0 +1,52 @@
(ns status-im.contexts.profile.edit.header.events
(:require [clojure.string :as string]
[status-im.common.profile-picture-picker.view :as profile-picture-picker]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(rf/reg-event-fx :profile/update-local-picture
(fn [{:keys [db]} [images]]
{:db (if images
(assoc-in db [:profile/profile :images] images)
(update db :profile/profile dissoc :images))}))
(rf/reg-event-fx :profile/edit-profile-picture-success
(fn [_ [images]]
{:fx [[:dispatch [:profile/update-local-picture (reverse images)]]
[:dispatch
[:toasts/upsert
{:type :positive
:theme :dark
:text (i18n/label :t/profile-picture-added)}]]]}))
(defn edit-profile-picture
[{:keys [db]} [picture crop-width crop-height]]
(let [key-uid (get-in db [:profile/profile :key-uid])
crop-width (or crop-width profile-picture-picker/crop-size)
crop-height (or crop-height profile-picture-picker/crop-size)
path (string/replace-first picture #"file://" "")]
{:fx [[:json-rpc/call
[{:method "multiaccounts_storeIdentityImage"
:params [key-uid path 0 0 crop-width crop-height]
:on-success [:profile/edit-profile-picture-success]}]]]}))
(rf/reg-event-fx :profile/edit-picture edit-profile-picture)
(rf/reg-event-fx :profile/delete-profile-picture-success
(fn [_]
{:fx [[:dispatch [:profile/update-local-picture nil]]
[:dispatch
[:toasts/upsert
{:type :positive
:theme :dark
:text (i18n/label :t/profile-picture-removed)}]]]}))
(defn delete-profile-picture
[{:keys [db]}]
(let [key-uid (get-in db [:profile/profile :key-uid])]
{:fx [[:json-rpc/call
[{:method "multiaccounts_deleteIdentityImage"
:params [key-uid]
:on-success [:profile/delete-profile-picture-success]}]]]}))
(rf/reg-event-fx :profile/delete-picture delete-profile-picture)
@@ -0,0 +1,27 @@
(ns status-im.contexts.profile.edit.header.events-test
(:require [cljs.test :refer [deftest is]]
matcher-combinators.test
[status-im.common.profile-picture-picker.view :as profile-picture-picker]
[status-im.contexts.profile.edit.header.events :as sut]))
(deftest edit-picture-test
(let [picture "new-picture"
key-uid "key-uid"
cofx {:db {:profile/profile {:key-uid key-uid}}}
expected {:fx [[:json-rpc/call
[{:method "multiaccounts_storeIdentityImage"
:params [key-uid picture 0 0 profile-picture-picker/crop-size
profile-picture-picker/crop-size]
:on-success [:profile/edit-profile-picture-success]}]]]}]
(is (match? expected
(sut/edit-profile-picture cofx [picture])))))
(deftest delete-picture-test
(let [key-uid "key-uid"
cofx {:db {:profile/profile {:key-uid key-uid}}}
expected {:fx [[:json-rpc/call
[{:method "multiaccounts_deleteIdentityImage"
:params [key-uid]
:on-success [:profile/delete-profile-picture-success]}]]]}]
(is (match? expected
(sut/delete-profile-picture cofx)))))
@@ -1,7 +1,7 @@
(ns status-im.contexts.profile.edit.header.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.not-implemented :as not-implemented]
[status-im.common.profile-picture-picker.view :as profile-picture-picker]
[status-im.contexts.profile.edit.style :as style]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
@@ -9,22 +9,37 @@
(defn view
[]
(let [profile (rf/sub [:profile/profile-with-image])
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)]
(let [profile (rf/sub [:profile/profile-with-image])
customization-color (rf/sub [:profile/customization-color])
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)
has-picture? (rf/sub [:profile/has-picture])
on-change-profile-pic (fn [picture]
(if picture
(rf/dispatch [:profile/edit-picture picture])
(rf/dispatch [:profile/delete-picture])))]
[rn/view
{:key :edit-profile
:style style/screen-container}
[quo/text-combinations {:title (i18n/label :t/edit-profile)}]
[rn/view style/avatar-wrapper
[quo/user-avatar
{:full-name full-name
:profile-picture profile-picture
:status-indicator? false
:ring? true
:size :big}]
{:full-name full-name
:profile-picture profile-picture
:customization-color customization-color
:status-indicator? false
:ring? true
:size :big}]
[quo/button
{:on-press not-implemented/alert
{:on-press (fn []
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[profile-picture-picker/view
{:update-profile-pic-callback on-change-profile-pic
:has-picture? has-picture?}])
:theme :dark
:shell? true}]))
:container-style style/camera-button
:icon-only? true
:type :grey
@@ -2,17 +2,21 @@
(:require [utils.i18n :as i18n]
[utils.re-frame :as rf]))
(rf/reg-event-fx :profile/edit-profile-name-success
(fn [_]
{:fx [[:dispatch [:navigate-back]]
[:dispatch
[:toasts/upsert
{:type :positive
:theme :dark
:text (i18n/label :t/name-updated)}]]]}))
(defn edit-profile-name
[{:keys [db]} [name]]
{:db (assoc-in db [:profile/profile :display-name] name)
:fx [[:json-rpc/call
[{:method "wakuext_setDisplayName"
:params [name]
:on-success (fn []
(rf/dispatch [:navigate-back])
(rf/dispatch [:toasts/upsert
{:type :positive
:theme :dark
:text (i18n/label :t/name-updated)}]))}]]]})
:on-success [:profile/edit-profile-name-success]}]]]})
(rf/reg-event-fx :profile/edit-name edit-profile-name)
@@ -10,6 +10,6 @@
:fx [[:json-rpc/call
[{:method "wakuext_setDisplayName"
:params [name]
:on-success fn?}]]]}]
:on-success [:profile/edit-profile-name-success]}]]]}]
(is (match? expected
(sut/edit-profile-name cofx [new-name])))))
@@ -3,6 +3,7 @@
[legacy.status-im.data-store.settings :as data-store.settings]
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.contexts.profile.edit.header.events]
[status-im.contexts.profile.edit.name.events]
[status-im.contexts.profile.login.events :as profile.login]
[status-im.contexts.profile.rpc :as profile.rpc]
@@ -70,6 +70,15 @@
{:on-success on-success}])
:on-cancel nil}]]})))
(rf/reg-event-fx :profile.settings/toggle-sepolia-test-network
(fn [{:keys [db]}]
(let [value (get-in db [:profile/profile :is-sepolia-enabled?])
on-success #(rf/dispatch [:wallet/initialize])]
{:fx [[:dispatch
[:profile.settings/profile-update :is-sepolia-enabled?
(not value)
{:on-success on-success}]]]})))
(rf/defn change-preview-privacy-flag
{:events [:profile.settings/change-preview-privacy]}
[{:keys [db] :as cofx} private?]
@@ -80,7 +89,6 @@
(boolean private?)
{})))
(rf/defn change-profile-pictures-show-to
{:events [:profile.settings/change-profile-pictures-show-to]}
[cofx id]
@@ -15,7 +15,7 @@
(def screens-map
{shell.constants/chat-screen chat/chat
shell.constants/community-screen communities.overview/overview
shell.constants/community-screen communities.overview/view
shell.constants/discover-communities-screen communities.discover/view})
(defn f-screen
@@ -3,13 +3,15 @@
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.svg :as svg]
[reagent.core :as reagent]
[status-im.common.scroll-page.view :as scroll-page]
[status-im.contexts.wallet.collectible.style :as style]
[status-im.contexts.wallet.collectible.tabs.view :as tabs]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
[utils.re-frame :as rf]
[utils.url :as url]))
(defn header
[collectible-name collection-name collection-image-url]
@@ -71,6 +73,32 @@
:accessibility-label :share-details
:label (i18n/label :t/share-details)}]]])
(defn options-drawer
[images index]
(let [{:keys [image]} (nth images index)
uri (url/replace-port image (rf/sub [:mediaserver/port]))]
[quo/action-drawer
[[{:icon :i/link
:accessibility-label :view-on-etherscan
:label (i18n/label :t/view-on-eth)}]
[{:icon :i/save
:accessibility-label :save-image
:label (i18n/label :t/save-image-to-photos)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:lightbox/save-image-to-gallery
uri
#(rf/dispatch [:toasts/upsert
{:id :random-id
:type :positive
:container-style {:bottom (when platform/android? 20)}
:text (i18n/label :t/photo-saved)}])]))}]
[{:icon :i/share
:accessibility-label :share-collectible
:label (i18n/label :t/share-collectible)
:right-icon :i/external}]]]))
(defn view-internal
[{:keys [theme] :as _props}]
(let [selected-tab (reagent/atom :overview)
@@ -107,20 +135,27 @@
:on-press (fn []
(if svg?
(js/alert "Can't visualize SVG images in lightbox")
(rf/dispatch [:lightbox/navigate-to-lightbox
token-id
{:images [{:image preview-uri
:image-width 300 ; collectibles don't have
; width/height but we need
; to pass something
:image-height 300 ; without it animation
; doesn't work smoothly and
; :border-radius not
; applied
:id token-id
:header collectible-name
:description collection-name}]
:index 0}])))}
(rf/dispatch
[:lightbox/navigate-to-lightbox
token-id
{:images [{:image preview-uri
:image-width 300 ; collectibles don't have
; width/height but we need
; to pass something
:image-height 300 ; without it animation
; doesn't work smoothly
; and :border-radius not
; applied
:id token-id
:header collectible-name
:description collection-name}]
:index 0
:on-options-press (fn [images index]
(rf/dispatch [:show-bottom-sheet
{:content (fn []
[options-drawer
images
index])}]))}])))}
(if svg?
[rn/view
{:style (assoc style/preview :overflow :hidden)
+19 -14
View File
@@ -74,11 +74,13 @@
(defn get-standard-crypto-format
"For full details: https://github.com/status-im/status-mobile/issues/18225"
[{:keys [market-values-per-currency]} token-units]
(let [price (get-in market-values-per-currency [:usd :price])
one-cent-value (if (pos? price) (/ 0.01 price) 0)
decimals-count (calc-max-crypto-decimals one-cent-value)]
(if (money/equal-to token-units 0)
"0"
(if (or (nil? token-units)
(nil? market-values-per-currency)
(money/equal-to token-units 0))
"0"
(let [price (-> market-values-per-currency :usd :price)
one-cent-value (if (pos? price) (/ 0.01 price) 0)
decimals-count (calc-max-crypto-decimals one-cent-value)]
(if (< token-units one-cent-value)
(str "<" (remove-trailing-zeroes (.toFixed one-cent-value decimals-count)))
(remove-trailing-zeroes (.toFixed token-units decimals-count))))))
@@ -188,17 +190,20 @@
address))
(def id->network
{constants/mainnet-chain-id :ethereum
constants/goerli-chain-id :ethereum
constants/optimism-chain-id :optimism
constants/optimism-test-chain-id :optimism
constants/arbitrum-chain-id :arbitrum
constants/arbitrum-test-chain-id :arbitrum})
{constants/ethereum-mainnet-chain-id :ethereum
constants/ethereum-goerli-chain-id :ethereum
constants/ethereum-sepolia-chain-id :ethereum
constants/optimism-mainnet-chain-id :optimism
constants/optimism-goerli-chain-id :optimism
constants/optimism-sepolia-chain-id :optimism
constants/arbitrum-mainnet-chain-id :arbitrum
constants/arbitrum-goerli-chain-id :arbitrum
constants/arbitrum-sepolia-chain-id :arbitrum})
(def short-name->id
{:eth constants/mainnet-chain-id
:opt constants/optimism-chain-id
:arb1 constants/arbitrum-chain-id})
{:eth constants/ethereum-mainnet-chain-id
:opt constants/optimism-mainnet-chain-id
:arb1 constants/arbitrum-mainnet-chain-id})
(defn get-standard-fiat-format
[crypto-value currency-symbol fiat-value]
+118 -46
View File
@@ -3,10 +3,12 @@
[camel-snake-kebab.core :as csk]
[camel-snake-kebab.extras :as cske]
[clojure.string :as string]
[native-module.core :as native-module]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.send.utils :as send-utils]
[taoensso.timbre :as log]
[utils.address :as address]
[utils.money :as money]
[utils.number]
[utils.re-frame :as rf]))
@@ -56,11 +58,14 @@
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui :send] dissoc :recipient :to-address)}))
(rf/reg-event-fx :wallet/select-send-address
(rf/reg-event-fx
:wallet/select-send-address
(fn [{:keys [db]} [{:keys [address token recipient stack-id]}]]
(let [[prefix to-address] (utils/split-prefix-and-address address)
prefix-seq (string/split prefix #":")
selected-networks (mapv #(utils/short-name->id (keyword %)) prefix-seq)]
selected-networks (->> prefix-seq
(remove string/blank?)
(mapv #(utils/short-name->id (keyword %))))]
{:db (-> db
(assoc-in [:wallet :ui :send :recipient] (or recipient address))
(assoc-in [:wallet :ui :send :to-address] to-address)
@@ -71,18 +76,20 @@
[:wallet-send-input-amount stack-id]
[:wallet-select-asset stack-id])]]})))
(rf/reg-event-fx :wallet/update-receiver-networks
(rf/reg-event-fx
:wallet/update-receiver-networks
(fn [{:keys [db]} [selected-networks]]
{:db (assoc-in db [:wallet :ui :send :selected-networks] selected-networks)}))
(rf/reg-event-fx :wallet/send-select-token
(fn [{:keys [db]} [{:keys [token stack-id]}]]
{:db (assoc-in db [:wallet :ui :send :token] token)
:fx [[:dispatch-later
{:ms 1
:dispatch [:navigate-to-within-stack [:wallet-send-input-amount stack-id]]}]]}))
{:db (-> db
(update-in [:wallet :ui :send] dissoc :collectible)
(assoc-in [:wallet :ui :send :token] token))
:fx [[:navigate-to-within-stack [:wallet-send-input-amount stack-id]]]}))
(rf/reg-event-fx :wallet/send-select-token-drawer
(rf/reg-event-fx
:wallet/send-select-token-drawer
(fn [{:keys [db]} [{:keys [token]}]]
{:db (assoc-in db [:wallet :ui :send :token] token)}))
@@ -90,6 +97,15 @@
(fn [{:keys [db]}]
{:db (assoc-in db [:wallet :ui :send :token] nil)}))
(rf/reg-event-fx :wallet/send-select-collectible
(fn [{:keys [db]} [{:keys [collectible stack-id]}]]
{:db (-> db
(update-in [:wallet :ui :send] dissoc :token)
(assoc-in [:wallet :ui :send :collectible] collectible)
(assoc-in [:wallet :ui :send :amount] 1))
:fx [[:dispatch [:wallet/get-suggested-routes 1]]
[:navigate-to-within-stack [:wallet-transaction-confirmation stack-id]]]}))
(rf/reg-event-fx :wallet/send-select-amount
(fn [{:keys [db]} [{:keys [amount stack-id]}]]
{:db (assoc-in db [:wallet :ui :send :amount] amount)
@@ -99,19 +115,25 @@
(fn [{:keys [db now]} [amount]]
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
token (get-in db [:wallet :ui :send :token])
account-address (get-in db [:wallet :ui :send :send-account-address])
selected-networks (get-in db [:wallet :ui :send :selected-networks])
to-address (or account-address (get-in db [:wallet :ui :send :to-address]))
token-decimal (:decimals token)
token-id (:symbol token)
network-preferences selected-networks
collectible (get-in db [:wallet :ui :send :collectible])
to-address (get-in db [:wallet :ui :send :to-address])
token-decimal (when token (:decimals token))
token-id (if token
(:symbol token)
(str (get-in collectible [:id :contract-id :address])
":"
(get-in collectible [:id :token-id])))
network-preferences (if token [] [(get-in collectible [:id :contract-id :chain-id])])
gas-rates constants/gas-rate-medium
amount-in (send-utils/amount-in-hex amount token-decimal)
amount-in (send-utils/amount-in-hex amount (if token token-decimal 0))
from-address wallet-address
disabled-from-chain-ids []
disabled-to-chain-ids []
from-locked-amount {}
request-params [constants/send-type-transfer
transaction-type (if token
constants/send-type-transfer
constants/send-type-erc-721-transfer)
request-params [transaction-type
from-address
to-address
amount-in
@@ -155,21 +177,46 @@
{:fx [[:dispatch [:dismiss-modal :wallet-transaction-progress]]]}))
(defn- transaction-bridge
[{:keys [from-address to-address route]}]
(let [{:keys [from bridge-name amount-out gas-amount gas-fees]} route
{:keys [gas-price max-fee-per-gas-medium max-priority-fee-per-gas]} gas-fees]
[{:BridgeName bridge-name
:ChainID (:chain-id from)
:TransferTx {:From from-address
:To to-address
:Gas (money/to-hex gas-amount)
:GasPrice (money/to-hex (money/->wei :gwei gas-price))
:Value amount-out
:Nonce nil
:MaxFeePerGas (money/to-hex (money/->wei :gwei max-fee-per-gas-medium))
:MaxPriorityFeePerGas (money/to-hex (money/->wei :gwei max-priority-fee-per-gas))
:Input ""
:Data "0x"}}]))
[{:keys [from-address from-chain-id to-address token-id token-address route data eth-transfer?]}]
(let [{:keys [bridge-name amount-out gas-amount
gas-fees]} route
eip-1559-enabled? (:eip-1559-enabled gas-fees)
{:keys [gas-price max-fee-per-gas-medium
max-priority-fee-per-gas]} gas-fees
transfer-tx (cond-> {:From from-address
:To (or token-address to-address)
:Gas (money/to-hex gas-amount)
:Value (when eth-transfer? amount-out)
:Nonce nil
:Input ""
:Data (or data "0x")}
eip-1559-enabled? (assoc :TxType "0x02"
:MaxFeePerGas
(money/to-hex
(money/->wei
:gwei
max-fee-per-gas-medium))
:MaxPriorityFeePerGas
(money/to-hex
(money/->wei
:gwei
max-priority-fee-per-gas)))
(not eip-1559-enabled?) (assoc :TxType "0x00"
:GasPrice (money/to-hex
(money/->wei
:gwei
gas-price))))]
[(cond-> {:BridgeName bridge-name
:ChainID from-chain-id}
(= bridge-name constants/bridge-name-erc-721-transfer)
(assoc :ERC721TransferTx
(assoc transfer-tx
:Recipient to-address
:TokenID token-id))
(= bridge-name constants/bridge-name-transfer)
(assoc :TransferTx transfer-tx))]))
(defn- multi-transaction-command
[{:keys [from-address to-address from-asset to-asset amount-out transfer-type]
@@ -183,25 +230,50 @@
(rf/reg-event-fx :wallet/send-transaction
(fn [{:keys [db]} [sha3-pwd]]
(let [route (get-in db [:wallet :ui :send :route])
from-address (get-in db [:wallet :current-viewing-account-address])
to-address (get-in db [:wallet :ui :send :to-address])
token (get-in db [:wallet :ui :send :token])
token-id (:symbol token)
request-params [(multi-transaction-command {:from-address from-address
:to-address to-address
:from-asset token-id
:to-asset token-id
:amount-out (:amount-out route)})
(transaction-bridge {:to-address to-address
:from-address from-address
:route route})
sha3-pwd]]
(let [route (get-in db [:wallet :ui :send :route])
from-address (get-in db [:wallet :current-viewing-account-address])
token (get-in db [:wallet :ui :send :token])
collectible (get-in db [:wallet :ui :send :collectible])
from-chain-id (get-in route [:from :chain-id])
token-id (if token
(:symbol token)
(get-in collectible [:id :token-id]))
erc20-transfer? (and token (not= token-id "ETH"))
eth-transfer? (and token (not erc20-transfer?))
token-address (cond collectible
(get-in collectible
[:id :contract-id :address])
erc20-transfer?
(get-in token [:balances-per-chain from-chain-id :address]))
to-address (get-in db [:wallet :ui :send :to-address])
data (when erc20-transfer?
(native-module/encode-transfer (address/normalized-hex to-address)
(:amount-out route)))
request-params [(multi-transaction-command
{:from-address from-address
:to-address to-address
:from-asset token-id
:to-asset token-id
:amount-out (if eth-transfer? (:amount-out route) "0x0")})
(transaction-bridge {:to-address to-address
:from-address from-address
:route route
:from-chain-id from-chain-id
:token-address token-address
:token-id (when collectible
(money/to-hex (js/parseInt token-id)))
:data data
:eth-transfer? eth-transfer?})
sha3-pwd]]
{:json-rpc/call [{:method "wallet_createMultiTransaction"
:params request-params
:on-success (fn [result]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/add-authorized-transaction result]))
(rf/dispatch [:wallet/add-authorized-transaction result])
(rf/dispatch [:wallet/clean-scanned-address])
(rf/dispatch [:wallet/clean-local-suggestions])
(rf/dispatch [:wallet/clean-send-address])
(rf/dispatch [:wallet/select-address-tab nil]))
:on-error (fn [error]
(log/error "failed to send transaction"
{:event :wallet/send-transaction
@@ -53,8 +53,9 @@
(h/test "Default render"
(h/setup-subs sub-mocks)
(h/render [input-amount/view
{:crypto-decimals 2
:limit-crypto 250}])
{:crypto-decimals 2
:limit-crypto 250
:initial-crypto-currency? false}])
(h/is-truthy (h/get-by-text "0"))
(h/is-truthy (h/get-by-text "ETH"))
(h/is-truthy (h/get-by-text "$0.00"))
@@ -65,9 +66,10 @@
(h/setup-subs sub-mocks)
(let [on-confirm (h/mock-fn)]
(h/render [input-amount/view
{:on-confirm on-confirm
:crypto-decimals 10
:limit-crypto 1000}])
{:on-confirm on-confirm
:crypto-decimals 10
:limit-crypto 1000
:initial-crypto-currency? false}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
@@ -88,9 +90,10 @@
(let [on-confirm (h/mock-fn)]
(h/render [input-amount/view
{:crypto-decimals 10
:limit-crypto 1000
:on-confirm on-confirm}])
{:crypto-decimals 10
:limit-crypto 1000
:on-confirm on-confirm
:initial-crypto-currency? false}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
@@ -30,28 +30,54 @@
(defn valid-input?
[current v]
(let [max-length 12
length-owerflow? (>= (count current) max-length)
length-overflow? (>= (count current) max-length)
extra-dot? (and (= v dot) (string/includes? current dot))
extra-leading-zero? (and (= current "0") (= "0" (str v)))
non-numeric? (re-find not-digits-or-dot-pattern (str v))]
(not (or non-numeric? extra-dot? extra-leading-zero? length-owerflow?))))
(not (or non-numeric? extra-dot? extra-leading-zero? length-overflow?))))
(defn- add-char-to-string
[s c idx]
(let [size (count s)]
(if (= size idx)
(str s c)
(str (subs s 0 idx)
c
(subs s idx size)))))
(defn- move-input-cursor
([input-selection-atom new-idx]
(move-input-cursor input-selection-atom new-idx new-idx))
([input-selection-atom new-start-idx new-end-idx]
(let [start-idx (if (< new-start-idx 0) 0 new-start-idx)
end-idx (if (< new-end-idx 0) 0 new-start-idx)]
(swap! input-selection-atom assoc :start start-idx :end end-idx))))
(defn- normalize-input
[current v]
(cond
(and (string/blank? current) (= v dot))
(str "0" v)
[current v input-selection-atom]
(let [{:keys [start end]} @input-selection-atom]
(if (= start end)
(cond
(and (string/blank? current) (= v dot))
(do
(move-input-cursor input-selection-atom 2)
(str "0" v))
(and (= current "0") (not= v dot))
(str v)
(and (= current "0") (not= v dot))
(do
(move-input-cursor input-selection-atom 1)
(str v))
:else
(str current v)))
:else
(do
(move-input-cursor input-selection-atom (inc start))
(add-char-to-string current v start)))
current)))
(defn- make-new-input
[current v]
[current v input-selection-atom]
(if (valid-input? current v)
(normalize-input current v)
(normalize-input current v input-selection-atom)
current))
(defn- reset-input-error
@@ -59,124 +85,154 @@
(reset! input-error
(> new-value prev-value)))
(defn delete-from-string
[s idx]
(let [size (count s)]
(str (subs s 0 (dec idx)) (subs s idx size))))
(defn- f-view-internal
;; crypto-decimals and limit-crypto args are needed for component tests only
[{:keys [crypto-decimals limit-crypto]}]
(let [bottom (safe-area/get-bottom)
{:keys [currency]} (rf/sub [:profile/profile])
token (rf/sub [:wallet/wallet-send-token])
loading-suggested-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
token-symbol (:symbol token)
limit-crypto (or limit-crypto
(utils/get-standard-crypto-format token (:total-balance token)))
conversion-rate (get-in token [:market-values-per-currency :usd :price])
limit-fiat (.toFixed (* (:total-balance token) conversion-rate) 2)
crypto-decimals (or crypto-decimals (utils/get-crypto-decimals-count token))
input-value (reagent/atom "")
input-error (reagent/atom false)
current-limit (reagent/atom {:amount limit-crypto
:currency token-symbol})
handle-swap (fn [crypto?]
(let [num-value (parse-double @input-value)]
(reset! current-limit (if crypto?
{:amount limit-crypto
:currency token-symbol}
{:amount limit-fiat
:currency currency}))
(reset-input-error num-value
(:amount @current-limit)
input-error)))
handle-keyboard-press (fn [v]
(let [current-value @input-value
new-value (make-new-input current-value v)
num-value (or (parse-double new-value) 0)
current-limit-amount (:amount @current-limit)]
(when (not loading-suggested-routes?)
(reset! input-value new-value)
(reset-input-error num-value current-limit-amount input-error)
(reagent/flush))))
handle-delete (fn [_]
(when-not loading-suggested-routes?
(let [current-limit-amount (:amount @current-limit)]
(swap! input-value #(subs % 0 (dec (count %))))
(reset-input-error @input-value current-limit-amount input-error)
(reagent/flush))))
handle-on-change (fn [v]
(when (valid-input? @input-value v)
(let [num-value (or (parse-double v) 0)
current-limit-amount (:amount @current-limit)]
(reset! input-value v)
(reset-input-error num-value current-limit-amount input-error)
(reagent/flush))))]
(fn [{:keys [on-confirm]
:or {on-confirm #(rf/dispatch [:wallet/send-select-amount
{:amount @input-value
:stack-id :wallet-send-input-amount}])}}]
(let [limit-label (make-limit-label @current-limit)
input-num-value (parse-double @input-value)
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
route (rf/sub [:wallet/wallet-send-route])
confirm-disabled? (or
(nil? route)
(empty? @input-value)
(<= input-num-value 0)
(> input-num-value (:amount @current-limit)))
amount (str @input-value " " token-symbol)
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
fetch-routes (fn []
;; crypto-decimals, limit-crypto and initial-crypto-currency? args are needed
;; for component tests only
[{default-on-confirm :on-confirm
default-limit-crypto :limit-crypto
default-crypto-decimals :crypto-decimals
initial-crypto-currency? :initial-crypto-currency?
:or {initial-crypto-currency? true}}]
(let [_ (rn/dismiss-keyboard!)
bottom (safe-area/get-bottom)
input-value (reagent/atom "")
input-error (reagent/atom false)
crypto-currency? (reagent/atom initial-crypto-currency?)
input-selection (reagent/atom {:start 0 :end 0})
handle-swap (fn [{:keys [crypto? limit-fiat limit-crypto]}]
(let [num-value (parse-double @input-value)
current-limit (if crypto? limit-crypto limit-fiat)]
(reset! crypto-currency? crypto?)
(reset-input-error num-value current-limit input-error)))
handle-keyboard-press (fn [v loading-routes? current-limit-amount]
(when-not loading-routes?
(let [current-value @input-value
new-value (make-new-input current-value v input-selection)
num-value (or (parse-double new-value) 0)]
(reset! input-value new-value)
(reset-input-error num-value current-limit-amount input-error)
(reagent/flush))))
handle-delete (fn [loading-routes? current-limit-amount]
(when-not loading-routes?
(let [{:keys [start end]} @input-selection]
(reset-input-error @input-value current-limit-amount input-error)
(when (= start end)
(swap! input-value delete-from-string start)
(move-input-cursor input-selection (dec start)))
(reagent/flush))))
handle-on-change (fn [v current-limit-amount]
(when (valid-input? @input-value v)
(let [num-value (or (parse-double v) 0)]
(reset! input-value v)
(reset-input-error num-value current-limit-amount input-error)
(reagent/flush))))
on-navigate-back (fn []
(rf/dispatch [:wallet/clean-selected-token])
(rf/dispatch [:navigate-back-within-stack :wallet-send-input-amount]))
fetch-routes (fn [input-num-value current-limit-amount]
(rf/dispatch [:wallet/clean-suggested-routes])
(when-not (or
(empty? @input-value)
(<= input-num-value 0)
(> input-num-value (:amount @current-limit)))
(debounce/debounce-and-dispatch [:wallet/get-suggested-routes
@input-value]
100)))]
(when-not (or (empty? @input-value)
(<= input-num-value 0)
(> input-num-value current-limit-amount))
(debounce/debounce-and-dispatch
[:wallet/get-suggested-routes @input-value]
100)))
handle-on-confirm (fn []
(rf/dispatch [:wallet/send-select-amount
{:amount @input-value
:stack-id :wallet-send-input-amount}]))
selection-change (fn [selection]
;; `reagent/flush` is needed to properly propagate the
;; input cursor state. Since this is a controlled
;; component the cursor will become static if
;; `reagent/flush` is removed.
(reset! input-selection selection)
(reagent/flush))]
(fn []
(let [{fiat-currency :currency} (rf/sub [:profile/profile])
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
{token-balance :total-balance
token-symbol :symbol
token-networks :networks
:as token} (rf/sub [:wallet/wallet-send-token])
conversion-rate (-> token :market-values-per-currency :usd :price)
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or default-crypto-decimals
(utils/get-crypto-decimals-count token))
crypto-limit (or default-limit-crypto
(utils/get-standard-crypto-format token token-balance))
fiat-limit (.toFixed (* token-balance conversion-rate) 2)
current-limit (if @crypto-currency? crypto-limit fiat-limit)
current-currency (if @crypto-currency? token-symbol fiat-currency)
limit-label (make-limit-label {:amount current-limit
:currency current-currency})
input-num-value (parse-double @input-value)
confirm-disabled? (or (nil? route)
(empty? @input-value)
(<= input-num-value 0)
(> input-num-value current-limit))
amount-text (str @input-value " " token-symbol)]
(rn/use-effect
(fn []
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)]
#(.remove app-keyboard-listener))))
(rn/use-effect #(fetch-routes) [@input-value])
(rn/use-effect
#(fetch-routes input-num-value current-limit)
[@input-value])
[rn/view
{:style style/screen
:accessibility-label (str "container" (when @input-error "-error"))}
[account-switcher/view
{:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back-within-stack :wallet-send-input-amount])
:on-press on-navigate-back
:switcher-type :select-account}]
[quo/token-input
{:container-style style/input-container
:token token-symbol
:currency currency
:crypto-decimals crypto-decimals
:error? @input-error
:networks (:networks token)
:title (i18n/label :t/send-limit {:limit limit-label})
:conversion conversion-rate
:show-keyboard? false
:value @input-value
:on-swap handle-swap
:on-change-text (fn [text]
(handle-on-change text))}]
{:container-style style/input-container
:token token-symbol
:currency current-currency
:crypto-decimals crypto-decimals
:error? @input-error
:networks token-networks
:title (i18n/label :t/send-limit {:limit limit-label})
:conversion conversion-rate
:show-keyboard? false
:value @input-value
:selection @input-selection
:on-change-text #(handle-on-change % current-limit)
:on-selection-change selection-change
:on-swap #(handle-swap
{:crypto? %
:currency current-currency
:token-symbol token-symbol
:limit-fiat fiat-limit
:limit-crypto crypto-limit})}]
[routes/view
{:amount amount
{:amount amount-text
:routes suggested-routes
:token token
:input-value @input-value
:fetch-routes fetch-routes}]
:fetch-routes #(fetch-routes % current-limit)}]
[quo/bottom-actions
{:actions :1-action
:customization-color color
:button-one-label (i18n/label :t/confirm)
:button-one-props {:disabled? confirm-disabled?
:on-press on-confirm}
:customization-color color}]
:on-press on-confirm}}]
[quo/numbered-keyboard
{:container-style (style/keyboard-container bottom)
:left-action :dot
:delete-key? true
:on-press handle-keyboard-press
:on-delete handle-delete}]]))))
:on-press #(handle-keyboard-press % loading-routes? current-limit)
:on-delete #(handle-delete loading-routes? current-limit)}]]))))
(defn- view-internal
[props]
@@ -17,22 +17,21 @@
{:id :tab/collectibles :label (i18n/label :t/collectibles) :accessibility-label :collectibles-tab}])
(defn- asset-component
[]
(fn [token _ _ {:keys [currency currency-symbol]}]
(let [on-press #(rf/dispatch [:wallet/send-select-token
{:token token
:stack-id :wallet-select-asset}])
token-units (utils/total-token-units-in-all-chains token)
crypto-formatted (utils/get-standard-crypto-format token token-units)
fiat-value (utils/total-token-fiat-value currency token)
fiat-formatted (utils/get-standard-fiat-format crypto-formatted currency-symbol fiat-value)]
[quo/token-network
{:token (:symbol token)
:label (:name token)
:token-value (str crypto-formatted " " (:symbol token))
:fiat-value fiat-formatted
:networks (:networks token)
:on-press on-press}])))
[token _ _ {:keys [currency currency-symbol]}]
(let [on-press #(rf/dispatch [:wallet/send-select-token
{:token token
:stack-id :wallet-select-asset}])
token-units (utils/total-token-units-in-all-chains token)
crypto-formatted (utils/get-standard-crypto-format token token-units)
fiat-value (utils/total-token-fiat-value currency token)
fiat-formatted (utils/get-standard-fiat-format crypto-formatted currency-symbol fiat-value)]
[quo/token-network
{:token (:symbol token)
:label (:name token)
:token-value (str crypto-formatted " " (:symbol token))
:fiat-value fiat-formatted
:networks (:networks token)
:on-press on-press}]))
(defn- asset-list
[search-text]
@@ -14,16 +14,18 @@
{:margin-right 4})
(defn details-container
[route? theme]
[{:keys [loading-suggested-routes? route-loaded? theme]}]
{:flex-direction :row
:justify-content (if route? :space-between :center)
:height 52
:justify-content (if route-loaded? :space-between :center)
:height (when (or loading-suggested-routes? route-loaded?) 52)
:padding-horizontal 12
:padding-top 7
:padding-bottom 8
:border-radius 16
:border-width 1
:border-color (colors/theme-colors colors/neutral-10 colors/neutral-90 theme)})
:border-color (if (or loading-suggested-routes? route-loaded?)
(colors/theme-colors colors/neutral-10 colors/neutral-90 theme)
:transparent)})
(def details-title-container
{:padding-horizontal 20
@@ -74,9 +74,10 @@
:account-props account-props}]])
(defn- transaction-details
[{:keys [estimated-time-min max-fees token amount to-address route theme]}]
(let [currency-symbol (rf/sub [:profile/currency-symbol])
route-loaded? (some? route)]
[{:keys [estimated-time-min max-fees token-symbol amount to-address route theme]}]
(let [currency-symbol (rf/sub [:profile/currency-symbol])
route-loaded? (some? route)
loading-suggested-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])]
[rn/view
{:style style/details-title-container}
[quo/text
@@ -86,8 +87,14 @@
:accessibility-label :summary-from-label}
(i18n/label :t/details)]
[rn/view
{:style (style/details-container route-loaded? theme)}
(if route-loaded?
{:style (style/details-container
{:loading-suggested-routes? loading-suggested-routes?
:route-loaded? route-loaded?
:theme theme})}
(cond
loading-suggested-routes?
[rn/activity-indicator {:style {:align-self :center}}]
route-loaded?
[:<>
[quo/data-item
{:container-style style/detail-item
@@ -123,8 +130,10 @@
:status :default
:size :small
:title (i18n/label :t/user-gets {:name (utils/get-shortened-address to-address)})
:subtitle (str amount " " (:symbol token))}]]
[rn/activity-indicator {:style {:align-self :center}}])]]))
:subtitle (str amount " " token-symbol)}]]
:else
[quo/text {:style {:align-self :center}}
(i18n/label :t/no-routes-found-confirmation)])]]))
(defn- view-internal
[_]
@@ -140,9 +149,10 @@
collectible-data (:collectible-data collectible)
collectible-id (get-in collectible [:id :token-id])
token-symbol (if collectible
(first (remove string/blank?
[(:name collectible-data)
(str (:name collection-data) " #" collectible-id)]))
(first (remove
string/blank?
[(:name collectible-data)
(str (:name collection-data) " #" collectible-id)]))
(:symbol token))
image-url (when collectible (:image-url collectible-data))
amount (:amount send-transaction-data)
@@ -157,7 +167,8 @@
:emoji (:emoji account)
:type :default
:name (:name account)
:address (utils/get-shortened-address (:address account))}
:address (utils/get-shortened-address (:address
account))}
user-props {:full-name to-address
:address (utils/get-shortened-address to-address)}]
[rn/view {:style {:flex 1}}
@@ -173,17 +184,17 @@
:on-press #(js/alert
"to be implemented")
:accessibility-label :advanced-options}]}]
:footer (if route
:footer (when route
[standard-auth/slide-button
{:size :size-48
:track-text (i18n/label :t/slide-to-send)
:container-style {:z-index 2}
:customization-color account-color
:on-auth-success #(rf/dispatch [:wallet/send-transaction
(security/safe-unmask-data
%)])
:auth-button-label (i18n/label :t/confirm)}]
[rn/activity-indicator])
:on-auth-success #(rf/dispatch
[:wallet/send-transaction
(security/safe-unmask-data
%)])
:auth-button-label (i18n/label :t/confirm)}])
:gradient-cover? true
:customization-color (:color account)}
[rn/view
@@ -213,7 +224,7 @@
[transaction-details
{:estimated-time-min estimated-time-min
:max-fees max-fees
:token token
:token-symbol token-symbol
:amount amount
:to-address to-address
:theme theme
+9 -2
View File
@@ -13,7 +13,8 @@
[react-native.platform :as platform]
[react-native.shake :as react-native-shake]
[reagent.impl.batching :as batching]
[status-im.common.log :as log]
[status-im.common.log :as logging]
[taoensso.timbre :as log]
[status-im.common.universal-links :as universal-links]
[status-im.config :as config]
[status-im.contexts.profile.push-notifications.events :as notifications]
@@ -34,12 +35,17 @@
(def adjust-resize 16)
(defn is-hermes
[]
(boolean (.-HermesInternal js/global)))
(defn init
[]
(native-module/init #(re-frame/dispatch [:signals/signal-received %]))
(when platform/android?
(native-module/set-soft-input-mode adjust-resize))
(log/setup config/log-level)
(logging/setup config/log-level)
(global-error/register-handler)
(notifications/listen-notifications)
(.addEventListener rn/app-state "change" #(re-frame/dispatch [:app-state-change %]))
@@ -54,5 +60,6 @@
(async-storage/get-item :screen-height #(reset! shell.state/screen-height %))
(dev/setup)
(log/info "hermesEnabled ->" (is-hermes))
(re-frame/dispatch-sync [:app-started]))
@@ -2,6 +2,8 @@
(:require
[cljs.test :refer [deftest is]]
[day8.re-frame.test :as rf-test]
[legacy.status-im.multiaccounts.logout.core :as logout]
[legacy.status-im.utils.test :as utils.test]
[status-im.contexts.profile.utils :as profile.utils]
[test-helpers.integration :as h]
[utils.re-frame :as rf]))
@@ -19,4 +21,38 @@
[:toasts/upsert]
(let [profile (rf/sub [:profile/profile])
display-name (profile.utils/displayed-name profile)]
(is (= new-name display-name))))))))))
(is (= new-name display-name)))
(h/logout)
(rf-test/wait-for [::logout/logout-method]))))))))
(deftest edit-profile-picture-test
(h/log-headline :edit-profile-picture-test)
(let [mock-image "resources/images/mock2/monkey.png"
absolute-path (.resolve utils.test/path mock-image)]
(rf-test/run-test-async
(h/with-app-initialized
(h/with-account
(rf/dispatch [:profile/edit-picture absolute-path 80 80])
(rf-test/wait-for
[:profile/update-local-picture]
(rf-test/wait-for
[:toasts/upsert]
(let [profile (rf/sub [:profile/profile])]
(is (not (nil? (:images profile)))))
(h/logout)
(rf-test/wait-for [::logout/logout-method]))))))))
(deftest delete-profile-picture-test
(h/log-headline :delete-profile-picture-test)
(rf-test/run-test-async
(h/with-app-initialized
(h/with-account
(rf/dispatch [:profile/delete-picture])
(rf-test/wait-for
[:profile/update-local-picture]
(rf-test/wait-for
[:toasts/upsert]
(let [profile (rf/sub [:profile/profile])]
(is (nil? (:image profile))))
(h/logout)
(rf-test/wait-for [::logout/logout-method])))))))
+1 -1
View File
@@ -143,7 +143,7 @@
:component communities.discover/view}
{:name :community-overview
:component communities.overview/overview}
:component communities.overview/view}
{:name :settings
:options options/transparent-screen-options
+4 -7
View File
@@ -1,6 +1,7 @@
(ns status-im.subs.communities
(:require
[clojure.string :as string]
[legacy.status-im.data-store.communities :as data-store]
[legacy.status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
@@ -307,10 +308,6 @@
[(re-frame/subscribe [:communities/community community-id])])
(fn [[{:keys [token-permissions-check token-permissions checking-permissions? token-images]}] _]
(let [can-request-access? (:satisfied token-permissions-check)
role-permissions #{constants/community-token-permission-become-admin
constants/community-token-permission-become-member
constants/community-token-permission-become-token-master
constants/community-token-permission-become-token-owner}
highest-permission-role
(when can-request-access?
(->> token-permissions-check
@@ -321,7 +318,7 @@
(and (first criteria)
(some #{(:type (permission-id->permission-value token-permissions
permission-id))}
role-permissions))]
constants/community-role-permissions))]
(if highest-permission-role
(min highest-permission-role permission-type)
permission-type)
@@ -339,8 +336,8 @@
(:permissions token-permissions-check))
:tokens (->>
token-permissions
(filter (fn [[_ {:keys [type]}]]
(= type constants/community-token-permission-become-member)))
(filter (fn [[_ permission]]
(data-store/role-permission? permission)))
(map (fn [[perm-key {:keys [token_criteria]}]]
(let [check-criteria (get-in token-permissions-check
[:permissions perm-key :criteria])]
+5 -1
View File
@@ -455,7 +455,11 @@
(swap! rf-db/app-db assoc-in [:communities community-id] community)
(is (match? {:can-request-access? true
:number-of-hold-tokens 2
:tokens [[{:symbol "ETH"
:tokens [[{:symbol "DAI"
:amount "5.0"
:sufficient? nil
:loading? checking-permissions?}]
[{:symbol "ETH"
:amount "0.001"
:sufficient? nil
:loading? checking-permissions?
+6
View File
@@ -106,6 +106,12 @@
(fn [profile]
(:test-networks-enabled? profile)))
(re-frame/reg-sub
:profile/is-sepolia-enabled?
:<- [:profile/profile]
(fn [profile]
(:is-sepolia-enabled? profile)))
(re-frame/reg-sub
:multiaccount/contact
:<- [:profile/profile]
+17 -6
View File
@@ -30,11 +30,22 @@
:short-name constants/optimism-short-name
:network-name constants/optimism-network-name})
(def network-list
{constants/mainnet-chain-id mainnet-network-details
constants/goerli-chain-id mainnet-network-details
constants/arbitrum-chain-id arbitrum-network-details
constants/optimism-chain-id optimism-network-details})
(defn get-network-details
[chain-id]
(case chain-id
(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/optimism-mainnet-chain-id constants/optimism-goerli-chain-id
constants/optimism-sepolia-chain-id)
optimism-network-details
nil))
(re-frame/reg-sub
:wallet/network-details
@@ -43,7 +54,7 @@
(->> networks
(keep
(fn [{:keys [chain-id related-chain-id layer test?]}]
(let [network-details (get network-list (if test? related-chain-id chain-id))]
(let [network-details (get-network-details (if test? related-chain-id chain-id))]
(assoc network-details
:chain-id chain-id
:related-chain-id related-chain-id
+3 -3
View File
@@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "v0.172.10",
"commit-sha1": "69948a7024df8c4537fc4986b39aded22e89179b",
"src-sha256": "0xg32k3f4yy7gwqzh60qdgrgi2zvm8s1w7sisqmcn5vw90jya70w"
"version": "e9b10c4beb2ddd16e9ddff089831392a951b5851",
"commit-sha1": "e9b10c4beb2ddd16e9ddff089831392a951b5851",
"src-sha256": "04f40067rh0s667yx4p94irl2h84ipi8ig0m023ifqik068fjdjv"
}
@@ -8,6 +8,7 @@ from selenium.common.exceptions import TimeoutException, NoSuchElementException
from tests import marks, run_in_parallel, transl
from tests.base_test_case import MultipleSharedDeviceTestCase, create_shared_drivers
from views.base_element import Button
from views.sign_in_view import SignInView
@@ -161,13 +162,15 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
self.home_2.just_fyi("Check 'Open in Status' option")
# url_to_open = 'http://status.app'
url_to_open = 'https://www.ethereum.org/en/run-a-node/'
url_to_open = 'https://github.com/status-im/status-mobile'
self.chat_1.send_message(url_to_open)
chat_element = self.chat_2.chat_element_by_text(url_to_open)
if chat_element.is_element_displayed(120):
chat_element.click_on_link_inside_message_body()
web_view = self.chat_2.open_in_status_button.click()
if not web_view.element_by_text("Take full control. Run your own node.").is_element_displayed(60):
text_element = web_view.element_by_text("a free (libre) open source, mobile OS for Ethereum")
sign_in_button = Button(self.chat_2.driver, xpath="//android.view.View[@content-desc='Sign in']")
if not text_element.is_element_displayed(30) or not sign_in_button.is_element_displayed(30):
self.errors.append('URL was not opened from 1-1 chat')
else:
self.errors.append("Message with URL was not received")
@@ -1090,8 +1090,6 @@ class TestCommunityMultipleDeviceMergedTwo(MultipleSharedDeviceTestCase):
self.errors.verify_no_errors()
@marks.testrail_id(703629)
@marks.xfail(
reason="Can't join a community if admin goes offline, https://github.com/status-im/status-mobile/issues/17678")
def test_community_join_when_node_owner_offline(self):
for home in self.homes:
home.navigate_back_to_home_view()
@@ -1143,7 +1141,8 @@ class TestCommunityMultipleDeviceMergedTwo(MultipleSharedDeviceTestCase):
else:
self.errors.append("Community channel is not displayed for user before join")
self.community_2.toast_content_element.wait_for_invisibility_of_element(30)
self.community_2.close_community_view_button.click()
self.community_2.close_community_view_button.click_until_absense_of_element(
self.community_2.close_community_view_button)
self.home_2.pending_communities_tab.click()
if self.home_2.get_chat(community_name, community=True).is_element_displayed():
self.home_2.get_chat(community_name, community=True).click()
@@ -1166,7 +1165,8 @@ class TestCommunityMultipleDeviceMergedTwo(MultipleSharedDeviceTestCase):
# ToDo: add verification when toast is fixed
if not self.community_2.community_status_joined.is_element_displayed():
self.errors.append("Joined status is not displayed")
self.community_2.close_community_view_button.click()
self.community_2.close_community_view_button.click_until_absense_of_element(
self.community_2.close_community_view_button)
self.home_2.joined_communities_tab.click()
chat_element = self.home_2.get_chat(community_name, community=True)
if chat_element.is_element_displayed(30):
+5 -1
View File
@@ -1183,6 +1183,8 @@
"profile-details": "Profile details",
"profile-name": "Profile name",
"profile-name-is-too-long": "Profile name is too long",
"profile-picture-added": "Profile picture added",
"profile-picture-removed": "Profile picture removed",
"public-chat": "Public chat",
"public-chats": "Public chats",
"public-group-status": "Public",
@@ -1283,6 +1285,7 @@
"share-channel": "Share channel",
"share-address": "Share address",
"share-chat": "Share chat",
"share-collectible": "Share collectible",
"share-contact-code": "Share my chat key",
"share-community": "Share community",
"share-dapp-text": "Check out this DApp I'm using on Status: {{link}}",
@@ -2479,5 +2482,6 @@
"preferred-by-receiver": "Preferred by receiver",
"not-preferred-by-receiver": "Not preferred by receiver",
"apply-changes": "Apply changes",
"receiver-networks-warning": "Changing these settings may result in sending tokens to networks the recipient doesn't use"
"receiver-networks-warning": "Changing these settings may result in sending tokens to networks the recipient doesn't use",
"no-routes-found-confirmation": "No routes found. Token type may not be supported or you may don't have enough ETH to cover gas."
}