chore: merge branch 'develop' into status-go/filter-improvements

This commit is contained in:
Prem Chaitanya Prathi 2024-06-13 18:46:09 +05:30
commit 59896858d0
60 changed files with 810 additions and 482 deletions

View File

@ -10,7 +10,7 @@
(reagent/as-element
(into [react/keyboard-avoiding-view
(update props
:keyboardVerticalOffset
:keyboard-vertical-offset
+
20
(if (:ignore-offset props) 44 0))]

View File

@ -275,7 +275,7 @@
(merge (when platform/ios? {:behavior :padding})
(if (:ignore-offset props)
props
(update props :keyboardVerticalOffset + 44 (:status-bar-height @navigation-const))))]
(update props :keyboard-vertical-offset + 44 (:status-bar-height @navigation-const))))]
children))
(defn keyboard-avoiding-view-new
@ -284,7 +284,7 @@
(merge (when platform/ios? {:behavior :padding})
(if (:ignore-offset props)
props
(update props :keyboardVerticalOffset + 44)))]
(update props :keyboard-vertical-offset + 44)))]
children))
(defn scroll-view

View File

@ -30,10 +30,7 @@
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))
:container-style style/title-icon-container
:accessibility-label :title-icon}])]
[address-text/view
{:networks (:networks account-props)
:address (:address account-props)
:format :short}]]])
[address-text/view (assoc account-props :format :short)]]])
(defn- balance-view
[{:keys [balance-props type theme]}]

View File

@ -25,12 +25,12 @@
:blur :grey})
(defn- page-nav-base
[{:keys [margin-top background on-press accessibility-label icon-name behind-overlay?]
[{:keys [margin-top background on-press accessibility-label icon-name behind-overlay? align-center?]
:or {background :white}}
& children]
(into [rn/view {:style (style/container margin-top)}
(when icon-name
[rn/view {:style style/icon-container}
[rn/view (when align-center? {:style style/icon-container})
[button/button
{:type (button-type background)
:icon-only? true

View File

@ -0,0 +1,32 @@
(ns quo.components.tabs.tabs.schema)
(def ^:private ?data
[:sequential
[:maybe
[:map
[:id [:or :int :keyword [:set :int]]]
[:label [:maybe :string]]
[:accessibility-label {:optional true} [:maybe [:or :keyword :string]]]
[:notification-dot? {:optional true} [:maybe :boolean]]]]])
(def ?schema
[:=>
[:catn
[:props
[:map
[:default-active {:optional true} [:maybe [:or :int :keyword]]]
[:active-tab-id {:optional true} [:maybe [:or :int :keyword]]]
[:data ?data]
[:fade-end-percentage {:optional true} [:or :double :string]]
[:fade-end? {:optional true} [:maybe :boolean]]
[:blur? {:optional true} [:maybe :boolean]]
[:on-change {:optional true} [:maybe fn?]]
[:on-scroll {:optional true} [:maybe fn?]]
[:scroll-on-press? {:optional true} [:maybe :boolean]]
[:scrollable? {:optional true} [:maybe :boolean]]
[:style {:optional true} [:maybe :map]]
[:container-style {:optional true} [:maybe :map]]
[:size {:optional true} [:maybe [:or :keyword :int]]]
[:in-scroll-view? {:optional true} [:maybe :boolean]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]]]]
:any])

View File

@ -2,12 +2,14 @@
(:require
[oops.core :refer [oget]]
[quo.components.tabs.tab.view :as tab]
[quo.components.tabs.tabs.schema :as component-schema]
[quo.components.tabs.tabs.style :as style]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[react-native.linear-gradient :as linear-gradient]
[react-native.masked-view :as masked-view]
[reagent.core :as reagent]
[schema.core :as schema]
[utils.collection :as utils.collection]
[utils.number]))
@ -84,36 +86,20 @@
:on-press #(on-press % index)}
label]])
(defn view
" Common options (for scrollable and non-scrollable tabs):
- `blur?` Boolean passed down to `quo.components.tabs.tab/tab`.
- `data` Vector of tab items.
- `on-change` Callback called after a tab is selected.
- `size` 32/24
- `style` Style map passed to View wrapping tabs or to the FlatList when tabs
are scrollable.
Options for scrollable tabs:
- `fade-end-percentage` Percentage where fading starts relative to the total
layout width of the `flat-list` data.
- `fade-end?` When non-nil, causes the end of the scrollable view to fade out.
- `on-scroll` Callback called on the on-scroll event of the FlatList. Only
used when `scrollable?` is non-nil.
- `scrollable?` When non-nil, use a scrollable flat-list to render tabs.
- `scroll-on-press?` When non-nil, clicking on a tag centers it the middle
(with animation enabled).
"
(defn- view-internal
[{:keys [default-active data fade-end-percentage fade-end? on-change on-scroll scroll-on-press?
scrollable? style container-style size blur? in-scroll-view? customization-color]
scrollable? style container-style size blur? in-scroll-view? customization-color
active-tab-id]
:or {fade-end-percentage 0.8
fade-end? false
scrollable? false
scroll-on-press? false
size default-tab-size}
:as props}]
(let [[active-tab-id
set-active-tab-id] (rn/use-state default-active)
(let [[active-tab-internal-id
set-active-tab-internal-id] (rn/use-state default-active)
tab-id (or active-tab-id active-tab-internal-id)
[fading set-fading] (rn/use-state fade-end-percentage)
flat-list-ref (rn/use-ref-atom nil)
tabs-data (rn/use-memo (fn [] (filterv some? data))
@ -143,11 +129,11 @@
{:animated false
:index
(utils.collection/first-index
#(= active-tab-id (:id %))
#(= tab-id (:id %))
tabs-data)}))))
[active-tab-id tabs-data])
[tab-id tabs-data])
on-tab-press (rn/use-callback (fn [id index]
(set-active-tab-id id)
(set-active-tab-internal-id id)
(when (and scroll-on-press? @flat-list-ref)
(.scrollToIndex ^js @flat-list-ref
#js
@ -156,7 +142,8 @@
:viewPosition 0.5}))
(when on-change
(on-change id)))
[set-active-tab-id scroll-on-press? on-change])]
[set-active-tab-internal-id scroll-on-press?
on-change])]
(if scrollable?
[rn/view {:style {:margin-top (- (dec unread-count-offset))}}
[masked-view-wrapper
@ -183,7 +170,7 @@
:on-scroll on-scroll
:on-layout set-initial-scroll-poisition
:render-fn tab-view
:render-data {:active-tab-id active-tab-id
:render-data {:active-tab-id tab-id
:blur? blur?
:customization-color customization-color
:number-of-items (count tabs-data)
@ -194,7 +181,7 @@
(map-indexed (fn [index item]
^{:key (:id item)}
[tab-view item index nil
{:active-tab-id active-tab-id
{:active-tab-id tab-id
:blur? blur?
:customization-color customization-color
:number-of-items (count tabs-data)
@ -202,3 +189,5 @@
:on-press on-tab-press
:style style}])
tabs-data)])))
(def view (schema/instrument #'view-internal component-schema/?schema))

View File

@ -59,26 +59,36 @@
:height 32
:margin-top 8})
(def content-container
{:margin-left 8})
(def container
{:flex-direction :row
:column-gap 8})
(def content-line
{:flex-direction :row
:margin-top 2
:align-items :center})
(defn icon-hole-view
[theme blur?]
(def icon-hole-view
{:width 32
:height 32
:border-width 1
:border-color (if-not blur?
(colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
colors/white-opa-5)
:border-radius 16
:align-items :center
:justify-content :center})
(defn icon-circle-border
[theme blur?]
{:position :absolute
:top 0
:left 0
:right 0
:bottom 0
:width 32
:height 32
:border-width 1
:border-radius 16
:border-color (if-not blur?
(colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
colors/white-opa-5)})
(defn icon-color
[theme]
(colors/theme-colors colors/neutral-100 colors/white theme))

View File

@ -70,20 +70,21 @@
status :pending}}
theme]
[rn/view {:style style/icon-container}
[rn/view {:style style/icon-status-container}
[icon/icon (status-icon status)
{:size 12
:no-color :true}]]
[hole-view/hole-view
{:style (style/icon-hole-view theme blur?)
{:style style/icon-hole-view
:holes [{:x 20
:y 20
:right 0
:width 12
:height 12
:borderRadius 6}]}
[icon/icon (transaction-icon transaction)
{:color (style/icon-color theme)}]]
[rn/view {:style style/icon-status-container}
[icon/icon (status-icon status)
{:size 12
:no-color :true}]]])
[icon/icon (transaction-icon transaction :i/placeholder)
{:color (style/icon-color theme)}]
[rn/view {:style (style/icon-circle-border theme blur?)}]]])
(defn prop-text
[label theme]
@ -116,11 +117,9 @@
:on-press on-press
:on-press-in on-press-in
:on-press-out on-press-out}
[rn/view
{:style {:flex-direction :row}}
[rn/view {:style style/container}
[transaction-icon-view props theme]
[rn/view
{:style style/content-container}
[transaction-header props theme]
[rn/view {:style style/content-line}
(when first-tag [prop-tag first-tag blur?])

View File

@ -41,13 +41,13 @@
[message]
(let [cause (if platform/android?
(condp = message
android-not-enrolled-error-message :biometrics/not-enrolled-error
android-not-enrolled-error-message :biometrics/fingerprints-not-enrolled-error
android-not-available-error-message :biometrics/not-available-error
android-too-many-attempts-error-message :biometric/too-many-attempts
:biometrics/unknown-error)
(condp #(string/includes? %2 %1) message
ios-not-enrolled-error-message :biometrics/not-enrolled-error
ios-not-enrolled-error-message :biometrics/ios-not-enrolled-error
:biometrics/unknown-error))]
(ex-info "Failed to authenticate with biometrics"
{:orig-error-message message}

View File

@ -14,8 +14,11 @@
(defn show-message
[_ [code]]
(let [content (case code
(:biometrics/not-enrolled-error
(:biometrics/fingerprints-not-enrolled-error
:biometrics/not-available-error)
(i18n/label :t/grant-fingerprints-permissions)
:biometrics/ios-not-enrolled-error
(i18n/label :t/grant-face-id-permissions)
:biometric/too-many-attempts

View File

@ -18,7 +18,7 @@
(let [cofx {:db {}}
expected {:fx [[:effects.utils/show-popup
{:title (i18n/label :t/biometric-auth-login-error-title)
:content (i18n/label :t/grant-face-id-permissions)}]]}]
:content (i18n/label :t/grant-fingerprints-permissions)}]]}]
(is (match? expected
(sut/show-message cofx
[:biometrics/not-available-error])))))

View File

@ -2,6 +2,7 @@
(:require
[clojure.string :as string]
[legacy.status-im.ethereum.mnemonic :as mnemonic]
[oops.core :as oops]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
@ -96,35 +97,36 @@
(defn recovery-phrase-screen
[{:keys [keypair title recovering-keypair? render-controls]}]
(reagent/with-let [keyboard-shown? (reagent/atom false)
keyboard-show-listener (.addListener rn/keyboard
"keyboardDidShow"
#(reset! keyboard-shown? true))
keyboard-hide-listener (.addListener rn/keyboard
"keyboardDidHide"
#(reset! keyboard-shown? false))
invalid-seed-phrase? (reagent/atom false)
input-ref (reagent/atom nil)
focus-input (fn []
(let [ref @input-ref]
(when ref
(.focus ref))))
set-invalid-seed-phrase #(reset! invalid-seed-phrase? true)
seed-phrase (reagent/atom "")
on-change-seed-phrase (fn [new-phrase]
(when @invalid-seed-phrase?
(reset! invalid-seed-phrase? false))
(reset! seed-phrase new-phrase))
on-submit (fn []
(swap! seed-phrase clean-seed-phrase)
(if recovering-keypair?
(rf/dispatch [:wallet/seed-phrase-entered
(security/mask-data
@seed-phrase)
set-invalid-seed-phrase])
(rf/dispatch [:onboarding/seed-phrase-entered
(security/mask-data @seed-phrase)
set-invalid-seed-phrase])))]
(reagent/with-let [keyboard-shown? (reagent/atom false)
keyboard-show-listener (.addListener rn/keyboard
"keyboardDidShow"
#(reset! keyboard-shown? true))
keyboard-hide-listener (.addListener rn/keyboard
"keyboardDidHide"
#(reset! keyboard-shown? false))
invalid-seed-phrase? (reagent/atom false)
incorrect-seed-phrase? (reagent/atom false)
input-ref (reagent/atom nil)
focus-input #(some-> @input-ref
(oops/ocall "focus"))
set-incorrect-seed-phrase #(reset! incorrect-seed-phrase? true)
set-invalid-seed-phrase #(reset! invalid-seed-phrase? true)
seed-phrase (reagent/atom "")
on-change-seed-phrase (fn [new-phrase]
(when @invalid-seed-phrase?
(reset! invalid-seed-phrase? false)
(reset! incorrect-seed-phrase? false))
(reset! seed-phrase new-phrase))
on-submit (fn []
(swap! seed-phrase clean-seed-phrase)
(if recovering-keypair?
(rf/dispatch [:wallet/seed-phrase-entered
(security/mask-data
@seed-phrase)
set-invalid-seed-phrase])
(rf/dispatch [:onboarding/seed-phrase-entered
(security/mask-data @seed-phrase)
set-invalid-seed-phrase])))]
(let [words-coll (mnemonic/passphrase->words @seed-phrase)
last-word (peek words-coll)
pick-suggested-word (fn [pressed-word]
@ -143,16 +145,18 @@
suggestions-state (cond
(or error-in-words?
words-exceeded?
@invalid-seed-phrase?) :error
@invalid-seed-phrase?
@incorrect-seed-phrase?) :error
(string/blank? @seed-phrase) :info
(string/ends-with? @seed-phrase " ") :empty
:else :words)
suggestions-text (cond
upper-case? (i18n/label :t/seed-phrase-words-uppercase)
words-exceeded? (i18n/label :t/seed-phrase-words-exceeded)
error-in-words? (i18n/label :t/seed-phrase-error)
@invalid-seed-phrase? (i18n/label :t/seed-phrase-invalid)
:else (i18n/label :t/seed-phrase-info))
upper-case? (i18n/label :t/seed-phrase-words-uppercase)
words-exceeded? (i18n/label :t/seed-phrase-words-exceeded)
error-in-words? (i18n/label :t/seed-phrase-error)
@invalid-seed-phrase? (i18n/label :t/seed-phrase-invalid)
@incorrect-seed-phrase? (i18n/label :t/seed-phrase-incorrect)
:else (i18n/label :t/seed-phrase-info))
error-state? (= suggestions-state :error)
button-disabled? (or error-state?
(not (constants/seed-phrase-valid-length word-count))
@ -166,13 +170,13 @@
:word-count word-count
:ref #(reset! input-ref %)}
(if (fn? render-controls)
(render-controls {:submit-disabled? button-disabled?
:keyboard-shown? @keyboard-shown?
:container-style (style/continue-button @keyboard-shown?)
:prepare-seed-phrase secure-clean-seed-phrase
:focus-input focus-input
:seed-phrase (security/mask-data @seed-phrase)
:set-invalid-seed-phrase set-invalid-seed-phrase})
(render-controls {:submit-disabled? button-disabled?
:keyboard-shown? @keyboard-shown?
:container-style (style/continue-button @keyboard-shown?)
:prepare-seed-phrase secure-clean-seed-phrase
:focus-input focus-input
:seed-phrase (security/mask-data @seed-phrase)
:set-incorrect-seed-phrase set-incorrect-seed-phrase})
[quo/button
{:container-style (style/continue-button @keyboard-shown?)
:type :primary

View File

@ -12,7 +12,7 @@
[utils.re-frame :as rf]))
(defn edit-message
[{:keys [text-value input-ref]}]
[{:keys [text-value input-ref input-height]}]
(let [theme (quo.theme/use-theme)]
[rn/view
{:style style/container
@ -32,7 +32,7 @@
{:size 24
:icon-only? true
:accessibility-label :edit-cancel-button
:on-press #(utils/cancel-edit-message text-value input-ref)
:on-press #(utils/cancel-edit-message text-value input-ref input-height)
:type :outline}
:i/close]]))

View File

@ -101,12 +101,16 @@
(rf/dispatch [:chat.ui/cancel-message-reply]))
(defn cancel-edit-message
[text-value input-ref]
[text-value input-ref input-height]
(reset! text-value "")
;; NOTE: adding a timeout to assure the input is blurred on the next tick
;; after the `text-value` was cleared. Otherwise the height will be calculated
;; with the old `text-value`, leading to wrong composer height after blur.
(js/setTimeout #(blur-input input-ref) 100)
(js/setTimeout
(fn []
(blur-input input-ref)
(reanimated/set-shared-value input-height constants/input-height))
100)
(.setNativeProps ^js @input-ref (clj->js {:text ""}))
(rf/dispatch [:chat.ui/set-input-content-height constants/input-height])
(rf/dispatch [:chat.ui/cancel-message-edit]))

View File

@ -105,8 +105,9 @@
[:<>
[reply/view state (:input-ref props)]
[edit/view
{:text-value (:text-value state)
:input-ref (:input-ref props)}]]
{:text-value (:text-value state)
:input-height (:height animations)
:input-ref (:input-ref props)}]]
[reanimated/touchable-opacity
{:active-opacity 1
:on-press (fn []

View File

@ -102,18 +102,43 @@
;; https://github.com/status-im/status-mobile/issues/17426
[quo/skeleton-list (skeleton-list-props :messages parent-height platform/ios?)]]))
(defn header-height
[{:keys [insets able-to-send-message? images reply edit link-previews? input-content-height]}]
(if able-to-send-message?
(cond-> composer.constants/composer-default-height
(ff/enabled? ::ff/shell.jump-to)
(+ jump-to.constants/floating-shell-button-height)
(seq images)
(+ composer.constants/images-container-height)
reply
(+ composer.constants/reply-container-height)
edit
(+ composer.constants/edit-container-height)
link-previews?
(+ composer.constants/links-container-height)
(and input-content-height (not= input-content-height composer.constants/input-height))
(+ composer.constants/input-height)
true
(+ (:bottom insets)))
(- 70 (:bottom insets))))
(defn list-header
[insets able-to-send-message?]
(let [images (rf/sub [:chats/sending-image])
height (if able-to-send-message?
(+ composer.constants/composer-default-height
(if (ff/enabled? ::ff/shell.jump-to)
jump-to.constants/floating-shell-button-height
0)
(if (seq images) composer.constants/images-container-height 0)
(:bottom insets))
(- 70 (:bottom insets)))]
[rn/view {:style {:height height}}]))
(let [header-data {:insets insets
:able-to-send-message? able-to-send-message?
:input-content-height (:input-content-height (rf/sub [:chats/current-chat-input]))
:images (rf/sub [:chats/sending-image])
:reply (rf/sub [:chats/reply-message])
:edit (rf/sub [:chats/edit-message])
:link-previews? (or (rf/sub [:chats/link-previews?])
(rf/sub [:chats/status-link-previews?]))}]
[rn/view {:style {:height (header-height header-data)}}]))
(defn list-footer-avatar
[{:keys [distance-from-list-top display-name online? profile-picture theme group-chat color

View File

@ -4,7 +4,6 @@
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.common.validation.profile :as profile-validator]
[status-im.constants :as constants]
[status-im.contexts.profile.edit.bio.style :as style]
@ -14,63 +13,63 @@
(defn view
[]
(let [insets (safe-area/get-insets)
profile (rf/sub [:profile/profile-with-image])
customization-color (rf/sub [:profile/customization-color])
alert-banners-top-margin (rf/sub [:alert-banners/top-margin])
profile-bio (:bio profile)
unsaved-bio (reagent/atom profile-bio)
error-msg (reagent/atom nil)
typing? (reagent/atom false)
validate-bio (debounce/debounce (fn [bio]
(reset! error-msg
(profile-validator/validation-bio bio))
(reset! typing? false))
300)
on-change-text (fn [s]
(reset! typing? true)
(reset! unsaved-bio s)
(validate-bio s))]
(fn []
[quo/overlay
{:type :shell
:container-style (style/page-wrapper insets)}
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back])}]
[rn/keyboard-avoiding-view
{:key :content
:keyboard-vertical-offset (if platform/ios? alert-banners-top-margin 0)
:style style/screen-container}
[rn/view {:style {:gap 22}}
[quo/text-combinations {:title (i18n/label :t/bio)}]
[quo/input
{:blur? true
:multiline? true
:error? (not (string/blank? @error-msg))
:container-style {:margin-bottom -11}
:default-value @unsaved-bio
:auto-focus true
:char-limit constants/profile-bio-max-length
:label (i18n/label :t/profile-bio)
:placeholder (i18n/label :t/something-about-you)
:on-change-text on-change-text}]
(when-not (string/blank? @error-msg)
[quo/info-message
{:type :error
:size :default
:icon :i/info}
@error-msg])]
[rn/view {:style style/button-wrapper}
[quo/button
{:type :primary
:customization-color customization-color
:on-press (fn []
(rf/dispatch [:profile/edit-bio @unsaved-bio]))
:disabled? (boolean (or @typing?
(and (string/blank? profile-bio)
(string/blank? @unsaved-bio))
(not (string/blank? @error-msg))))}
(i18n/label :t/save-bio)]]]])))
(let [insets (safe-area/get-insets)
profile (rf/sub [:profile/profile-with-image])
customization-color (rf/sub [:profile/customization-color])
alert-banners-top-margin (rf/sub [:alert-banners/top-margin])
current-bio (:bio profile)
[bio set-bio] (rn/use-state current-bio)
[error-msg set-error-msg] (rn/use-state nil)
[typing? set-typing] (rn/use-state false)
validate-bio (debounce/debounce (fn [bio]
(set-error-msg (profile-validator/validation-bio
bio))
(set-typing false))
300)
on-change-text (fn [s]
(set-typing true)
(set-bio s)
(validate-bio s))]
[quo/overlay
{:type :shell
:container-style (style/page-wrapper insets)}
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back])}]
[rn/keyboard-avoiding-view
{:key :content
:keyboard-vertical-offset (if platform/ios? alert-banners-top-margin 0)
:style style/screen-container}
[rn/view {:style {:gap 22}}
[quo/text-combinations {:title (i18n/label :t/bio)}]
[quo/input
{:blur? true
:multiline? true
:error? (not (string/blank? error-msg))
:container-style {:margin-bottom -11}
:default-value bio
:auto-focus true
:max-height 200
:char-limit constants/profile-bio-max-length
:label (i18n/label :t/profile-bio)
:placeholder (i18n/label :t/something-about-you)
:on-change-text on-change-text}]
(when-not (string/blank? error-msg)
[quo/info-message
{:type :error
:size :default
:icon :i/info}
error-msg])]
[rn/view {:style style/button-wrapper}
[quo/button
{:type :primary
:customization-color customization-color
:on-press (fn []
(rf/dispatch [:profile/edit-bio bio]))
:disabled? (boolean (or typing?
(and (string/blank? current-bio)
(string/blank? bio))
(not (string/blank? error-msg))))}
(i18n/label :t/save-bio)]]]]))

View File

@ -195,8 +195,8 @@
profile-picture (rf/sub [:profile/login-profiles-picture key-uid])
login-multiaccount (rn/use-callback #(rf/dispatch [:profile.login/login]))]
[rn/keyboard-avoiding-view
{:style style/login-container
:keyboardVerticalOffset (- (safe-area/get-bottom))}
{:style style/login-container
:keyboard-vertical-offset (- (safe-area/get-bottom))}
[rn/view {:style style/multi-profile-button-container}
(when config/quo-preview-enabled?
[quo/button

View File

@ -35,7 +35,7 @@
:blur? true
:action :arrow}
{:title (i18n/label :t/wallet)
:on-press #(rf/dispatch [:navigate-to-within-stack [:screen/settings.wallet :settings]])
:on-press #(rf/dispatch [:open-modal :screen/settings.wallet])
:image-props :i/wallet
:image :icon
:blur? true

View File

@ -1,6 +1,7 @@
(ns status-im.contexts.profile.settings.screens.password.change-password.style
(:require
[quo.foundations.colors :as colors]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]))
(def form-container
@ -48,7 +49,7 @@
:justify-content :space-between})
(def bottom-part
{:margin-bottom (- (safe-area/get-bottom) 12)
{:margin-bottom (if platform/ios? (safe-area/get-bottom) 12)
:justify-content :flex-end})
(def disclaimer-container

View File

@ -17,7 +17,7 @@
(defn view
[]
(let [{:keys [top]} (safe-area/get-insets)
(let [{:keys [top bottom]} (safe-area/get-insets)
alert-banners-top-margin (rf/sub [:alert-banners/top-margin])
current-step (rf/sub [:settings/change-password-current-step])]
(rn/use-unmount #(rf/dispatch [:change-password/reset]))
@ -33,8 +33,12 @@
:icon-name :i/arrow-left
:on-press navigate-back}]
[rn/keyboard-avoiding-view
{:style {:flex 1}
:keyboardVerticalOffset (if platform/ios? alert-banners-top-margin 0)}
{:style {:flex 1}
:keyboard-vertical-offset (if platform/ios?
(-> 12
(+ alert-banners-top-margin)
(- bottom))
0)}
(condp = current-step
:old-password [old-password-form/view]
:new-password [new-password-form/view])]]]))

View File

@ -30,18 +30,18 @@
supported? (boolean biometric-type)
enabled? (= auth-method constants/auth-method-biometric)
biometric-on? (and supported? enabled?)
press-handler (if biometric-on?
(fn [] (rf/dispatch [:biometric/disable]))
(on-press-biometric-enable label theme))]
press-handler (when supported?
(if biometric-on?
(fn [] (rf/dispatch [:biometric/disable]))
(on-press-biometric-enable label theme)))]
{:title label
:image-props icon
:image :icon
:blur? true
:action :selector
:action-props {:disabled? (not supported?)
:on-change press-handler
:action-props {:on-change press-handler
:checked? biometric-on?}
:on-press (when supported? press-handler)}))
:on-press press-handler}))
(defn- get-change-password-item
[]

View File

@ -167,9 +167,9 @@
(rf/reg-event-fx :wallet/verify-private-key-for-keypair verify-private-key-for-keypair)
(defn import-keypair-by-seed-phrase
(defn import-missing-keypair-by-seed-phrase
[_ [{:keys [keypair-key-uid seed-phrase password on-success on-error]}]]
{:fx [[:import-keypair-by-seed-phrase
{:fx [[:effects.wallet/import-missing-keypair-by-seed-phrase
{:keypair-key-uid keypair-key-uid
:seed-phrase seed-phrase
:password password
@ -178,24 +178,26 @@
#{keypair-key-uid}])
(rf/call-continuation on-success))
:on-error (fn [error]
(rf/dispatch [:wallet/import-keypair-by-seed-phrase-failed error])
(rf/dispatch [:wallet/import-missing-keypair-by-seed-phrase-failed error])
(log/error "failed to import missing keypair with seed phrase"
{:error error})
(rf/call-continuation on-error error))}]]})
(rf/reg-event-fx :wallet/import-keypair-by-seed-phrase import-keypair-by-seed-phrase)
(rf/reg-event-fx :wallet/import-missing-keypair-by-seed-phrase import-missing-keypair-by-seed-phrase)
(defn import-keypair-by-seed-phrase-failed
(defn import-missing-keypair-by-seed-phrase-failed
[_ [error]]
(let [error-type (-> error ex-message keyword)
error-data (ex-data error)]
(when-not (and (= error-type :import-keypair-by-seed-phrase/import-error)
(= (:hint error-data) :incorrect-seed-phrase-for-keypair))
(when-not (= error-type :import-missing-keypair-by-seed-phrase/import-error)
{:fx [[:dispatch
[:toasts/upsert
{:type :negative
:theme :dark
:text (:error error-data)}]]]})))
(rf/reg-event-fx :wallet/import-keypair-by-seed-phrase-failed import-keypair-by-seed-phrase-failed)
(rf/reg-event-fx :wallet/import-missing-keypair-by-seed-phrase-failed
import-missing-keypair-by-seed-phrase-failed)
(defn import-missing-keypair-by-private-key
[_ [{:keys [keypair-key-uid private-key password on-success on-error]}]]

View File

@ -1,7 +1,7 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.actions.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.settings.wallet.keypairs-and-accounts.remove.view :as remove-key-pair]
[status-im.contexts.settings.wallet.keypairs-and-accounts.remove.view :as remove-keypair]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@ -14,20 +14,22 @@
[(:key-uid keypair)]])
[keypair])
on-show-qr (rn/use-callback #(rf/dispatch [:open-modal
:screen/settings.encrypted-key-pair-qr
:screen/settings.encrypted-keypair-qr
keypair])
[keypair])
on-remove-keypair (rn/use-callback #(rf/dispatch
[:show-bottom-sheet
{:theme :dark
:content (fn []
[remove-key-pair/view keypair])}])
[remove-keypair/view keypair])}])
[keypair])
on-rename-keypair (rn/use-callback #(rf/dispatch [:open-modal :screen/settings.rename-keypair
keypair])
[keypair])
on-import-seed-phrase (rn/use-callback
#(rf/dispatch [:open-modal :screen/settings.import-seed-phrase keypair])
#(rf/dispatch [:open-modal
:screen/settings.missing-keypair.import-seed-phrase
keypair])
[keypair])
on-import-private-key (rn/use-callback
#(rf/dispatch [:open-modal

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.countdown.view
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.countdown.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.style
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.style
(:require
[quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.view
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
@ -7,8 +7,10 @@
[status-im.common.qr-codes.view :as qr-codes]
[status-im.common.resources :as resources]
[status-im.common.standard-authentication.core :as standard-auth]
[status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.countdown.view :as countdown]
[status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.style :as style]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.countdown.view
:as countdown]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.style
:as style]
[status-im.contexts.syncing.utils :as sync-utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.import-private-key.style)
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-private-key.style)
(def form-container
{:row-gap 8

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.import-private-key.view
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-private-key.view
(:require
[clojure.string :as string]
[quo.core :as quo]
@ -7,7 +7,8 @@
[react-native.safe-area :as safe-area]
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.common.standard-authentication.core :as standard-auth]
[status-im.contexts.settings.wallet.keypairs-and-accounts.import-private-key.style :as style]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-private-key.style
:as style]
[status-im.contexts.wallet.common.validation :as validation]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
@ -68,8 +69,7 @@
[on-change])
on-import-error (rn/use-callback
(fn [_error]
(rf/dispatch [:hide-bottom-sheet])
(show-invalid)))
(rf/dispatch [:hide-bottom-sheet])))
on-import-success (rn/use-callback
(fn []
(rf/dispatch [:hide-bottom-sheet])
@ -134,5 +134,5 @@
(case flow-state
:correct-private-key (i18n/label :t/correct-private-key)
:invalid-private-key (i18n/label :t/invalid-private-key)
:incorrect-private-key (i18n/label :t/incorrect-private-key {:name (:name keypair)})
:incorrect-private-key (i18n/label :t/incorrect-private-key)
nil)])]]]))

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.import-seed-phrase.view
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-seed-phrase.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
@ -14,16 +14,17 @@
container-style
prepare-seed-phrase
seed-phrase
set-invalid-seed-phrase
set-incorrect-seed-phrase
focus-input]}]
(let [keypair (rf/sub [:get-screen-params])
customization-color (rf/sub [:profile/customization-color])
show-errors (rn/use-callback
#(js/setTimeout
(fn []
(focus-input)
(reagent/next-tick set-invalid-seed-phrase))
600))
(fn [_error]
(js/setTimeout
(fn []
(focus-input)
(reagent/next-tick set-incorrect-seed-phrase))
600)))
on-import-error (rn/use-callback
(fn [_error]
(rf/dispatch [:hide-bottom-sheet])
@ -34,7 +35,7 @@
(rf/dispatch [:navigate-back])))
on-auth-success (rn/use-callback
(fn [password]
(rf/dispatch [:wallet/import-keypair-by-seed-phrase
(rf/dispatch [:wallet/import-missing-keypair-by-seed-phrase
{:keypair-key-uid (:key-uid keypair)
:seed-phrase (prepare-seed-phrase seed-phrase)
:password password

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.scan-qr.view
(ns status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.scan-qr.view
(:require
[react-native.core :as rn]
[status-im.common.scan-qr-code.view :as scan-qr-code]

View File

@ -159,9 +159,7 @@
:ens address-or-ens
:ens? ens-name?}])
(rf/dispatch
[:navigate-to-within-stack
[:screen/settings.save-address
:screen/settings.add-address-to-save]]))
[:open-modal :screen/settings.save-address]))
[address ens-name? address-or-ens])]
(rn/use-unmount #(rf/dispatch [:wallet/clear-address-to-save]))
[quo/overlay {:type :shell}

View File

@ -66,8 +66,7 @@
(defn- add-address-to-save
[]
(rf/dispatch [:navigate-to-within-stack
[:screen/settings.add-address-to-save :screen/settings.saved-addresses]]))
(rf/dispatch [:open-modal :screen/settings.add-address-to-save]))
(defn view
[]

View File

@ -8,11 +8,11 @@
(defn open-saved-addresses-settings-modal
[]
(rf/dispatch [:navigate-to-within-stack [:screen/settings.saved-addresses :settings]]))
(rf/dispatch [:open-modal :screen/settings.saved-addresses]))
(defn open-keypairs-and-accounts-settings-modal
[]
(rf/dispatch [:navigate-to-within-stack [:screen/settings.keypairs-and-accounts :settings]]))
(rf/dispatch [:open-modal :screen/settings.keypairs-and-accounts]))
(defn basic-settings-options
[]
@ -38,7 +38,7 @@
(defn open-network-settings-modal
[]
(rf/dispatch [:navigate-to-within-stack [:screen/settings.network-settings :settings]]))
(rf/dispatch [:open-modal :screen/settings.network-settings]))
(defn advanced-settings-options
[]

View File

@ -2,7 +2,6 @@
(:require
[quo.core :as quo]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.contexts.wallet.account.style :as style]
[status-im.contexts.wallet.account.tabs.view :as tabs]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
@ -21,57 +20,59 @@
(not watch-only?) (conj {:id :dapps :label (i18n/label :t/dapps) :accessibility-label :dapps})
true (conj {:id :about :label (i18n/label :t/about) :accessibility-label :about})))
(defn- change-tab [id] (rf/dispatch [:wallet/select-account-tab id]))
(defn view
[]
(let [selected-tab (reagent/atom first-tab-id)]
(fn []
(let [{:keys [name color formatted-balance
watch-only?]} (rf/sub [:wallet/current-viewing-account])
customization-color (rf/sub [:profile/customization-color])]
(rn/use-unmount #(rf/dispatch [:wallet/close-account-page]))
[rn/view {:style {:flex 1}}
[account-switcher/view
{:type :wallet-networks
:on-press #(rf/dispatch [:wallet/close-account-page])}]
[quo/account-overview
{:container-style style/account-overview
:current-value formatted-balance
:account-name name
:account (if watch-only? :watched-address :default)
:customization-color color}]
(when (ff/enabled? ::ff/wallet.graph) [quo/wallet-graph {:time-frame :empty}])
(when (not watch-only?)
[quo/wallet-ctas
{:container-style style/cta-buttons
:send-action (fn []
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/wizard-navigate-forward
{:start-flow? true
:flow-id :wallet-send-flow}]))
:receive-action #(rf/dispatch [:open-modal :screen/wallet.share-address
{:status :receive}])
:buy-action #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])
:bridge-action (fn []
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/wizard-navigate-forward
{:start-flow? true
:flow-id :wallet-bridge-flow}]))
:swap-action (when (ff/enabled? ::ff/wallet.swap)
#(rf/dispatch [:wallet.swap/start]))}])
[quo/tabs
{:style style/tabs
:size 32
:default-active @selected-tab
:data (tabs-data watch-only?)
:on-change (rn/use-callback (fn [tab] (reset! selected-tab tab)))
:scrollable? true
:scroll-on-press? true}]
[tabs/view {:selected-tab @selected-tab}]
(when (ff/enabled? ::ff/shell.jump-to)
[quo/floating-shell-button
{:jump-to
{:on-press #(rf/dispatch [:shell/navigate-to-jump-to])
:customization-color customization-color
:label (i18n/label :t/jump-to)}}
style/shell-button])]))))
(let [selected-tab (or (rf/sub [:wallet/account-tab]) first-tab-id)
{:keys [name color formatted-balance watch-only?
address]} (rf/sub [:wallet/current-viewing-account])
customization-color (rf/sub [:profile/customization-color])]
(rn/use-mount
#(rf/dispatch [:wallet/fetch-activities-for-current-account address]))
[rn/view {:style {:flex 1}}
[account-switcher/view
{:type :wallet-networks
:on-press #(rf/dispatch [:wallet/close-account-page])}]
[quo/account-overview
{:container-style style/account-overview
:current-value formatted-balance
:account-name name
:account (if watch-only? :watched-address :default)
:customization-color color}]
(when (ff/enabled? ::ff/wallet.graph) [quo/wallet-graph {:time-frame :empty}])
(when (not watch-only?)
[quo/wallet-ctas
{:container-style style/cta-buttons
:send-action (fn []
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/wizard-navigate-forward
{:start-flow? true
:flow-id :wallet-send-flow}]))
:receive-action #(rf/dispatch [:open-modal :screen/wallet.share-address
{:status :receive}])
:buy-action #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])
:bridge-action (fn []
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/wizard-navigate-forward
{:start-flow? true
:flow-id :wallet-bridge-flow}]))
:swap-action (when (ff/enabled? ::ff/wallet.swap)
#(rf/dispatch [:wallet.swap/start]))}])
[quo/tabs
{:style style/tabs
:size 32
:active-tab-id selected-tab
:data (tabs-data watch-only?)
:on-change change-tab
:scrollable? true
:scroll-on-press? true}]
[tabs/view {:selected-tab selected-tab}]
(when (ff/enabled? ::ff/shell.jump-to)
[quo/floating-shell-button
{:jump-to
{:on-press #(rf/dispatch [:shell/navigate-to-jump-to])
:customization-color customization-color
:label (i18n/label :t/jump-to)}}
style/shell-button])]))

View File

@ -7,5 +7,4 @@
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :bridge-to-chain-id])))}
{:screen-id :screen/wallet.bridge-input-amount
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :amount])))}
{:screen-id :screen/wallet.transaction-confirmation}
{:screen-id :screen/wallet.transaction-progress}])
{:screen-id :screen/wallet.transaction-confirmation}])

View File

@ -49,6 +49,7 @@
:on-press #(rf/dispatch
[:wallet/set-collectible-to-send
{:collectible collectible
:start-flow? true
:current-screen :screen/wallet.collectible}])}
(i18n/label :t/send)])
[quo/button

View File

@ -35,6 +35,7 @@
:on-press on-press
:accessibility-label accessibility-label
:networks networks
:align-center? true
:networks-on-press #(rf/dispatch [:show-bottom-sheet {:content network-filter/view}])
:right-side [(when (and (ff/enabled? ::ff/wallet.wallet-connect)
(not watch-only?))

View File

@ -0,0 +1,44 @@
(ns status-im.contexts.wallet.common.activity-tab.events
(:require [camel-snake-kebab.extras :as cske]
[utils.ethereum.chain :as chain]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
(rf/reg-event-fx
:wallet/fetch-activities-for-current-account
(fn [{:keys [db]}]
(let [address (-> db :wallet :current-viewing-account-address)
chain-ids (chain/chain-ids db)
request-id 0
filters {:period {:startTimestamp 0
:endTimestamp 0}
:types []
:statuses []
:counterpartyAddresses []
:assets []
:collectibles []
:filterOutAssets false
:filterOutCollectibles false}
offset 0
limit 35
request-params [request-id [address] chain-ids filters offset limit]]
{:fx [[:json-rpc/call
[{;; This method is deprecated and will be replaced by
;; "wallet_startActivityFilterSession"
;; https://github.com/status-im/status-mobile/issues/19864
:method "wallet_filterActivityAsync"
:params request-params
:on-error [:wallet/log-rpc-error
{:event :wallet/fetch-activities-for-current-account
:params request-params}]}]]]})))
(rf/reg-event-fx
:wallet/activity-filtering-for-current-account-done
(fn [{:keys [db]} [{:keys [message]}]]
(let [address (-> db :wallet :current-viewing-account-address)
activities (->> message
(transforms/json->clj)
(:activities)
(cske/transform-keys transforms/->kebab-case-keyword))
sorted-activities (sort :timestamp activities)]
{:db (assoc-in db [:wallet :activities address] sorted-activities)})))

View File

@ -1,61 +1,42 @@
(ns status-im.contexts.wallet.common.activity-tab.view
(:require
[legacy.status-im.utils.hex :as utils.hex]
[native-module.core :as native-module]
[quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[quo.theme]
[react-native.core :as rn]
[status-im.common.resources :as resources]
[status-im.contexts.shell.jump-to.constants :as jump-to.constants]
[status-im.contexts.wallet.common.activity-tab.constants :as constants]
[status-im.contexts.wallet.common.empty-tab.view :as empty-tab]
[utils.datetime :as datetime]
[utils.ethereum.chain :as chain]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))
(def precision 6)
(defn send-and-receive-activity
[{:keys [transaction relative-date status sender recipient token amount network-name
network-logo]}]
[quo/wallet-activity
{:transaction transaction
:timestamp relative-date
:status status
:counter 1
:first-tag {:size 24
:type :token
:token token
:amount amount}
:second-tag-prefix :t/from
:second-tag {:type :address :address sender}
:third-tag-prefix :t/to
:third-tag {:type :address :address recipient}
:fourth-tag-prefix :t/via
:fourth-tag {:size 24
:type :network
:network-name network-name
:network-logo network-logo}
:blur? false}])
(defn activity-item
[{:keys [activity-type activity-status timestamp symbol-out symbol-in token-in token-out amount-in
amount-out sender recipient]}]
(let [chain-id (or (:chain-id token-in) (:chain-id token-out))
amount-in-units (native-module/hex-to-number
(utils.hex/normalize-hex amount-in))
amount-in-value (str (money/with-precision
(money/wei->ether amount-in-units)
precision))
amount-out-units (native-module/hex-to-number
(utils.hex/normalize-hex amount-out))
amount-out-value (str (money/with-precision
(money/wei->ether amount-out-units)
precision))
relative-date (datetime/timestamp->relative (* timestamp 1000))
receiving-activity? (= activity-type constants/wallet-activity-type-receive)]
[quo/wallet-activity
{:transaction (constants/wallet-activity-id->name activity-type)
:timestamp relative-date
:status (constants/wallet-activity-status->name activity-status)
:counter 1
:first-tag {:size 24
:type :token
:token (or symbol-out symbol-in)
:amount (if receiving-activity? amount-in-value amount-out-value)}
:second-tag-prefix (constants/second-tag-prefix activity-type)
:second-tag {:type :address
:address (if receiving-activity? recipient sender)}
:third-tag-prefix (constants/third-tag-prefix activity-type)
:third-tag {:type :address
:address (if receiving-activity? sender recipient)}
:fourth-tag-prefix (constants/fourth-tag-prefix activity-type)
:fourth-tag {:size 24
:type :network
:network-logo (quo.resources/get-network (chain/chain-id->chain-keyword
chain-id))
:network-name (chain/chain-id->chain-name chain-id)}
:blur? false}]))
[{:keys [transaction] :as activity}]
(case transaction
(:send :receive) [send-and-receive-activity activity]
nil))
(defn view
[]
@ -69,7 +50,8 @@
[rn/section-list
{:sections activity-list
:sticky-section-headers-enabled false
:style {:flex 1}
:style {:flex 1
:padding-horizontal 8}
:content-container-style {:padding-bottom jump-to.constants/floating-shell-button-height}
:render-fn activity-item
:render-section-header-fn (fn [{:keys [title]}] [quo/divider-date title])}])))

View File

@ -72,8 +72,10 @@
selected-account? (rf/sub [:wallet/current-viewing-account-address])
send-params (if selected-account?
{:token token-data
:stack-id :screen/wallet.accounts
:start-flow? true}
{:token-symbol token-symbol
:stack-id :wallet-stack
:start-flow? true})]
[quo/action-drawer
[(cond->> [(when (ff/enabled? ::ff/wallet.assets-modal-manage-tokens)

View File

@ -79,7 +79,7 @@
:on-success (fn [value]
(resolver {:value value}))}))))
(defn import-keypair-by-seed-phrase
(defn import-missing-keypair-by-seed-phrase
[keypair-key-uid seed-phrase password]
(-> (validate-mnemonic seed-phrase)
(promesa/then
@ -87,20 +87,20 @@
(if (not= keypair-key-uid key-uid)
(promesa/rejected
(ex-info
(error-message :import-keypair-by-seed-phrase/import-error)
(error-message :import-missing-keypair-by-seed-phrase/import-error)
{:hint :incorrect-seed-phrase-for-keypair}))
(make-seed-phrase-fully-operable seed-phrase password))))
(promesa/catch
(fn [error]
(promesa/rejected
(ex-info
(error-message :import-keypair-by-seed-phrase/import-error)
(error-message :import-missing-keypair-by-seed-phrase/import-error)
(ex-data error)))))))
(rf/reg-fx
:import-keypair-by-seed-phrase
:effects.wallet/import-missing-keypair-by-seed-phrase
(fn [{:keys [keypair-key-uid seed-phrase password on-success on-error]}]
(-> (import-keypair-by-seed-phrase keypair-key-uid seed-phrase password)
(-> (import-missing-keypair-by-seed-phrase keypair-key-uid seed-phrase password)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))

View File

@ -6,6 +6,7 @@
[status-im.constants :as constants]
[status-im.contexts.settings.wallet.effects]
[status-im.contexts.settings.wallet.events]
[status-im.contexts.wallet.common.activity-tab.events]
[status-im.contexts.wallet.common.utils.external-links :as external-links]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.data-store :as data-store]
@ -35,6 +36,12 @@
(fn [{:keys [db]} [address]]
{:db (assoc-in db [:wallet :current-viewing-account-address] address)
:fx [[:dispatch [:navigate-to :screen/wallet.accounts address]]
[:dispatch [:wallet/fetch-activities-for-current-account]]]}))
(rf/reg-event-fx :wallet/navigate-to-account-within-stack
(fn [{:keys [db]} [address]]
{:db (assoc-in db [:wallet :current-viewing-account-address] address)
:fx [[:dispatch [:navigate-to-within-stack [:screen/wallet.accounts :shell-stack] address]]
[:dispatch [:wallet/fetch-activities]]]}))
(rf/reg-event-fx :wallet/navigate-to-new-account
@ -44,6 +51,14 @@
[:dispatch [:navigate-to :screen/wallet.accounts address]]
[:dispatch [:wallet/show-account-created-toast address]]]}))
(rf/reg-event-fx :wallet/select-account-tab
(fn [{:keys [db]} [tab]]
{:db (assoc-in db [:wallet :ui :account-page :active-tab] tab)}))
(rf/reg-event-fx :wallet/clear-account-tab
(fn [{:keys [db]}]
{:db (assoc-in db [:wallet :ui :account-page :active-tab] nil)}))
(rf/reg-event-fx :wallet/switch-current-viewing-account
(fn [{:keys [db]} [address]]
{:db (assoc-in db [:wallet :current-viewing-account-address] address)}))
@ -55,6 +70,7 @@
(rf/reg-event-fx :wallet/close-account-page
(fn [_]
{:fx [[:dispatch [:wallet/clean-current-viewing-account]]
[:dispatch [:wallet/clear-account-tab]]
[:dispatch [:pop-to-root :shell-stack]]]}))
(defn log-rpc-error
@ -491,49 +507,6 @@
(rf/reg-event-fx :wallet/update-selected-networks update-selected-networks)
(rf/reg-event-fx
:wallet/fetch-activities
(fn [{:keys [db]}]
(let [addresses (->> (get-in db [:wallet :accounts])
vals
(map :address))
chain-ids (chain/chain-ids db)
request-id 0
filters {:period {:startTimestamp 0
:endTimestamp 0}
:types []
:statuses []
:counterpartyAddresses []
:assets []
:collectibles []
:filterOutAssets false
:filterOutCollectibles false}
offset 0
limit 20
request-params [request-id
addresses
chain-ids
filters
offset
limit]]
{:fx [[:json-rpc/call
[{;; This method is deprecated and will be replaced by
;; "wallet_startActivityFilterSession"
;; https://github.com/status-im/status-mobile/issues/19864
:method "wallet_filterActivityAsync"
:params request-params
:on-error [:wallet/log-rpc-error
{:event :wallet/fetch-activities
:params request-params}]}]]]})))
(rf/reg-event-fx
:wallet/activity-filtering-done
(fn [{:keys [db]} [{:keys [message]}]]
(let [{:keys [activities]} (transforms/json->clj message)
activities (cske/transform-keys transforms/->kebab-case-keyword activities)
sorted-activities (sort :timestamp activities)]
{:db (assoc-in db [:wallet :activities] sorted-activities)})))
(rf/reg-event-fx
:wallet/get-crypto-on-ramps-success
(fn [{:keys [db]} [data]]

View File

@ -253,7 +253,7 @@
(rf/reg-event-fx
:wallet/set-collectible-to-send
(fn [{db :db} [{:keys [collectible current-screen]}]]
(fn [{db :db} [{:keys [collectible current-screen start-flow?]}]]
(let [collection-data (:collection-data collectible)
collectible-data (:collectible-data collectible)
contract-type (:contract-type collectible)
@ -282,6 +282,7 @@
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen current-screen
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]})))
(rf/reg-event-fx
@ -447,18 +448,26 @@
(assoc-in [:wallet :transactions] transaction-details)
(assoc-in [:wallet :ui :send :transaction-ids] transaction-ids))
:fx [[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen :screen/wallet.transaction-confirmation
:flow-id :wallet-send-flow}]]]})))
[:wallet/end-transaction-flow]]]})))
(rf/reg-event-fx :wallet/close-transaction-progress-page
(rf/reg-event-fx :wallet/clean-up-transaction-flow
(fn [_]
{:fx [[:dispatch [:wallet/clean-scanned-address]]
{:fx [[:dispatch [:dismiss-modal :screen/wallet.transaction-confirmation]]
[:dispatch [:wallet/clean-scanned-address]]
[:dispatch [:wallet/clean-local-suggestions]]
[:dispatch [:wallet/clean-send-address]]
[:dispatch [:wallet/clean-disabled-from-networks]]
[:dispatch [:wallet/select-address-tab nil]]
[:dispatch [:dismiss-modal :screen/wallet.transaction-progress]]]}))
[:dispatch [:wallet/select-address-tab nil]]]}))
(rf/reg-event-fx :wallet/end-transaction-flow
(fn [{:keys [db]}]
(let [address (get-in db [:wallet :current-viewing-account-address])]
{:fx [[:dispatch [:wallet/navigate-to-account-within-stack address]]
[:dispatch [:wallet/fetch-activities]]
[:dispatch [:wallet/select-account-tab :activity]]
[:dispatch-later
[{:ms 20
:dispatch [:wallet/clean-up-transaction-flow]}]]]})))
(defn- transaction-data
[{:keys [from-address to-address token-address route data eth-transfer?]}]

View File

@ -27,5 +27,4 @@
:skip-step? (fn [db]
(or (not (collectible-selected? db))
(some? (get-in db [:wallet :ui :send :amount]))))}
{:screen-id :screen/wallet.transaction-confirmation}
{:screen-id :screen/wallet.transaction-progress}])
{:screen-id :screen/wallet.transaction-confirmation}])

View File

@ -9,7 +9,7 @@
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- on-press
(defn- on-account-press
[address network-details]
(rf/dispatch [:wallet/select-from-account
{:address address
@ -22,15 +22,19 @@
(rf/dispatch [:navigate-back]))
(defn- render-fn
[item]
(let [network-details (rf/sub [:wallet/network-details])]
[item _ _ {:keys [network-details]}]
(let [transformed-address (rf/sub [:wallet/account-address (:address item)
(:network-preferences-names item)])]
[quo/account-item
{:on-press #(on-press (:address item) network-details)
:account-props item}]))
{:on-press #(on-account-press (:address item) network-details)
:account-props (assoc item
:address transformed-address
:full-address? true)}]))
(defn view
[]
(let [accounts (rf/sub [:wallet/accounts-with-current-asset])]
(let [accounts (rf/sub [:wallet/accounts-with-current-asset])
network-details (rf/sub [:wallet/network-details])]
[floating-button-page/view
{:footer-container-padding 0
:header [account-switcher/view
@ -45,5 +49,6 @@
{:style style/accounts-list
:content-container-style style/accounts-list-container
:data accounts
:render-data {:network-details network-details}
:render-fn render-fn
:shows-horizontal-scroll-indicator false}]]))

View File

@ -7,11 +7,13 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.controlled-input.utils :as controlled-input]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.asset-list.view :as asset-list]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.send.input-amount.style :as style]
[status-im.contexts.wallet.send.routes.view :as routes]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.unpreferred-networks-alert.view :as unpreferred-networks-alert]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
@ -125,6 +127,15 @@
:style {:margin-top 15}}
(i18n/label :t/no-routes-found)]])
(defn- not-enough-asset
[]
[quo/alert-banner
{:action? true
:text (i18n/label :t/not-enough-assets)
:button-text (i18n/label :t/buy-eth)
:on-button-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])}])
(defn view
;; crypto-decimals, limit-crypto and initial-crypto-currency? args are needed
;; for component tests only
@ -264,7 +275,25 @@
limit-insufficient? (> (controlled-input/numeric-value input-state)
current-limit)
should-try-again? (and (not limit-insufficient?) no-routes-found?)
current-address (rf/sub [:wallet/current-viewing-account-address])]
current-address (rf/sub [:wallet/current-viewing-account-address])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case
constants/mainnet-short-name)
enabled-from-chain-ids])
not-enough-asset? (and
(or no-routes-found? limit-insufficient?)
(not-empty sender-network-values)
(if (= token-symbol
(string/upper-case
constants/mainnet-short-name))
(= current-limit input-amount)
(money/equal-to (:total-balance
owned-eth-token)
0)))
show-no-routes? (and
(or no-routes-found? limit-insufficient?)
(not-empty sender-network-values)
(not not-enough-asset?))]
(rn/use-mount
(fn []
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
@ -339,8 +368,9 @@
{:loading-routes? loading-routes?
:fees fee-formatted
:amount amount-text}])
(when (and (or no-routes-found? limit-insufficient?) (not-empty sender-network-values))
[no-routes-found])
(cond
show-no-routes? [no-routes-found]
not-enough-asset? [not-enough-asset])
[quo/bottom-actions
{:actions :one-action
:button-one-label (if should-try-again?

View File

@ -17,15 +17,21 @@
:description (i18n/label :t/here-is-a-cat-in-a-box-instead)
:image (resources/get-themed-image :cat-in-box theme)
:container-style style/empty-container-style}]
(into [rn/view {:style style/my-accounts-container}]
(map (fn [{:keys [color address] :as account}]
[quo/account-item
{:account-props (assoc account :customization-color color)
:on-press #(rf/dispatch [:wallet/select-send-address
{:address address
:recipient account
:stack-id :screen/wallet.select-address}])}]))
other-accounts))))
[rn/view {:style style/my-accounts-container}
(doall
(for [{:keys [color address] :as account} other-accounts]
^{:key (str address)}
(let [transformed-address (rf/sub [:wallet/account-address address
(:network-preferences-names account)])]
[quo/account-item
{:account-props (assoc account
:customization-color color
:address transformed-address
:full-address? true)
:on-press #(rf/dispatch [:wallet/select-send-address
{:address address
:recipient account
:stack-id :screen/wallet.select-address}])}])))])))
(defn- recent-transactions
[theme]
@ -46,16 +52,56 @@
:stack-id :screen/wallet.select-address}])}]))
recent-recipients))))
(defn- saved-address
[{:keys [name address chain-short-names customization-color ens? ens]}]
(let [full-address (str chain-short-names address)
on-press-saved-address (rn/use-callback
#(rf/dispatch
[:wallet/select-send-address
{:address full-address
:recipient full-address
:stack-id :screen/wallet.select-address}])
[full-address])]
[quo/saved-address
{:user-props {:name name
:address full-address
:ens (when ens? ens)
:customization-color customization-color}
:container-style {:margin-horizontal 8}
:on-press on-press-saved-address}]))
(defn- saved-addresses
[theme]
(let [group-saved-addresses (rf/sub [:wallet/grouped-saved-addresses])
section-header (rn/use-callback
(fn [{:keys [title index]}]
[quo/divider-label
{:tight? true
:container-style (when (pos? index) {:margin-top 8})}
title]))
empty-state-component (rn/use-memo
(fn []
[quo/empty-state
{:title (i18n/label :t/no-saved-addresses)
:description (i18n/label
:t/you-like-to-type-43-characters)
:image (resources/get-themed-image :sweating-man
theme)}])
[theme])]
[rn/section-list
{:key-fn :title
:shows-vertical-scroll-indicator false
:render-section-header-fn section-header
:sections group-saved-addresses
:render-fn saved-address
:empty-component empty-state-component}]))
(defn view
[{:keys [selected-tab]}]
(let [theme (quo.theme/use-theme)]
(case selected-tab
:tab/recent [recent-transactions theme]
:tab/saved [quo/empty-state
{:title (i18n/label :t/no-saved-addresses)
:description (i18n/label :t/you-like-to-type-43-characters)
:image (resources/get-themed-image :sweating-man theme)
:container-style style/empty-container-style}]
:tab/saved [saved-addresses theme]
:tab/contacts [quo/empty-state
{:title (i18n/label :t/no-contacts)
:description (i18n/label :t/no-contacts-description)

View File

@ -47,7 +47,7 @@
(defn view
[]
(let [leave-page #(rf/dispatch [:wallet/close-transaction-progress-page])
(let [leave-page #(rf/dispatch [:wallet/end-transaction-flow])
{:keys [color]} (rf/sub [:wallet/current-viewing-account])]
(fn []
(rn/use-effect

View File

@ -37,7 +37,8 @@
"wallet-blockchain-status-changed" {:fx [[:dispatch
[:wallet/blockchain-status-changed
(transforms/js->clj event-js)]]]}
"wallet-activity-filtering-done" {:fx [[:dispatch
[:wallet/activity-filtering-done
(transforms/js->clj event-js)]]]}
"wallet-activity-filtering-done" {:fx
[[:dispatch
[:wallet/activity-filtering-for-current-account-done
(transforms/js->clj event-js)]]]}
(log/debug ::unknown-wallet-event :type event-type)))))

View File

@ -27,8 +27,8 @@
(rf/defn navigate-to-within-stack
{:events [:navigate-to-within-stack]}
[{:keys [db]} comp-id]
{:db (assoc db :view-id (first comp-id))
[{:keys [db]} comp-id screen-params]
{:db (all-screens-params db (first comp-id) screen-params)
:fx [[:navigate-to-within-stack comp-id]]})
(re-frame/reg-event-fx :open-modal

View File

@ -57,14 +57,15 @@
[status-im.contexts.profile.settings.screens.password.change-password.view :as change-password]
[status-im.contexts.profile.settings.screens.password.view :as settings-password]
[status-im.contexts.profile.settings.view :as settings]
[status-im.contexts.settings.wallet.keypairs-and-accounts.encrypted-qr.view :as
encrypted-key-pair-qr]
[status-im.contexts.settings.wallet.keypairs-and-accounts.import-private-key.view :as
import-private-key]
[status-im.contexts.settings.wallet.keypairs-and-accounts.import-seed-phrase.view :as
import-seed-phrase]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.encrypted-qr.view
:as encrypted-keypair-qr]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-private-key.view
:as missing-keypairs.import-private-key]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.import-seed-phrase.view
:as missing-keypairs.import-seed-phrase]
[status-im.contexts.settings.wallet.keypairs-and-accounts.missing-keypairs.scan-qr.view
:as scan-keypair-qr]
[status-im.contexts.settings.wallet.keypairs-and-accounts.rename.view :as keypair-rename]
[status-im.contexts.settings.wallet.keypairs-and-accounts.scan-qr.view :as scan-keypair-qr]
[status-im.contexts.settings.wallet.keypairs-and-accounts.view :as keypairs-and-accounts]
[status-im.contexts.settings.wallet.network-settings.view :as network-settings]
[status-im.contexts.settings.wallet.saved-addresses.add-address-to-save.view :as
@ -535,9 +536,9 @@
:options (assoc options/dark-screen :sheet? true)
:component keypair-rename/view}
{:name :screen/settings.encrypted-key-pair-qr
{:name :screen/settings.encrypted-keypair-qr
:options options/transparent-screen-options
:component encrypted-key-pair-qr/view}
:component encrypted-keypair-qr/view}
{:name :screen/settings.saved-addresses
:options options/transparent-modal-screen-options
@ -551,13 +552,13 @@
:options options/transparent-modal-screen-options
:component scan-keypair-qr/view}
{:name :screen/settings.import-seed-phrase
{:name :screen/settings.missing-keypair.import-seed-phrase
:options options/transparent-screen-options
:component import-seed-phrase/view}
:component missing-keypairs.import-seed-phrase/view}
{:name :screen/settings.missing-keypair-import-private-key
:options options/transparent-screen-options
:component import-private-key/view}
:component missing-keypairs.import-private-key/view}
{:name :screen/settings.network-settings
:options options/transparent-modal-screen-options

View File

@ -1,26 +1,82 @@
(ns status-im.subs.wallet.activities
(:require
[legacy.status-im.utils.hex :as utils.hex]
[native-module.core :as native-module]
[quo.foundations.resources :as quo.resources]
[quo.foundations.resources]
[re-frame.core :as rf]
[status-im.contexts.wallet.common.activity-tab.constants :as constants]
[utils.datetime :as datetime]))
[utils.datetime :as datetime]
[utils.money :as money]))
(def precision 6)
(rf/reg-sub
:wallet/all-activities
:<- [:wallet]
:-> :activities)
(rf/reg-sub :wallet/activities-for-current-viewing-account
(defn- activity-amount
[amount]
(-> amount
(utils.hex/normalize-hex)
(native-module/hex-to-number)
(money/wei->ether)
(money/with-precision precision)
(str)))
(defn- process-send-activity
[{:keys [symbol-out chain-id-out amount-out]} activity chain-id->network-name]
(let [network-name (chain-id->network-name chain-id-out)]
(assoc activity
:transaction :send
:token symbol-out
:amount (activity-amount amount-out)
:network-name network-name
:network-logo (quo.resources/get-network network-name))))
(defn- process-receive-activity
[{:keys [symbol-in amount-in chain-id-in]} activity chain-id->network-name]
(let [network-name (chain-id->network-name chain-id-in)]
(assoc activity
:transaction :receive
:token symbol-in
:amount (activity-amount amount-in)
:network-name network-name
:network-logo (quo.resources/get-network network-name))))
(defn- process-activity-by-type
[chain-id->network-name
{:keys [activity-type activity-status timestamp sender recipient] :as data}]
(let [activity {:relative-date (datetime/timestamp->relative (* timestamp 1000))
:timestamp timestamp
:status (constants/wallet-activity-status->name activity-status)
:sender sender
:recipient recipient}]
(condp = activity-type
constants/wallet-activity-type-send
(process-send-activity data activity chain-id->network-name)
constants/wallet-activity-type-receive
(process-receive-activity data activity chain-id->network-name)
nil)))
(rf/reg-sub
:wallet/activities-for-current-viewing-account
:<- [:wallet/all-activities]
:<- [:wallet/current-viewing-account-address]
(fn [[activities current-viewing-account-address]]
(->> activities
(filter (fn [{:keys [sender recipient activity-type]}]
(let [receiving-activity? (= activity-type constants/wallet-activity-type-receive)
relevant-address (if receiving-activity? recipient sender)]
(= relevant-address current-viewing-account-address))))
(distinct)
(group-by (fn [{:keys [timestamp]}]
(datetime/timestamp->relative-short-date (* timestamp 1000))))
(map (fn [[date activities]]
{:title date :data activities :timestamp (:timestamp (first activities))}))
(sort-by (fn [{:keys [timestamp]}] (- timestamp))))))
:<- [:wallet/network-details]
(fn [[activities current-viewing-account-address network-details]]
(let [chain-id->network-name (update-vals (group-by :chain-id network-details)
(comp :network-name first))]
(->> current-viewing-account-address
(get activities)
(keep #(process-activity-by-type chain-id->network-name %))
(group-by (fn [{:keys [timestamp]}]
(datetime/timestamp->relative-short-date (* timestamp 1000))))
(map (fn [[date activities]]
{:title date
:data activities
:timestamp (:timestamp (first activities))}))
(sort-by (fn [{:keys [timestamp]}] (- timestamp)))))))

View File

@ -2,8 +2,9 @@
(:require
[cljs.test :refer [is testing]]
[re-frame.db :as rf-db]
status-im.subs.root
status-im.subs.wallet.collectibles
[status-im.contexts.wallet.common.activity-tab.constants :as constants]
[status-im.subs.root]
[status-im.subs.wallet.collectibles]
[test-helpers.unit :as h]
[utils.re-frame :as rf]))
@ -14,7 +15,7 @@
[:wallet :activities]
[{:id 1 :name "Transaction1"}
{:id 2 :name "Transaction2"}])
(is (= [{:id 1 :name "Transaction1"} {:id 2 :name "Transaction2"}] (rf/sub [sub-name])))))
(is (match? [{:id 1 :name "Transaction1"} {:id 2 :name "Transaction2"}] (rf/sub [sub-name])))))
(h/deftest-sub :wallet/activities-for-current-viewing-account
[sub-name]
@ -23,11 +24,62 @@
(fn [db]
(-> db
(assoc-in [:wallet :activities]
[{:sender "acc1" :recipient "acc2" :timestamp 1588291200}
{:sender "acc2" :recipient "acc1" :timestamp 1588377600}
{:sender "acc3" :recipient "acc4" :timestamp 1588464000}])
{"acc1" [{:activity-type constants/wallet-activity-type-send
:amount-out "0x1"
:sender "acc1"
:recipient "acc2"
:timestamp 1588291200}
{:activity-type constants/wallet-activity-type-receive
:amount-in "0x1"
:sender "acc2"
:recipient "acc1"
:timestamp 1588377600}
{:activity-type constants/wallet-activity-type-send
:amount-out "0x1"
:sender "acc1"
:recipient "acc4"
:timestamp 1588464000}]
"acc3" [{:activity-type constants/wallet-activity-type-receive
:amount-in "0x1"
:sender "acc4"
:recipient "acc3"
:timestamp 1588464000}]})
(assoc-in [:wallet :current-viewing-account-address] "acc1"))))
(is (= [{:title "May 1, 2020"
:data [{:sender "acc1" :recipient "acc2" :timestamp 1588291200}]
:timestamp 1588291200}]
(rf/sub [sub-name])))))
(is
(match? [{:title "May 3, 2020"
:timestamp 1588464000
:data [{:relative-date "May 3, 2020"
:amount "0"
:network-logo nil
:recipient "acc4"
:transaction :send
:token nil
:network-name nil
:status nil
:sender "acc1"
:timestamp 1588464000}]}
{:title "May 2, 2020"
:timestamp 1588377600
:data [{:relative-date "May 2, 2020"
:amount "0"
:network-logo nil
:recipient "acc1"
:transaction :receive
:token nil
:network-name nil
:status nil
:sender "acc2"
:timestamp 1588377600}]}
{:title "May 1, 2020"
:timestamp 1588291200
:data [{:relative-date "May 1, 2020"
:amount "0"
:network-logo nil
:recipient "acc2"
:transaction :send
:token nil
:network-name nil
:status nil
:sender "acc1"
:timestamp 1588291200}]}]
(rf/sub [sub-name])))))

View File

@ -1,7 +1,10 @@
(ns status-im.subs.wallet.networks
(:require [quo.foundations.resources :as resources]
[re-frame.core :as re-frame]
[status-im.constants :as constants]))
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils.networks :as network-utils]))
(def max-network-prefixes 2)
(re-frame/reg-sub
:wallet/networks
@ -88,3 +91,13 @@
(filter
#(contains? selected-networks (:network-name %))
network-details)))
(re-frame/reg-sub
:wallet/account-address
(fn [_ [_ address network-preferences]]
(let [short-names (map network-utils/network->short-name network-preferences)
prefix (when (<= (count short-names) max-network-prefixes)
(network-utils/short-names->network-preference-prefix
short-names))
transformed-address (str prefix address)]
transformed-address)))

View File

@ -82,3 +82,14 @@
:chain-id 10
:layer 2}}
(rf/sub [sub-name])))))
(h/deftest-sub :wallet/account-address
[sub-name]
(testing
"returns the address with prefixes when an address and less than 3 network preferences are passed"
(is
(match? "eth:0x01" (rf/sub [sub-name "0x01" [:ethereum]]))))
(testing
"returns the address without the prefixes when an address and equal or more than 3 network preferences are passed"
(is
(match? "0x01" (rf/sub [sub-name "0x01" [:ethereum :optimism :arbitrum]])))))

View File

@ -1,6 +1,7 @@
(ns status-im.subs.wallet.send
(:require
[re-frame.core :as rf]
[status-im.contexts.wallet.common.activity-tab.constants :as constants]
[utils.number]))
(rf/reg-sub
@ -40,14 +41,15 @@
(rf/reg-sub
:wallet/recent-recipients
:<- [:wallet/activities-for-current-viewing-account]
:<- [:wallet/all-activities]
:<- [:wallet/current-viewing-account-address]
(fn [[sections current-viewing-account-address]]
(let [all-transactions (mapcat :data sections)
users-sent-transactions (filter (fn [{:keys [sender]}]
(= sender current-viewing-account-address))
all-transactions)]
(set (map :recipient users-sent-transactions)))))
(fn [[all-activities current-viewing-account-address]]
(let [address-activity (get all-activities current-viewing-account-address)]
(->> address-activity
(keep (fn [{:keys [activity-type recipient]}]
(when (= constants/wallet-activity-type-send activity-type)
recipient)))
(distinct)))))
(rf/reg-sub
:wallet/send-token-not-supported-in-receiver-networks?

View File

@ -2,8 +2,9 @@
(:require
[cljs.test :refer [is testing]]
[re-frame.db :as rf-db]
status-im.subs.root
status-im.subs.wallet.send
[status-im.contexts.wallet.common.activity-tab.constants :as constants]
[status-im.subs.root]
[status-im.subs.wallet.send]
[test-helpers.unit :as h]
[utils.re-frame :as rf]))
@ -61,8 +62,25 @@
(fn [db]
(-> db
(assoc-in [:wallet :activities]
[{:sender "acc1" :recipient "acc2" :timestamp 1588291200}
{:sender "acc2" :recipient "acc1" :timestamp 1588377600}
{:sender "acc3" :recipient "acc4" :timestamp 1588464000}])
{"acc1" [{:activity-type constants/wallet-activity-type-send
:amount-out "0x1"
:sender "acc1"
:recipient "acc2"
:timestamp 1588291200}
{:activity-type constants/wallet-activity-type-receive
:amount-in "0x1"
:sender "acc2"
:recipient "acc1"
:timestamp 1588377600}
{:activity-type constants/wallet-activity-type-send
:amount-out "0x1"
:sender "acc1"
:recipient "acc4"
:timestamp 1588464000}]
"acc3" [{:activity-type constants/wallet-activity-type-receive
:amount-in "0x1"
:sender "acc4"
:recipient "acc3"
:timestamp 1588464000}]})
(assoc-in [:wallet :current-viewing-account-address] "acc1"))))
(is (= #{"acc2"} (rf/sub [sub-name])))))
(is (match? ["acc2" "acc4"] (rf/sub [sub-name])))))

View File

@ -259,22 +259,22 @@
:wallet/settings-keypairs-accounts
:<- [:wallet/keypairs]
(fn [keypairs [_ format-options]]
(let [grouped-keypairs (group-by :lowest-operability keypairs)
operable-key-pair-ids (->> (concat (:fully grouped-keypairs)
(:partially grouped-keypairs))
(map :key-uid)
(into #{}))
missing-key-pair-ids (->> (map :key-uid (:no grouped-keypairs))
(into #{}))]
(let [grouped-keypairs (group-by :lowest-operability keypairs)
operable-keypair-ids (->> (concat (:fully grouped-keypairs)
(:partially grouped-keypairs))
(map :key-uid)
(into #{}))
missing-keypair-ids (->> (map :key-uid (:no grouped-keypairs))
(into #{}))]
{:operable (->> keypairs
(filter #(contains? operable-key-pair-ids (:key-uid %)))
(filter #(contains? operable-keypair-ids (:key-uid %)))
(map (fn [{:keys [accounts name type key-uid]}]
{:type (keyword type)
:name name
:key-uid key-uid
:accounts (format-settings-keypair-accounts accounts format-options)})))
:missing (->> keypairs
(filter #(contains? missing-key-pair-ids (:key-uid %)))
(filter #(contains? missing-keypair-ids (:key-uid %)))
(map (fn [{:keys [accounts name type key-uid]}]
{:type (keyword type)
:name name
@ -435,6 +435,12 @@
accounts)
accounts))))
(rf/reg-sub
:wallet/account-tab
:<- [:wallet/ui]
(fn [ui]
(get-in ui [:account-page :active-tab])))
(rf/reg-sub
:wallet/current-viewing-account-token-values
:<- [:wallet/current-viewing-account]

View File

@ -1587,6 +1587,7 @@
"seed-phrase-words-uppercase": "Recovery phrase cannot contain uppercase characters",
"seed-phrase-error": "Recovery phrase contains invalid words",
"seed-phrase-invalid": "Invalid recovery phrase",
"seed-phrase-incorrect": "Recovery phrase does not match key pair",
"seed-phrase-info": "Enter 12, 18 or 24 words separated by spaces",
"word-count": "Word count",
"word-n": "Word #{{number}}",
@ -1629,6 +1630,7 @@
"ok-save-pass": "OK, save password",
"lock-app-with": "Lock app with",
"grant-face-id-permissions": "To grant the required Face ID permission, please go to your system settings and make sure that Status > Face ID is selected",
"grant-fingerprints-permissions": "To grant the required fingerprints permission, please go to your system settings and make sure that Status > Fingerprints is selected",
"request-feature": "Request a feature",
"select-account-dapp": "Select the account you wish to use with Dapps",
"apply": "Apply",
@ -2658,7 +2660,7 @@
"import-private-key-info": "New addresses cannot be derived from an account imported from a private key. Import using a seed phrase if you wish to derive addresses.",
"invalid-private-key": "Its not a valid private key",
"correct-private-key": "Correct private key",
"incorrect-private-key": "This is not the private key for {{name}}",
"incorrect-private-key": "This is not the private key for this key pair",
"private-key-public-address": "Public address of private key",
"this-account-has-no-activity": "This account has no activity",
"this-address-has-activity": "This address has activity",
@ -2699,5 +2701,7 @@
"wallet-connect-version-not-supported": "WalletConnect version {{version}} is not supported",
"add-network-preferences": "Add network preferences",
"saved-address-network-preference-selection-description": "Only change if you know which networks the address owner is happy to to receive funds on",
"add-preferences": "Add preferences"
"add-preferences": "Add preferences",
"buy-eth": "Buy ETH",
"not-enough-assets": "Not enough assets to pay gas fees"
}