Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5cbb79bc7 | ||
|
|
05a810b051 | ||
|
|
7db35a3cc8 | ||
|
|
1301bbc0d9 | ||
|
|
5f4d4bfc11 | ||
|
|
26dd51c5da | ||
|
|
bd7ced5f25 | ||
|
|
61f8c51d85 | ||
|
|
781b83d13d | ||
|
|
b7bffb3bd3 | ||
|
|
047e45d2a3 | ||
|
|
acc6a3c072 | ||
|
|
7171c7085d | ||
|
|
6d227b61e1 | ||
|
|
bf1a734c4e |
@@ -34,3 +34,4 @@ STICKERS_TEST_ENABLED=1
|
||||
LOCAL_PAIRING_ENABLED=1
|
||||
TEST_STATEOFUS=1
|
||||
FAST_CREATE_COMMUNITY_ENABLED=1
|
||||
FLAG_NEW_CONTACT_UI_ENABLED=0
|
||||
|
||||
@@ -359,7 +359,7 @@ test-watch-for-repl: ##@test Watch all Clojure tests and support REPL connection
|
||||
"until [ -f $$SHADOW_OUTPUT_TO ] ; do sleep 1 ; done ; node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO --repl"
|
||||
|
||||
test-unit: export SHADOW_OUTPUT_TO := target/unit_test/test.js
|
||||
test-unit: export SHADOW_NS_REGEXP := ^(?!tests\.integration-test)(?!tests-im\.contract-test).*-test$$
|
||||
test-unit: export SHADOW_NS_REGEXP := ^(?!tests\.integration-test)(?!tests\.contract-test).*-test$$
|
||||
test-unit: ##@test Run unit tests
|
||||
test-unit: _test-clojure
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ pipeline {
|
||||
buildDiscarder(logRotator(
|
||||
numToKeepStr: '10',
|
||||
daysToKeepStr: '20',
|
||||
artifactNumToKeepStr: '10',
|
||||
artifactNumToKeepStr: '1',
|
||||
))
|
||||
/* Allows combined build to copy */
|
||||
copyArtifactPermission('/status-mobile/*')
|
||||
|
||||
@@ -13,7 +13,7 @@ pipeline {
|
||||
buildDiscarder(logRotator(
|
||||
numToKeepStr: '10',
|
||||
daysToKeepStr: '30',
|
||||
artifactNumToKeepStr: '10',
|
||||
artifactNumToKeepStr: '1',
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ pipeline {
|
||||
buildDiscarder(logRotator(
|
||||
numToKeepStr: '10',
|
||||
daysToKeepStr: '30',
|
||||
artifactNumToKeepStr: '1',
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ pipeline {
|
||||
buildDiscarder(logRotator(
|
||||
numToKeepStr: '10',
|
||||
daysToKeepStr: '20',
|
||||
artifactNumToKeepStr: '10',
|
||||
artifactNumToKeepStr: '1',
|
||||
))
|
||||
/* Allows combined build to copy */
|
||||
copyArtifactPermission('/status-mobile/*')
|
||||
|
||||
+10
-2
@@ -10,12 +10,12 @@ pipeline {
|
||||
options {
|
||||
timestamps()
|
||||
/* Prevent Jenkins jobs from running forever */
|
||||
timeout(time: 15, unit: 'MINUTES')
|
||||
timeout(time: 20, unit: 'MINUTES')
|
||||
/* Limit builds retained */
|
||||
buildDiscarder(logRotator(
|
||||
numToKeepStr: '10',
|
||||
daysToKeepStr: '20',
|
||||
artifactNumToKeepStr: '10',
|
||||
artifactNumToKeepStr: '1',
|
||||
))
|
||||
/* Abort old PR builds. */
|
||||
disableConcurrentBuilds(
|
||||
@@ -62,6 +62,14 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Contract Tests') {
|
||||
steps {
|
||||
sh """#!/bin/bash
|
||||
set -eo pipefail
|
||||
make test-contract 2>&1 | tee -a ${LOG_FILE}
|
||||
"""
|
||||
}
|
||||
}
|
||||
stage('Integration Tests') {
|
||||
steps {
|
||||
sh """#!/bin/bash
|
||||
|
||||
@@ -29,6 +29,11 @@ pipeline {
|
||||
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
buildDiscarder(logRotator(
|
||||
numToKeepStr: '10',
|
||||
daysToKeepStr: '30',
|
||||
artifactNumToKeepStr: '1',
|
||||
))
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
@@ -47,6 +47,11 @@ pipeline {
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
timeout(time: 90, unit: 'MINUTES')
|
||||
buildDiscarder(logRotator(
|
||||
numToKeepStr: '10',
|
||||
daysToKeepStr: '30',
|
||||
artifactNumToKeepStr: '1',
|
||||
))
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
@@ -28,6 +28,11 @@ pipeline {
|
||||
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
buildDiscarder(logRotator(
|
||||
numToKeepStr: '10',
|
||||
daysToKeepStr: '30',
|
||||
artifactNumToKeepStr: '1',
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ pipeline {
|
||||
buildDiscarder(logRotator(
|
||||
numToKeepStr: '20',
|
||||
daysToKeepStr: '30',
|
||||
artifactNumToKeepStr: '1',
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,8 @@
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Status uses camera to take pictures and scan QR codes.</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Status needs access to the local network to give you the ability to connect with peers.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>Location access is required for some DApps to function properly.</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
@@ -133,7 +135,7 @@
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
<false/>
|
||||
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
|
||||
<array>
|
||||
<string>A00000080400010101</string>
|
||||
|
||||
@@ -4,10 +4,25 @@
|
||||
#import "Statusgo.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@interface NetworkManager() <NSNetServiceBrowserDelegate>
|
||||
|
||||
@property (nonatomic, strong) NSNetServiceBrowser *serviceBrowser;
|
||||
@property (nonatomic, copy) RCTResponseSenderBlock callback;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NetworkManager
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
- (instancetype)init {
|
||||
if ((self = [super init])) {
|
||||
self.serviceBrowser = [[NSNetServiceBrowser alloc] init];
|
||||
self.serviceBrowser.delegate = self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(startSearchForLocalPairingPeers:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoStartSearchForLocalPairingPeers();
|
||||
callback(@[result]);
|
||||
@@ -95,6 +110,39 @@ RCT_EXPORT_METHOD(callPrivateRPC:(NSString *)payload
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(requestLocalNetworkAccess:(RCTResponseSenderBlock)callback) {
|
||||
self.callback = callback;
|
||||
|
||||
// Start browsing for services using TCP on the local network.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.serviceBrowser searchForServicesOfType:@"_http._tcp." inDomain:@""];
|
||||
});
|
||||
}
|
||||
|
||||
// NSNetServiceBrowser delegate method
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser didNotSearch:(NSDictionary<NSString *,NSNumber *> *)errorDict {
|
||||
RCTLogInfo(@"Failed to search for services: %@", errorDict);
|
||||
if (self.callback) {
|
||||
self.callback(@[@"Failed to search for services"]);
|
||||
}
|
||||
}
|
||||
|
||||
// NSNetServiceBrowser delegate method
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindService:(NSNetService *)service moreComing:(BOOL)moreComing {
|
||||
RCTLogInfo(@"Found service: %@", service);
|
||||
// we stop the search after finding the first service or wait until the search is finished.
|
||||
[browser stop];
|
||||
|
||||
if (self.callback) {
|
||||
self.callback(@[[NSNull null], @"Requested local network access and found service"]);
|
||||
}
|
||||
}
|
||||
|
||||
// NSNetServiceBrowser delegate method
|
||||
- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser {
|
||||
RCTLogInfo(@"Stopped searching for services");
|
||||
}
|
||||
|
||||
#pragma mark - Recover
|
||||
|
||||
RCT_EXPORT_METHOD(recover:(NSString *)message
|
||||
|
||||
@@ -72,18 +72,3 @@
|
||||
{:events [:show-tooltip]}
|
||||
[_ tooltip-id]
|
||||
{:show-tooltip tooltip-id})
|
||||
|
||||
(rf/defn show-profile
|
||||
{:events [:chat.ui/show-profile]}
|
||||
[{:keys [db]} identity ens-name]
|
||||
(let [my-public-key (get-in db [:profile/profile :public-key])]
|
||||
(if (not= my-public-key identity)
|
||||
{:db (-> db
|
||||
(assoc :contacts/identity identity)
|
||||
(assoc :contacts/ens-name ens-name))
|
||||
:dispatch [:contacts/build-contact
|
||||
{:pubkey identity
|
||||
:ens ens-name
|
||||
:success-fn (fn [_]
|
||||
{:dispatch [:open-modal :profile]})}]}
|
||||
{:dispatch [:navigate-to :my-profile]})))
|
||||
|
||||
@@ -8,24 +8,6 @@
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn local-notifications
|
||||
[]
|
||||
(let [{:keys [enabled?]} (rf/sub [:push-notifications/wallet-transactions])
|
||||
{:keys [notifications-enabled?]} (rf/sub [:profile/profile])]
|
||||
[:<>
|
||||
[quo/separator
|
||||
{:color (:ui-02 @colors/theme)
|
||||
:style {:margin-vertical 8}}]
|
||||
[quo/list-header (i18n/label :t/local-notifications)]
|
||||
[list.item/list-item
|
||||
{:size :small
|
||||
:title (i18n/label :t/notifications-transactions)
|
||||
:accessibility-label :notifications-button
|
||||
:active (and notifications-enabled? enabled?)
|
||||
:on-press #(rf/dispatch [:push-notifications.wallet/switch-transactions
|
||||
(not enabled?)])
|
||||
:accessory :switch}]]))
|
||||
|
||||
(defn notifications-settings-ios
|
||||
[]
|
||||
(let [{:keys [notifications-enabled?
|
||||
@@ -63,8 +45,7 @@
|
||||
:on-press #(rf/dispatch
|
||||
[:push-notifications/switch-block-mentions
|
||||
(not push-notifications-block-mentions?)])
|
||||
:accessory :switch}]
|
||||
[local-notifications]]))
|
||||
:accessory :switch}]]))
|
||||
|
||||
(defn notifications-settings-android
|
||||
[]
|
||||
@@ -76,8 +57,7 @@
|
||||
:subtitle (i18n/label :t/local-notifications-subtitle)
|
||||
:active notifications-enabled?
|
||||
:on-press #(rf/dispatch [:push-notifications/switch (not notifications-enabled?)])
|
||||
:accessory :switch}]
|
||||
[local-notifications]]))
|
||||
:accessory :switch}]]))
|
||||
|
||||
(defn notifications-settings
|
||||
[]
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
:FlatList {}
|
||||
:SectionList {}
|
||||
:Text {}
|
||||
:StatusBar {}
|
||||
:StatusBar {:setBarStyle identity}
|
||||
:ScrollView {}
|
||||
:SafeAreaView {}
|
||||
:KeyboardAvoidingView {}
|
||||
|
||||
@@ -258,6 +258,11 @@
|
||||
(log/debug "[native-module] set-soft-input-mode")
|
||||
(.setSoftInputMode ^js (ui-helper) mode))
|
||||
|
||||
(defn request-local-network-access
|
||||
[callback]
|
||||
(log/debug "[native-module] request-local-network-access")
|
||||
(.requestLocalNetworkAccess ^js (network) callback))
|
||||
|
||||
(defn call-rpc
|
||||
[payload callback]
|
||||
(log/debug "[native-module] call-rpc")
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.platform :as platform]))
|
||||
|
||||
(defn remove-http-https-www
|
||||
[value]
|
||||
@@ -68,78 +67,86 @@
|
||||
[:cursor-color :placeholder-text-color :editable :on-change-text :on-focus
|
||||
:on-blur :on-clear :value :disabled? :blur? :customization-color :theme])
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [default-value]
|
||||
:or {default-value ""}}]
|
||||
(let [state (reagent/atom :default)
|
||||
value (reagent/atom default-value)
|
||||
set-active #(reset! state :active)
|
||||
set-default #(reset! state :default)
|
||||
set-value #(reset! value %)
|
||||
ref (atom nil)
|
||||
clear-input (fn []
|
||||
(.clear ^js @ref)
|
||||
(reset! value ""))
|
||||
focus-input (fn []
|
||||
(set-active)
|
||||
(.focus ^js @ref))]
|
||||
(fn [{:keys [disabled? blur? on-change-text customization-color
|
||||
on-clear on-focus on-blur theme get-ref locked?
|
||||
favicon favicon-color favicon-size]
|
||||
:as props}]
|
||||
(let [clean-props (apply dissoc props props-to-remove)]
|
||||
[rn/view {:style style/root-container}
|
||||
(when (and (seq @value) (= @state :default))
|
||||
[rn/touchable-opacity
|
||||
{:style style/default-container
|
||||
:on-press focus-input}
|
||||
(when favicon
|
||||
[icon/icon favicon
|
||||
{:accessibility-label :browser-input-favicon
|
||||
:color favicon-color
|
||||
:container-style style/favicon-icon-container
|
||||
:size favicon-size}])
|
||||
[rn/text
|
||||
{:accessibility-label :browser-input-label
|
||||
:style (style/text)} (remove-http-https-www @value)]
|
||||
(when locked?
|
||||
[lock-icon
|
||||
{:blur? blur?
|
||||
:theme theme}])])
|
||||
[rn/view {:style (style/active-container (or (empty? @value) (= @state :active)))}
|
||||
[rn/text-input
|
||||
(merge
|
||||
clean-props
|
||||
{:accessibility-label :browser-input
|
||||
:auto-capitalize :none
|
||||
:auto-correct false
|
||||
:cursor-color (cursor-color customization-color theme)
|
||||
:editable (not disabled?)
|
||||
:keyboard-appearance (colors/theme-colors :light :dark theme)
|
||||
:keyboard-type :web-search
|
||||
:on-blur (fn []
|
||||
(set-default)
|
||||
(when on-blur (on-blur)))
|
||||
:on-change-text (fn [new-text]
|
||||
(set-value new-text)
|
||||
(when on-change-text (on-change-text new-text)))
|
||||
:on-focus (fn []
|
||||
(set-active)
|
||||
(when on-focus (on-focus)))
|
||||
:placeholder-text-color (placeholder-color @state blur? theme)
|
||||
:ref (fn [r]
|
||||
(reset! ref r)
|
||||
(when get-ref (get-ref r)))
|
||||
:selection-color (when platform/ios?
|
||||
(cursor-color customization-color theme))
|
||||
:select-text-on-focus true
|
||||
:style (style/input disabled?)})]
|
||||
(when (seq @value)
|
||||
[clear-button
|
||||
{:blur? blur?
|
||||
:on-press (fn []
|
||||
(clear-input)
|
||||
(when on-clear (on-clear)))
|
||||
:theme theme}])]]))))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
(defn view
|
||||
[{:keys [disabled? blur? on-change-text customization-color
|
||||
on-clear on-focus on-blur get-ref locked?
|
||||
favicon favicon-color favicon-size default-value]
|
||||
:or {default-value ""}
|
||||
:as props}]
|
||||
(let [ref (rn/use-ref-atom nil)
|
||||
on-ref (rn/use-callback
|
||||
(fn [r]
|
||||
(reset! ref r)
|
||||
(when get-ref (get-ref r)))
|
||||
[get-ref])
|
||||
theme (quo.theme/use-theme)
|
||||
[state set-state] (rn/use-state :default)
|
||||
[value set-value] (rn/use-state default-value)
|
||||
on-clear (rn/use-callback
|
||||
(fn []
|
||||
(.clear ^js @ref)
|
||||
(set-value "")
|
||||
(when on-clear (on-clear)))
|
||||
[on-clear])
|
||||
focus-input (rn/use-callback
|
||||
(fn []
|
||||
(set-state :active)
|
||||
(.focus ^js @ref)))
|
||||
on-blur (rn/use-callback
|
||||
(fn []
|
||||
(set-state :default)
|
||||
(when on-blur (on-blur)))
|
||||
[on-blur])
|
||||
on-change-text (rn/use-callback
|
||||
(fn [new-text]
|
||||
(set-value new-text)
|
||||
(when on-change-text (on-change-text new-text)))
|
||||
[on-change-text])
|
||||
on-focus (rn/use-callback
|
||||
(fn []
|
||||
(set-state :active)
|
||||
(when on-focus (on-focus)))
|
||||
[on-focus])
|
||||
clean-props (apply dissoc props props-to-remove)]
|
||||
[rn/view {:style style/root-container}
|
||||
(when (and (seq value) (= state :default))
|
||||
[rn/touchable-opacity
|
||||
{:style style/default-container
|
||||
:on-press focus-input}
|
||||
(when favicon
|
||||
[icon/icon favicon
|
||||
{:accessibility-label :browser-input-favicon
|
||||
:color favicon-color
|
||||
:container-style style/favicon-icon-container
|
||||
:size favicon-size}])
|
||||
[rn/text
|
||||
{:accessibility-label :browser-input-label
|
||||
:style (style/text)}
|
||||
(remove-http-https-www value)]
|
||||
(when locked?
|
||||
[lock-icon {:blur? blur? :theme theme}])])
|
||||
[rn/view {:style (style/active-container (or (empty? value) (= state :active)))}
|
||||
[rn/text-input
|
||||
(merge
|
||||
clean-props
|
||||
{:accessibility-label :browser-input
|
||||
:auto-capitalize :none
|
||||
:auto-correct false
|
||||
:cursor-color (cursor-color customization-color theme)
|
||||
:editable (not disabled?)
|
||||
:keyboard-appearance (colors/theme-colors :light :dark theme)
|
||||
:keyboard-type :web-search
|
||||
:on-blur on-blur
|
||||
:on-change-text on-change-text
|
||||
:on-focus on-focus
|
||||
:placeholder-text-color (placeholder-color state blur? theme)
|
||||
:ref on-ref
|
||||
:selection-color (when platform/ios?
|
||||
(cursor-color customization-color theme))
|
||||
:select-text-on-focus true
|
||||
:style (style/input disabled?)})]
|
||||
(when (seq value)
|
||||
[clear-button
|
||||
{:blur? blur?
|
||||
:on-press on-clear
|
||||
:theme theme}])]]))
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
[quo.foundations.customization-colors :as customization-colors]
|
||||
[quo.theme :as theme]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- button-internal
|
||||
(defn button
|
||||
"with label
|
||||
[button opts \"label\"]
|
||||
opts
|
||||
@@ -29,102 +28,103 @@
|
||||
:theme :light/:dark
|
||||
only icon
|
||||
[button {:icon-only? true} :i/close-circle]"
|
||||
[_ _]
|
||||
(let [pressed-state? (reagent/atom false)]
|
||||
(fn
|
||||
[{:keys [on-press on-long-press disabled? type background size icon-left icon-right icon-top
|
||||
customization-color theme accessibility-label icon-only? container-style inner-style
|
||||
pressed? on-press-in on-press-out allow-multiple-presses?]
|
||||
:or {type :primary
|
||||
size 40
|
||||
customization-color (if (= type :primary) :blue nil)}}
|
||||
children]
|
||||
(let [{:keys [icon-color background-color label-color border-color blur-type
|
||||
blur-overlay-color border-radius overlay-customization-color]}
|
||||
(button-properties/get-values {:customization-color customization-color
|
||||
:background background
|
||||
:type type
|
||||
:theme theme
|
||||
:pressed? (if pressed? pressed? @pressed-state?)
|
||||
:icon-only? icon-only?})
|
||||
icon-size (when (= 24 size) 12)]
|
||||
[rn/touchable-without-feedback
|
||||
{:disabled disabled?
|
||||
:accessibility-label accessibility-label
|
||||
:on-press-in (fn []
|
||||
(reset! pressed-state? true)
|
||||
(when on-press-in (on-press-in)))
|
||||
:on-press-out (fn []
|
||||
(reset! pressed-state? nil)
|
||||
(when on-press-out (on-press-out)))
|
||||
:on-press on-press
|
||||
:allow-multiple-presses? allow-multiple-presses?
|
||||
:on-long-press on-long-press}
|
||||
[{:keys [on-press on-long-press disabled? type background size icon-left icon-right icon-top
|
||||
customization-color accessibility-label icon-only? container-style inner-style
|
||||
pressed? on-press-in on-press-out allow-multiple-presses?]
|
||||
:or {type :primary
|
||||
size 40
|
||||
customization-color (if (= type :primary) :blue nil)}}
|
||||
children]
|
||||
(let [[pressed-state? set-pressed-state] (rn/use-state false)
|
||||
theme (theme/use-theme-value)
|
||||
{:keys [icon-color background-color label-color border-color blur-type
|
||||
blur-overlay-color border-radius overlay-customization-color]}
|
||||
(button-properties/get-values {:customization-color customization-color
|
||||
:background background
|
||||
:type type
|
||||
:theme theme
|
||||
:pressed? (if pressed? pressed? pressed-state?)
|
||||
:icon-only? icon-only?})
|
||||
icon-size (when (= 24 size) 12)
|
||||
on-press-in-cb (rn/use-callback
|
||||
(fn []
|
||||
(set-pressed-state true)
|
||||
(when on-press-in (on-press-in))))
|
||||
on-press-out-cb (rn/use-callback
|
||||
(fn []
|
||||
(set-pressed-state nil)
|
||||
(when on-press-out (on-press-out))))]
|
||||
[rn/touchable-without-feedback
|
||||
{:disabled disabled?
|
||||
:accessibility-label accessibility-label
|
||||
:on-press-in on-press-in-cb
|
||||
:on-press-out on-press-out-cb
|
||||
:on-press on-press
|
||||
:allow-multiple-presses? allow-multiple-presses?
|
||||
:on-long-press on-long-press}
|
||||
[rn/view
|
||||
{:style (merge
|
||||
(style/shape-style-container size border-radius)
|
||||
container-style)}
|
||||
[rn/view
|
||||
{:style (merge
|
||||
(style/style-container {:size size
|
||||
:disabled? disabled?
|
||||
:border-radius border-radius
|
||||
:background-color background-color
|
||||
:border-color border-color
|
||||
:icon-only? icon-only?
|
||||
:icon-top icon-top
|
||||
:icon-left icon-left
|
||||
:icon-right icon-right})
|
||||
inner-style)}
|
||||
(when overlay-customization-color
|
||||
[customization-colors/overlay
|
||||
{:customization-color overlay-customization-color
|
||||
:theme theme
|
||||
:pressed? (if pressed? pressed? pressed-state?)}])
|
||||
(when (= background :photo)
|
||||
[blur/view
|
||||
{:blur-radius 20
|
||||
:blur-type blur-type
|
||||
:overlay-color blur-overlay-color
|
||||
:style style/blur-view}])
|
||||
(when icon-top
|
||||
[rn/view
|
||||
{:style (merge
|
||||
(style/shape-style-container size border-radius)
|
||||
container-style)}
|
||||
[rn/view
|
||||
{:style (merge
|
||||
(style/style-container {:size size
|
||||
:disabled? disabled?
|
||||
:border-radius border-radius
|
||||
:background-color background-color
|
||||
:border-color border-color
|
||||
:icon-only? icon-only?
|
||||
:icon-top icon-top
|
||||
:icon-left icon-left
|
||||
:icon-right icon-right})
|
||||
inner-style)}
|
||||
(when overlay-customization-color
|
||||
[customization-colors/overlay
|
||||
{:customization-color overlay-customization-color
|
||||
:theme theme
|
||||
:pressed? (if pressed? pressed? @pressed-state?)}])
|
||||
(when (= background :photo)
|
||||
[blur/view
|
||||
{:blur-radius 20
|
||||
:blur-type blur-type
|
||||
:overlay-color blur-overlay-color
|
||||
:style style/blur-view}])
|
||||
(when icon-top
|
||||
[rn/view
|
||||
[quo.icons/icon icon-top
|
||||
{:container-style {:margin-bottom 2}
|
||||
:color icon-color
|
||||
:size icon-size}]])
|
||||
(when icon-left
|
||||
[rn/view
|
||||
{:style (style/icon-left-icon-style
|
||||
{:size size
|
||||
:icon-size icon-size})}
|
||||
[quo.icons/icon icon-left
|
||||
{:color icon-color
|
||||
:size icon-size}]])
|
||||
[rn/view
|
||||
(cond
|
||||
icon-only?
|
||||
[quo.icons/icon children
|
||||
{:color label-color
|
||||
:size icon-size}]
|
||||
[quo.icons/icon icon-top
|
||||
{:container-style {:margin-bottom 2}
|
||||
:color icon-color
|
||||
:size icon-size}]])
|
||||
(when icon-left
|
||||
[rn/view
|
||||
{:style (style/icon-left-icon-style
|
||||
{:size size
|
||||
:icon-size icon-size})}
|
||||
[quo.icons/icon icon-left
|
||||
{:color icon-color
|
||||
:size icon-size}]])
|
||||
[rn/view
|
||||
(cond
|
||||
icon-only?
|
||||
[quo.icons/icon children
|
||||
{:color label-color
|
||||
:size icon-size}]
|
||||
|
||||
(string? children)
|
||||
[text/text
|
||||
{:size (when (#{56 24} size) :paragraph-2)
|
||||
:weight :medium
|
||||
:number-of-lines 1
|
||||
:style {:color label-color}}
|
||||
children]
|
||||
(string? children)
|
||||
[text/text
|
||||
{:size (when (#{56 24} size) :paragraph-2)
|
||||
:weight :medium
|
||||
:number-of-lines 1
|
||||
:style {:color label-color}}
|
||||
children]
|
||||
|
||||
(vector? children)
|
||||
children)]
|
||||
(when icon-right
|
||||
[rn/view
|
||||
{:style (style/icon-right-icon-style
|
||||
{:size size
|
||||
:icon-size icon-size})}
|
||||
[quo.icons/icon icon-right
|
||||
{:color icon-color
|
||||
:size icon-size}]])]]]))))
|
||||
|
||||
(def button (theme/with-theme button-internal))
|
||||
(vector? children)
|
||||
children)]
|
||||
(when icon-right
|
||||
[rn/view
|
||||
{:style (style/icon-right-icon-style
|
||||
{:size size
|
||||
:icon-size icon-size})}
|
||||
[quo.icons/icon icon-right
|
||||
{:color icon-color
|
||||
:size icon-size}]])]]]))
|
||||
|
||||
@@ -3,28 +3,24 @@
|
||||
[quo.components.buttons.composer-button.style :as style]
|
||||
[quo.components.icon :as quo.icons]
|
||||
[quo.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- view-internal
|
||||
[_ _]
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn
|
||||
[{:keys [on-press on-long-press disabled? theme blur? icon accessibility-label container-style]}]
|
||||
[rn/pressable
|
||||
{:accessibility-label (or accessibility-label :composer-button)
|
||||
:on-press on-press
|
||||
:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? nil)
|
||||
:on-long-press on-long-press
|
||||
:disabled disabled?
|
||||
:style (merge (style/main {:pressed? @pressed?
|
||||
:blur? blur?
|
||||
:theme theme
|
||||
:disabled? disabled?})
|
||||
container-style)}
|
||||
[quo.icons/icon icon
|
||||
{:color (style/get-label-color {:blur? blur?
|
||||
:theme theme})}]])))
|
||||
|
||||
(def view (theme/with-theme view-internal))
|
||||
(defn view
|
||||
[{:keys [on-press on-long-press disabled? blur? icon accessibility-label container-style]}]
|
||||
(let [[pressed? set-pressed] (rn/use-state false)
|
||||
theme (theme/use-theme-value)
|
||||
on-press-in (rn/use-callback #(set-pressed true))
|
||||
on-press-out (rn/use-callback #(set-pressed nil))]
|
||||
[rn/pressable
|
||||
{:accessibility-label (or accessibility-label :composer-button)
|
||||
:on-press on-press
|
||||
:on-press-in on-press-in
|
||||
:on-press-out on-press-out
|
||||
:on-long-press on-long-press
|
||||
:disabled disabled?
|
||||
:style (merge (style/main {:pressed? pressed?
|
||||
:blur? blur?
|
||||
:theme theme
|
||||
:disabled? disabled?})
|
||||
container-style)}
|
||||
[quo.icons/icon icon {:color (style/get-label-color {:blur? blur? :theme theme})}]]))
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- get-button-color
|
||||
[{:keys [type pressed? customization-color theme]}]
|
||||
@@ -45,48 +44,48 @@
|
||||
:color (get-icon-and-text-color type theme)
|
||||
:container-style (style/container type)}])
|
||||
|
||||
(defn- view-internal
|
||||
(defn view
|
||||
"[dynamic-button opts]
|
||||
opts
|
||||
{:type :jump-to/:mention/:notification-down/:notification-up/:search/:search-with-label/:scroll-to-bottom
|
||||
:on-press fn
|
||||
:count mentions or notifications count
|
||||
:customization-color customize jump-to and mention button color}"
|
||||
[_]
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn [{:keys [type label on-press customization-color style theme] :as args}]
|
||||
[rn/touchable-opacity
|
||||
{:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? false)
|
||||
:on-press on-press
|
||||
:active-opacity 1
|
||||
:hit-slop {:top 5 :bottom 5 :left 5 :right 5}
|
||||
:pointer-events :auto
|
||||
:style {:height 24}
|
||||
:accessibility-label type}
|
||||
[rn/view
|
||||
{:style (merge
|
||||
{:flex-direction :row
|
||||
:height 24
|
||||
:border-radius 12
|
||||
:background-color (get-button-color {:type type
|
||||
:pressed? @pressed?
|
||||
:customization-color (or customization-color
|
||||
:primary)
|
||||
:theme theme})}
|
||||
style)}
|
||||
(when (#{:mention :search :search-with-label :scroll-to-bottom} type)
|
||||
[icon-view type])
|
||||
(when (#{:jump-to :mention :notification-down :notification-up :search-with-label} type)
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (assoc (style/text type) :color (get-icon-and-text-color type theme))}
|
||||
(case type
|
||||
:jump-to label
|
||||
:search-with-label label
|
||||
(:mention :notification-down :notification-up) (str (:count args)))])
|
||||
(when (#{:jump-to :notification-down :notification-up} type)
|
||||
[icon-view type theme])]])))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
[{:keys [type label on-press customization-color style] :as args}]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
[pressed? set-pressed] (rn/use-state false)
|
||||
button-color (get-button-color {:type type
|
||||
:pressed? pressed?
|
||||
:customization-color (or customization-color :primary)
|
||||
:theme theme})
|
||||
on-press-in (rn/use-callback #(set-pressed true))
|
||||
on-press-out (rn/use-callback #(set-pressed false))]
|
||||
[rn/touchable-opacity
|
||||
{:on-press-in on-press-in
|
||||
:on-press-out on-press-out
|
||||
:on-press on-press
|
||||
:active-opacity 1
|
||||
:hit-slop {:top 5 :bottom 5 :left 5 :right 5}
|
||||
:pointer-events :auto
|
||||
:style {:height 24}
|
||||
:accessibility-label type}
|
||||
[rn/view
|
||||
{:style (merge
|
||||
{:flex-direction :row
|
||||
:height 24
|
||||
:border-radius 12
|
||||
:background-color button-color}
|
||||
style)}
|
||||
(when (#{:mention :search :search-with-label :scroll-to-bottom} type)
|
||||
[icon-view type])
|
||||
(when (#{:jump-to :mention :notification-down :notification-up :search-with-label} type)
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (assoc (style/text type) :color (get-icon-and-text-color type theme))}
|
||||
(case type
|
||||
:jump-to label
|
||||
:search-with-label label
|
||||
(:mention :notification-down :notification-up) (str (:count args)))])
|
||||
(when (#{:jump-to :notification-down :notification-up} type)
|
||||
[icon-view type theme])]]))
|
||||
|
||||
@@ -6,29 +6,25 @@
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(defn- view-internal
|
||||
[_]
|
||||
(let [pressed? (reagent/atom false)
|
||||
on-press-in #(reset! pressed? true)
|
||||
on-press-out #(reset! pressed? nil)]
|
||||
(fn
|
||||
[{:keys [on-press on-long-press disabled? theme container-style]}]
|
||||
[rn/pressable
|
||||
{:accessibility-label :log-out-button
|
||||
:on-press on-press
|
||||
:on-press-in on-press-in
|
||||
:on-press-out on-press-out
|
||||
:on-long-press on-long-press
|
||||
:disabled disabled?
|
||||
:style (merge (style/main {:pressed? @pressed?
|
||||
:theme theme
|
||||
:disabled? disabled?})
|
||||
container-style)}
|
||||
[icon/icon :i/log-out {:color (if pressed? colors/white-opa-40 colors/white-opa-70)}]
|
||||
[text/text {:weight :medium :size :paragraph-1}
|
||||
(i18n/label :t/logout)]])))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
(defn view
|
||||
[{:keys [on-press on-long-press disabled? container-style]}]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
[pressed? set-pressed] (rn/use-state false)
|
||||
on-press-in (rn/use-callback #(set-pressed true))
|
||||
on-press-out (rn/use-callback #(set-pressed nil))]
|
||||
[rn/pressable
|
||||
{:accessibility-label :log-out-button
|
||||
:on-press on-press
|
||||
:on-press-in on-press-in
|
||||
:on-press-out on-press-out
|
||||
:on-long-press on-long-press
|
||||
:disabled disabled?
|
||||
:style (merge (style/main {:pressed? pressed?
|
||||
:theme theme
|
||||
:disabled? disabled?})
|
||||
container-style)}
|
||||
[icon/icon :i/log-out {:color (if pressed? colors/white-opa-40 colors/white-opa-70)}]
|
||||
[text/text {:weight :medium :size :paragraph-1}
|
||||
(i18n/label :t/logout)]]))
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
(ns quo.components.buttons.slide-button.animations
|
||||
(:require
|
||||
[oops.core :as oops]
|
||||
[quo.components.buttons.slide-button.utils :as utils]
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
(:require [react-native.reanimated :as reanimated]))
|
||||
|
||||
(def ^:private extrapolation
|
||||
{:extrapolateLeft "clamp"
|
||||
@@ -66,36 +62,6 @@
|
||||
:damping 30
|
||||
:stiffness 400}))
|
||||
|
||||
(defn- complete-animation
|
||||
[sliding-complete?]
|
||||
(reset! sliding-complete? true))
|
||||
|
||||
(defn reset-track-position
|
||||
[x-pos]
|
||||
(animate-spring x-pos 0))
|
||||
|
||||
;; Gestures
|
||||
(defn drag-gesture
|
||||
[x-pos
|
||||
gestures-disabled?
|
||||
disabled?
|
||||
track-width
|
||||
sliding-complete?]
|
||||
(let [gestures-enabled? (not (or disabled? @gestures-disabled?))]
|
||||
(-> (gesture/gesture-pan)
|
||||
(gesture/with-test-ID :slide-button-gestures)
|
||||
(gesture/enabled gestures-enabled?)
|
||||
(gesture/min-distance 0)
|
||||
(gesture/on-update (fn [event]
|
||||
(let [x-translation (oops/oget event "translationX")
|
||||
clamped-x (utils/clamp-value x-translation 0 track-width)
|
||||
reached-end? (>= clamped-x track-width)]
|
||||
(reanimated/set-shared-value x-pos clamped-x)
|
||||
(when (and reached-end? (not @sliding-complete?))
|
||||
(reset! gestures-disabled? true)
|
||||
(complete-animation sliding-complete?)))))
|
||||
(gesture/on-end (fn [event]
|
||||
(let [x-translation (oops/oget event "translationX")
|
||||
reached-end? (>= x-translation track-width)]
|
||||
(when (not reached-end?)
|
||||
(reset-track-position x-pos))))))))
|
||||
|
||||
@@ -11,94 +11,35 @@
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(defn- f-slider
|
||||
[{:keys [disabled?]}]
|
||||
(let [track-width (reagent/atom nil)
|
||||
sliding-complete? (reagent/atom false)
|
||||
gestures-disabled? (reagent/atom disabled?)
|
||||
on-track-layout (fn [evt]
|
||||
(let [width (oops/oget evt "nativeEvent.layout.width")]
|
||||
(reset! track-width width)))]
|
||||
(fn [{:keys [on-reset
|
||||
on-complete
|
||||
track-text
|
||||
track-icon
|
||||
disabled?
|
||||
customization-color
|
||||
size
|
||||
container-style
|
||||
theme
|
||||
type
|
||||
blur?]}]
|
||||
(let [x-pos (reanimated/use-shared-value 0)
|
||||
dimensions (partial utils/get-dimensions
|
||||
(or @track-width constants/default-width)
|
||||
size)
|
||||
interpolate-track (partial animations/interpolate-track
|
||||
x-pos
|
||||
(dimensions :usable-track)
|
||||
(dimensions :thumb))
|
||||
custom-color (if (= type :danger) :danger customization-color)]
|
||||
(rn/use-effect (fn []
|
||||
(when @sliding-complete?
|
||||
(on-complete)))
|
||||
[@sliding-complete?])
|
||||
(rn/use-effect (fn []
|
||||
(when on-reset
|
||||
(reset! sliding-complete? false)
|
||||
(reset! gestures-disabled? false)
|
||||
(animations/reset-track-position x-pos)
|
||||
(on-reset)))
|
||||
[on-reset])
|
||||
[gesture/gesture-detector
|
||||
{:gesture (animations/drag-gesture x-pos
|
||||
gestures-disabled?
|
||||
disabled?
|
||||
(dimensions :usable-track)
|
||||
sliding-complete?)}
|
||||
[reanimated/view
|
||||
{:test-ID :slide-button-track
|
||||
:style (merge (style/track {:disabled? disabled?
|
||||
:customization-color custom-color
|
||||
:height (dimensions :track-height)
|
||||
:blur? blur?})
|
||||
container-style)
|
||||
:on-layout (when-not (some? @track-width)
|
||||
on-track-layout)}
|
||||
[reanimated/view {:style (style/track-cover interpolate-track)}
|
||||
[rn/view {:style (style/track-cover-text-container @track-width)}
|
||||
[icon/icon track-icon
|
||||
{:color (utils/text-color custom-color theme blur?)
|
||||
:size 20}]
|
||||
[rn/view {:width 4}]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-1
|
||||
:style (style/track-text custom-color theme blur?)}
|
||||
track-text]]]
|
||||
[reanimated/view
|
||||
{:style (style/thumb-container {:interpolate-track interpolate-track
|
||||
:thumb-size (dimensions :thumb)
|
||||
:customization-color custom-color
|
||||
:theme theme
|
||||
:blur? blur?})}
|
||||
[reanimated/view {:style (style/arrow-icon-container interpolate-track)}
|
||||
[icon/icon :arrow-right
|
||||
{:color colors/white
|
||||
:size 20}]]
|
||||
[reanimated/view
|
||||
{:style (style/action-icon interpolate-track
|
||||
(dimensions :thumb))}
|
||||
[icon/icon track-icon
|
||||
{:color colors/white
|
||||
:size 20}]]]]]))))
|
||||
(defn drag-gesture
|
||||
[x-pos gestures-disabled? set-gestures-disabled disabled? track-width sliding-complete?
|
||||
set-sliding-complete
|
||||
on-complete reset-fn]
|
||||
(let [gestures-enabled? (not (or disabled? gestures-disabled?))]
|
||||
(-> (gesture/gesture-pan)
|
||||
(gesture/with-test-ID :slide-button-gestures)
|
||||
(gesture/enabled gestures-enabled?)
|
||||
(gesture/min-distance 0)
|
||||
(gesture/on-update (fn [event]
|
||||
(let [x-translation (oops/oget event "translationX")
|
||||
clamped-x (utils/clamp-value x-translation 0 track-width)
|
||||
reached-end? (>= clamped-x track-width)]
|
||||
(reanimated/set-shared-value x-pos clamped-x)
|
||||
(when (and reached-end? (not sliding-complete?))
|
||||
(set-gestures-disabled true)
|
||||
(set-sliding-complete true)
|
||||
(when on-complete (on-complete reset-fn))))))
|
||||
(gesture/on-end (fn [event]
|
||||
(let [x-translation (oops/oget event "translationX")
|
||||
reached-end? (>= x-translation track-width)]
|
||||
(when (not reached-end?)
|
||||
(animations/reset-track-position x-pos))))))))
|
||||
|
||||
(defn- view-internal
|
||||
(defn view
|
||||
"Options
|
||||
- `on-complete` Callback called when the sliding is complete
|
||||
- `on-complete` Callback called when the sliding is complete, returns reset fn as a parameter
|
||||
- `disabled?` Boolean that disables the button
|
||||
(_and gestures_)
|
||||
- `size` :size/s-40`/`:size/s-48`
|
||||
@@ -106,9 +47,78 @@
|
||||
- `track-icon` Key of the icon shown on the track
|
||||
(e.g. `:face-id`)
|
||||
- `customization-color` Customization color
|
||||
- `on-reset` A callback which can be used to reset the component and run required functionality
|
||||
"
|
||||
[props]
|
||||
[:f> f-slider props])
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
[{:keys [on-complete track-text track-icon disabled? customization-color size
|
||||
container-style type blur?]}]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
x-pos (reanimated/use-shared-value 0)
|
||||
[track-width set-track-width] (rn/use-state nil)
|
||||
[sliding-complete?
|
||||
set-sliding-complete] (rn/use-state false)
|
||||
[gestures-disabled?
|
||||
set-gestures-disabled] (rn/use-state disabled?)
|
||||
on-track-layout (rn/use-callback
|
||||
#(set-track-width (oops/oget % "nativeEvent.layout.width")))
|
||||
reset-fn (rn/use-callback
|
||||
(fn []
|
||||
(set-sliding-complete false)
|
||||
(set-gestures-disabled false)
|
||||
(animations/reset-track-position x-pos)))
|
||||
dimensions (rn/use-callback
|
||||
(partial utils/get-dimensions
|
||||
(or track-width constants/default-width)
|
||||
size)
|
||||
[track-width])
|
||||
interpolate-track (rn/use-callback
|
||||
(partial animations/interpolate-track
|
||||
x-pos
|
||||
(dimensions :usable-track)
|
||||
(dimensions :thumb))
|
||||
[dimensions])
|
||||
custom-color (if (= type :danger) :danger customization-color)
|
||||
gesture (rn/use-memo #(drag-gesture x-pos
|
||||
gestures-disabled?
|
||||
set-gestures-disabled
|
||||
disabled?
|
||||
(dimensions :usable-track)
|
||||
sliding-complete?
|
||||
set-sliding-complete
|
||||
on-complete
|
||||
reset-fn)
|
||||
[gestures-disabled? sliding-complete? disabled?])]
|
||||
[gesture/gesture-detector
|
||||
{:gesture gesture}
|
||||
[reanimated/view
|
||||
{:test-ID :slide-button-track
|
||||
:style (merge (style/track {:disabled? disabled?
|
||||
:customization-color custom-color
|
||||
:height (dimensions :track-height)
|
||||
:blur? blur?})
|
||||
container-style)
|
||||
:on-layout on-track-layout}
|
||||
[reanimated/view {:style (style/track-cover interpolate-track)}
|
||||
[rn/view {:style (style/track-cover-text-container track-width)}
|
||||
[icon/icon track-icon
|
||||
{:color (utils/text-color custom-color theme blur?)
|
||||
:size 20}]
|
||||
[rn/view {:width 4}]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-1
|
||||
:style (style/track-text custom-color theme blur?)}
|
||||
track-text]]]
|
||||
[reanimated/view
|
||||
{:style (style/thumb-container {:interpolate-track interpolate-track
|
||||
:thumb-size (dimensions :thumb)
|
||||
:customization-color custom-color
|
||||
:theme theme
|
||||
:blur? blur?})}
|
||||
[reanimated/view {:style (style/arrow-icon-container interpolate-track)}
|
||||
[icon/icon :arrow-right
|
||||
{:color colors/white
|
||||
:size 20}]]
|
||||
[reanimated/view
|
||||
{:style (style/action-icon interpolate-track (dimensions :thumb))}
|
||||
[icon/icon track-icon
|
||||
{:color colors/white
|
||||
:size 20}]]]]]))
|
||||
|
||||
@@ -4,26 +4,24 @@
|
||||
[quo.components.icon :as quo.icons]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- view-internal
|
||||
[]
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn
|
||||
[{:keys [on-press on-long-press disabled? icon accessibility-label container-style theme]}]
|
||||
[rn/pressable
|
||||
{:accessibility-label (or accessibility-label :wallet-button)
|
||||
:on-press on-press
|
||||
:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? nil)
|
||||
:on-long-press on-long-press
|
||||
:disabled disabled?
|
||||
:style (merge (style/main {:pressed? @pressed?
|
||||
:theme theme
|
||||
:disabled? disabled?})
|
||||
container-style)}
|
||||
[quo.icons/icon icon
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white theme)}]])))
|
||||
|
||||
(def view (theme/with-theme view-internal))
|
||||
(defn view
|
||||
[{:keys [on-press on-long-press disabled? icon accessibility-label container-style]}]
|
||||
(let [theme (theme/use-theme-value)
|
||||
[pressed? set-pressed] (rn/use-state false)
|
||||
on-press-in (rn/use-callback #(set-pressed true))
|
||||
on-press-out (rn/use-callback #(set-pressed nil))]
|
||||
[rn/pressable
|
||||
{:accessibility-label (or accessibility-label :wallet-button)
|
||||
:on-press on-press
|
||||
:on-press-in on-press-in
|
||||
:on-press-out on-press-out
|
||||
:on-long-press on-long-press
|
||||
:disabled disabled?
|
||||
:style (merge (style/main {:pressed? pressed?
|
||||
:theme theme
|
||||
:disabled? disabled?})
|
||||
container-style)}
|
||||
[quo.icons/icon icon
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white theme)}]]))
|
||||
|
||||
@@ -8,36 +8,36 @@
|
||||
[quo.components.calendar.calendar.years-list.view :as years-list]
|
||||
[quo.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[utils.number :as utils.number]))
|
||||
|
||||
(defn- view-internal
|
||||
[]
|
||||
(let [selected-year (reagent/atom (utils/current-year))
|
||||
selected-month (reagent/atom (utils/current-month))
|
||||
on-change-year #(reset! selected-year %)
|
||||
on-change-month (fn [new-date]
|
||||
(reset! selected-year (utils.number/parse-int (:year new-date)))
|
||||
(reset! selected-month (utils.number/parse-int (:month new-date))))]
|
||||
(fn [{:keys [on-change start-date end-date theme]}]
|
||||
[rn/view
|
||||
{:style (style/container theme)}
|
||||
[years-list/view
|
||||
{:on-change-year on-change-year
|
||||
:year @selected-year}]
|
||||
[rn/view
|
||||
{:style style/container-main}
|
||||
[month-picker/view
|
||||
{:year @selected-year
|
||||
:month @selected-month
|
||||
:on-change on-change-month}]
|
||||
[weekdays-header/view]
|
||||
[days-grid/view
|
||||
{:year @selected-year
|
||||
:month @selected-month
|
||||
:start-date start-date
|
||||
:end-date end-date
|
||||
:on-change on-change
|
||||
:customization-color :blue}]]])))
|
||||
|
||||
(def view (theme/with-theme view-internal))
|
||||
(defn view
|
||||
[{:keys [on-change start-date end-date]}]
|
||||
(let [theme (theme/use-theme-value)
|
||||
[selected-year set-selected-year] (rn/use-state (utils/current-year))
|
||||
[selected-month set-selected-month] (rn/use-state (utils/current-month))
|
||||
on-change-year (rn/use-callback #(set-selected-year %))
|
||||
on-change-month (rn/use-callback
|
||||
(fn [new-date]
|
||||
(set-selected-year
|
||||
(utils.number/parse-int (:year new-date)))
|
||||
(set-selected-month
|
||||
(utils.number/parse-int (:month new-date)))))]
|
||||
[rn/view
|
||||
{:style (style/container theme)}
|
||||
[years-list/view
|
||||
{:on-change-year on-change-year
|
||||
:year selected-year}]
|
||||
[rn/view
|
||||
{:style style/container-main}
|
||||
[month-picker/view
|
||||
{:year selected-year
|
||||
:month selected-month
|
||||
:on-change on-change-month}]
|
||||
[weekdays-header/view]
|
||||
[days-grid/view
|
||||
{:year selected-year
|
||||
:month selected-month
|
||||
:start-date start-date
|
||||
:end-date end-date
|
||||
:on-change on-change
|
||||
:customization-color :blue}]]]))
|
||||
|
||||
@@ -3,13 +3,7 @@
|
||||
[quo.components.colors.color.constants :as constants]
|
||||
[quo.components.colors.color.view :as color]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn- on-change-handler
|
||||
[selected color-name on-change]
|
||||
(reset! selected color-name)
|
||||
(when on-change (on-change color-name)))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn get-item-layout
|
||||
[_ index]
|
||||
@@ -18,15 +12,34 @@
|
||||
:offset (* (+ constants/color-size 8) index)
|
||||
:index index})
|
||||
|
||||
(defn- view-internal
|
||||
(defn view
|
||||
"Options
|
||||
- `default-selected` Default selected color name.
|
||||
- `on-change` Callback called when a color is selected `(fn [color-name])`.
|
||||
- `blur?` Boolean to enable blur background support.}"
|
||||
[{:keys [default-selected blur? on-change feng-shui? container-style]}]
|
||||
(let [selected (reagent/atom default-selected)
|
||||
{window-width :width} (rn/get-window)
|
||||
ref (atom nil)]
|
||||
(let [[selected set-selected] (rn/use-state default-selected)
|
||||
{window-width :width} (rn/get-window)
|
||||
ref (rn/use-ref-atom nil)
|
||||
on-ref (rn/use-callback #(reset! ref %))
|
||||
render-fn (rn/use-callback
|
||||
(fn [color idx]
|
||||
[color/view
|
||||
{:selected? (= color selected)
|
||||
:on-press (fn [color-name]
|
||||
(.scrollToIndex ^js @ref
|
||||
#js
|
||||
{:animated true
|
||||
:index idx
|
||||
:viewPosition 0.5})
|
||||
(set-selected color-name)
|
||||
(when on-change (on-change color-name)))
|
||||
:blur? blur?
|
||||
:key color
|
||||
:color color
|
||||
:idx idx
|
||||
:window-width window-width}])
|
||||
[selected blur? on-change @ref])]
|
||||
(rn/use-mount
|
||||
(fn []
|
||||
(js/setTimeout
|
||||
@@ -40,32 +53,14 @@
|
||||
:viewPosition 0.5})))))
|
||||
50)))
|
||||
[rn/flat-list
|
||||
{:ref #(reset! ref %)
|
||||
{:ref on-ref
|
||||
;; TODO: using :feng-shui? temporarily while b & w is being developed.
|
||||
;; https://github.com/status-im/status-mobile/discussions/16676
|
||||
:data (if feng-shui?
|
||||
(conj colors/account-colors :feng-shui)
|
||||
colors/account-colors)
|
||||
:render-fn (fn [color idx]
|
||||
[color/view
|
||||
{:selected? (= color @selected)
|
||||
:on-press (fn [e]
|
||||
(.scrollToIndex ^js @ref
|
||||
#js
|
||||
{:animated true
|
||||
:index idx
|
||||
:viewPosition 0.5})
|
||||
(on-change-handler selected e on-change))
|
||||
:blur? blur?
|
||||
:key color
|
||||
:color color
|
||||
:idx idx
|
||||
:window-width window-width}])
|
||||
:render-fn render-fn
|
||||
:get-item-layout get-item-layout
|
||||
:horizontal true
|
||||
:shows-horizontal-scroll-indicator false
|
||||
:content-container-style container-style}]))
|
||||
|
||||
(defn view
|
||||
[props]
|
||||
[:f> view-internal props])
|
||||
|
||||
@@ -35,12 +35,11 @@
|
||||
(defn image
|
||||
"Same as rn/image but cache the image source in a js/Set, so the image won't
|
||||
flicker when re-render on android"
|
||||
[]
|
||||
(let [loaded-source (reagent/atom nil)
|
||||
on-source-loaded #(reset! loaded-source %)]
|
||||
(fn [props]
|
||||
(if platform/ios?
|
||||
[rn/image props]
|
||||
[:<>
|
||||
[rn/image (assoc props :source @loaded-source)]
|
||||
[caching-image props on-source-loaded]]))))
|
||||
[props]
|
||||
(let [[loaded-source set-loaded-source] (rn/use-state nil)
|
||||
on-source-loaded (rn/use-callback #(set-loaded-source %))]
|
||||
(if platform/ios?
|
||||
[rn/image props]
|
||||
[:<>
|
||||
[rn/image (assoc props :source loaded-source)]
|
||||
[caching-image props on-source-loaded]])))
|
||||
|
||||
@@ -3,25 +3,23 @@
|
||||
[quo.components.dropdowns.network-dropdown.style :as style]
|
||||
[quo.components.list-items.preview-list.view :as preview-list]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- internal-view
|
||||
[_ _]
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn
|
||||
[{:keys [on-press state] :as props} networks]
|
||||
[rn/pressable
|
||||
{:style (style/dropdown-container (merge props {:pressed? @pressed?}))
|
||||
:accessibility-label :network-dropdown
|
||||
:disabled (= state :disabled)
|
||||
:on-press on-press
|
||||
:on-press-in (fn [] (reset! pressed? true))
|
||||
:on-press-out (fn [] (reset! pressed? false))}
|
||||
[preview-list/view
|
||||
{:type :network
|
||||
:list-size (count networks)
|
||||
:size :size-20}
|
||||
networks]])))
|
||||
|
||||
(def view (quo.theme/with-theme internal-view))
|
||||
(defn view
|
||||
[{:keys [on-press state] :as props} networks]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
[pressed? set-pressed] (rn/use-state false)
|
||||
on-press-in (rn/use-callback #(set-pressed true))
|
||||
on-press-out (rn/use-callback #(set-pressed false))]
|
||||
[rn/pressable
|
||||
{:style (style/dropdown-container (merge props {:pressed? pressed? :theme theme}))
|
||||
:accessibility-label :network-dropdown
|
||||
:disabled (= state :disabled)
|
||||
:on-press on-press
|
||||
:on-press-in on-press-in
|
||||
:on-press-out on-press-out}
|
||||
[preview-list/view
|
||||
{:type :network
|
||||
:list-size (count networks)
|
||||
:size :size-20}
|
||||
networks]]))
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
(def initial-spacing 56)
|
||||
(def end-spacing 22)
|
||||
(def y-axis-label-width -33)
|
||||
(def inspecting? (reagent/atom false))
|
||||
|
||||
(defn- pointer
|
||||
[customization-color]
|
||||
@@ -32,14 +31,9 @@
|
||||
:pointer-color customization-color
|
||||
:pointer-strip-enable-gradient true})
|
||||
|
||||
(defn- get-pointer-props
|
||||
[pointer-props]
|
||||
(let [pointer-index (.-pointerIndex ^js pointer-props)]
|
||||
(reset! inspecting? (not= pointer-index -1))))
|
||||
|
||||
(defn- get-line-color
|
||||
[state theme]
|
||||
(if @inspecting?
|
||||
[state theme inspecting?]
|
||||
(if inspecting?
|
||||
(colors/theme-colors colors/neutral-80-opa-40
|
||||
colors/white-opa-20
|
||||
theme)
|
||||
@@ -51,11 +45,13 @@
|
||||
colors/danger-60
|
||||
theme))))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [data state customization-color theme reference-value reference-prefix decimal-separator]
|
||||
(defn view
|
||||
[{:keys [data state customization-color reference-value reference-prefix decimal-separator]
|
||||
:or {reference-prefix "$"
|
||||
decimal-separator :dot}}]
|
||||
(let [data (if (> (count data) max-data-points)
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
[inspecting? set-inspecting] (rn/use-state false)
|
||||
data (if (> (count data) max-data-points)
|
||||
(utils/downsample-data data max-data-points)
|
||||
data)
|
||||
highest-value (utils/find-highest-value data)
|
||||
@@ -64,7 +60,11 @@
|
||||
max-value (- (utils/calculate-rounded-max highest-value) min-value)
|
||||
step-value (/ max-value 4)
|
||||
width (:width (rn/get-window))
|
||||
line-color (get-line-color state theme)
|
||||
line-color (get-line-color state theme inspecting?)
|
||||
get-pointer-props (rn/use-callback
|
||||
(fn [pointer-props]
|
||||
(let [pointer-index (.-pointerIndex ^js pointer-props)]
|
||||
(set-inspecting (not= pointer-index -1)))))
|
||||
rules-color (colors/theme-colors colors/neutral-80-opa-10
|
||||
colors/white-opa-5
|
||||
theme)
|
||||
@@ -119,7 +119,7 @@
|
||||
:show-strip-on-focus true
|
||||
:reference-line-1-config {:color rules-color}
|
||||
:reference-line-1-position 0
|
||||
:show-reference-line-2 (and (not @inspecting?)
|
||||
:show-reference-line-2 (and (not inspecting?)
|
||||
(<= reference-value highest-value)
|
||||
(>= reference-value lowest-value))
|
||||
:reference-line-2-config {:color y-axis-label-text-color
|
||||
@@ -138,5 +138,3 @@
|
||||
:x-axis-label-text-style (style/x-axis-label-text (/ width (count x-axis-label-texts))
|
||||
y-axis-label-text-color)
|
||||
:x-axis-label-texts x-axis-label-texts}]]))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
(h/fire-event :on-focus (h/get-by-label-text :address-text-input))
|
||||
(h/has-prop (h/get-by-label-text :address-text-input)
|
||||
:placeholder-text-color
|
||||
colors/neutral-40)))
|
||||
colors/neutral-30)))
|
||||
|
||||
(h/test "on focus with blur? true"
|
||||
(with-redefs [clipboard/get-string #(% "")]
|
||||
@@ -30,27 +30,22 @@
|
||||
(h/fire-event :on-focus (h/get-by-label-text :address-text-input))
|
||||
(h/has-prop (h/get-by-label-text :address-text-input)
|
||||
:placeholder-text-color
|
||||
colors/neutral-80-opa-40)))
|
||||
colors/neutral-80-opa-20)))
|
||||
|
||||
(h/test "scanned value is properly set"
|
||||
(let [on-change-text (h/mock-fn)
|
||||
scanned-value "scanned-value"]
|
||||
(h/test "default value is properly set"
|
||||
(let [default-value "default-value"]
|
||||
(with-redefs [clipboard/get-string #(% "")]
|
||||
(h/render [address-input/address-input
|
||||
{:scanned-value scanned-value
|
||||
:on-change-text on-change-text
|
||||
:ens-regex ens-regex}])
|
||||
(-> (h/wait-for #(h/get-by-label-text :clear-button))
|
||||
(.then (fn []
|
||||
(h/was-called-with on-change-text scanned-value)
|
||||
(h/has-prop (h/get-by-label-text :address-text-input)
|
||||
:default-value
|
||||
scanned-value)))))))
|
||||
{:default-value default-value
|
||||
:ens-regex ens-regex}])
|
||||
(h/has-prop (h/get-by-label-text :address-text-input)
|
||||
:value
|
||||
default-value))))
|
||||
|
||||
(h/test "clear icon is shown when input has text"
|
||||
(with-redefs [clipboard/get-string #(% "")]
|
||||
(h/render [address-input/address-input
|
||||
{:scanned-value "scanned value"
|
||||
{:default-value "default value"
|
||||
:ens-regex ens-regex}])
|
||||
(-> (h/wait-for #(h/get-by-label-text :clear-button-container))
|
||||
(.then #(h/is-truthy (h/get-by-label-text :clear-button))))))
|
||||
@@ -58,7 +53,7 @@
|
||||
(h/test "on blur with text and blur? false"
|
||||
(with-redefs [clipboard/get-string #(% "")]
|
||||
(h/render [address-input/address-input
|
||||
{:scanned-value "scanned value"
|
||||
{:default-value "default value"
|
||||
:ens-regex ens-regex}])
|
||||
(-> (h/wait-for #(h/get-by-label-text :clear-button))
|
||||
(.then (fn []
|
||||
@@ -71,7 +66,7 @@
|
||||
(h/test "on blur with text blur? true"
|
||||
(with-redefs [clipboard/get-string #(% "")]
|
||||
(h/render [address-input/address-input
|
||||
{:scanned-value "scanned value"
|
||||
{:default-value "default value"
|
||||
:blur? true
|
||||
:ens-regex ens-regex}])
|
||||
(-> (h/wait-for #(h/get-by-label-text :clear-button))
|
||||
@@ -106,7 +101,7 @@
|
||||
(let [on-clear (h/mock-fn)]
|
||||
(with-redefs [clipboard/get-string #(% "")]
|
||||
(h/render [address-input/address-input
|
||||
{:scanned-value "scanned value"
|
||||
{:default-value "default value"
|
||||
:on-clear on-clear
|
||||
:ens-regex ens-regex}])
|
||||
(-> (h/wait-for #(h/get-by-label-text :clear-button))
|
||||
@@ -148,7 +143,7 @@
|
||||
(-> (h/wait-for #(h/get-by-label-text :clear-button))
|
||||
(.then (fn []
|
||||
(h/has-prop (h/get-by-label-text :address-text-input)
|
||||
:default-value
|
||||
:value
|
||||
clipboard)))))))
|
||||
|
||||
(h/test "ENS loading state and call on-detect-ens"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
[react-native.clipboard :as clipboard]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[reagent.core :as reagent]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(defn- icon-color
|
||||
@@ -55,124 +54,135 @@
|
||||
(and (not= status :default) (not blur?))
|
||||
(colors/theme-colors colors/neutral-30 colors/neutral-60 theme)))
|
||||
|
||||
(defn- f-address-input-internal
|
||||
[]
|
||||
(let [status (reagent/atom :default)
|
||||
value (reagent/atom "")
|
||||
focused? (atom false)]
|
||||
(fn [{:keys [scanned-value theme blur? on-change-text on-blur on-focus on-clear on-scan
|
||||
on-detect-ens on-detect-address on-detect-unclassified address-regex ens-regex
|
||||
valid-ens-or-address? container-style]}]
|
||||
(let [on-change (fn [text]
|
||||
(when (not= @value text)
|
||||
(let [address? (when address-regex
|
||||
(boolean (re-matches address-regex text)))
|
||||
ens? (when ens-regex
|
||||
(boolean (re-matches ens-regex text)))]
|
||||
(if (> (count text) 0)
|
||||
(reset! status :typing)
|
||||
(reset! status :active))
|
||||
(reset! value text)
|
||||
(when on-change-text
|
||||
(on-change-text text))
|
||||
(when (and on-detect-ens ens?)
|
||||
(reset! status :loading)
|
||||
(on-detect-ens text #(reset! status :typing)))
|
||||
(when (and address? on-detect-address)
|
||||
(reset! status :loading)
|
||||
(on-detect-address text))
|
||||
(when (and (not address?)
|
||||
(not ens?)
|
||||
on-detect-unclassified)
|
||||
(on-detect-unclassified text)))))
|
||||
on-paste (fn []
|
||||
(clipboard/get-string
|
||||
(fn [clipboard]
|
||||
(when-not (empty? clipboard)
|
||||
(on-change clipboard)
|
||||
(reset! value clipboard)))))
|
||||
on-clear (fn []
|
||||
(reset! value "")
|
||||
(reset! status (if @focused? :active :default))
|
||||
(when on-change-text
|
||||
(on-change-text ""))
|
||||
(when on-clear
|
||||
(on-clear)))
|
||||
on-scan #(when on-scan
|
||||
(on-scan))
|
||||
on-focus (fn []
|
||||
(when (= (count @value) 0)
|
||||
(reset! status :active))
|
||||
(reset! focused? true)
|
||||
(when on-focus (on-focus)))
|
||||
on-blur (fn []
|
||||
(when (= @status :active)
|
||||
(reset! status :default))
|
||||
(reset! focused? false)
|
||||
(when on-blur (on-blur)))
|
||||
placeholder-text-color (get-placeholder-text-color @status theme blur?)]
|
||||
(rn/use-effect (fn []
|
||||
(when-not (empty? scanned-value)
|
||||
(on-change scanned-value)))
|
||||
[scanned-value])
|
||||
[rn/view {:style (style/container container-style)}
|
||||
[rn/text-input
|
||||
{:accessibility-label :address-text-input
|
||||
:style (style/input-text theme)
|
||||
:placeholder (i18n/label :t/name-ens-or-address)
|
||||
:placeholder-text-color placeholder-text-color
|
||||
:default-value @value
|
||||
:auto-complete (when platform/ios? :off)
|
||||
:auto-capitalize :none
|
||||
:auto-correct false
|
||||
:spell-check false
|
||||
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
|
||||
:on-focus on-focus
|
||||
:on-blur on-blur
|
||||
:on-change-text on-change}]
|
||||
(when (or (= @status :default)
|
||||
(= @status :active))
|
||||
[rn/view
|
||||
{:style style/buttons-container
|
||||
:accessibility-label :paste-scan-buttons-container}
|
||||
[button/button
|
||||
{:accessibility-label :paste-button
|
||||
:type :outline
|
||||
:size 24
|
||||
:container-style {:margin-right 8}
|
||||
:inner-style (style/accessory-button blur? theme)
|
||||
:on-press on-paste}
|
||||
(i18n/label :t/paste)]
|
||||
[button/button
|
||||
{:accessibility-label :scan-button
|
||||
:icon-only? true
|
||||
:type :outline
|
||||
:size 24
|
||||
:inner-style (style/accessory-button blur? theme)
|
||||
:on-press on-scan}
|
||||
:main-icons/scan]])
|
||||
(when (= @status :typing)
|
||||
[rn/view
|
||||
{:style style/buttons-container
|
||||
:accessibility-label :clear-button-container}
|
||||
[clear-button
|
||||
{:on-press on-clear
|
||||
:blur? blur?
|
||||
:theme theme}]])
|
||||
(when (and (= @status :loading) (not valid-ens-or-address?))
|
||||
[rn/view
|
||||
{:style style/buttons-container
|
||||
:accessibility-label :loading-button-container}
|
||||
[loading-icon blur? theme]])
|
||||
(when (and (= @status :loading) valid-ens-or-address?)
|
||||
[rn/view
|
||||
{:style style/buttons-container
|
||||
:accessibility-label :positive-button-container}
|
||||
[positive-state-icon theme]])]))))
|
||||
|
||||
(defn address-input-internal
|
||||
[props]
|
||||
[:f> f-address-input-internal props])
|
||||
|
||||
(def address-input
|
||||
(quo.theme/with-theme address-input-internal))
|
||||
(defn address-input
|
||||
[{:keys [default-value blur? on-change-text on-blur on-focus on-clear on-scan
|
||||
on-detect-ens on-detect-address on-detect-unclassified address-regex ens-regex
|
||||
valid-ens-or-address? container-style]}]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
[status set-status] (rn/use-state :default)
|
||||
value (rn/use-ref-atom nil)
|
||||
[_ trigger-render-value] (rn/use-state @value)
|
||||
[focused? set-focused] (rn/use-state false)
|
||||
on-change (rn/use-callback
|
||||
(fn [text]
|
||||
(let [address? (when address-regex
|
||||
(boolean (re-matches address-regex text)))
|
||||
ens? (when ens-regex
|
||||
(boolean (re-matches ens-regex text)))]
|
||||
(reset! value text)
|
||||
(if (> (count text) 0)
|
||||
(set-status :typing)
|
||||
(set-status :active))
|
||||
(when on-change-text
|
||||
(on-change-text text))
|
||||
(when (and on-detect-ens ens?)
|
||||
(set-status :loading)
|
||||
(on-detect-ens text #(set-status :typing)))
|
||||
(when (and address? on-detect-address)
|
||||
(set-status :loading)
|
||||
(on-detect-address text))
|
||||
(when (and (not address?)
|
||||
(not ens?)
|
||||
on-detect-unclassified)
|
||||
(on-detect-unclassified text)))))
|
||||
set-value (rn/use-callback
|
||||
(fn [new-value]
|
||||
(reset! value new-value)
|
||||
(on-change new-value)
|
||||
(trigger-render-value new-value)))
|
||||
on-paste (rn/use-callback
|
||||
(fn []
|
||||
(clipboard/get-string
|
||||
(fn [clipboard]
|
||||
(when-not (empty? clipboard)
|
||||
(set-value clipboard))))))
|
||||
on-clear (rn/use-callback
|
||||
(fn []
|
||||
(set-value "")
|
||||
(set-status (if focused? :active :default))
|
||||
(when on-change-text
|
||||
(on-change-text ""))
|
||||
(when on-clear
|
||||
(on-clear)))
|
||||
[focused?])
|
||||
on-clear (rn/use-callback
|
||||
(fn []
|
||||
(set-value "")
|
||||
(set-status (if focused? :active :default))
|
||||
(when on-change-text
|
||||
(on-change-text ""))
|
||||
(when on-clear
|
||||
(on-clear)))
|
||||
[focused?])
|
||||
on-scan (when on-scan (rn/use-callback #(on-scan set-value)))
|
||||
on-focus (rn/use-callback
|
||||
(fn []
|
||||
(when (= (count @value) 0)
|
||||
(set-status :active))
|
||||
(set-focused true)
|
||||
(when on-focus (on-focus))))
|
||||
on-blur (rn/use-callback
|
||||
(fn []
|
||||
(when (= status :active)
|
||||
(set-status :default))
|
||||
(set-focused false)
|
||||
(when on-blur (on-blur)))
|
||||
[status])
|
||||
placeholder-text-color (rn/use-memo #(get-placeholder-text-color status theme blur?)
|
||||
[status theme blur?])]
|
||||
(rn/use-mount #(on-change (or default-value "")))
|
||||
[rn/view {:style (style/container container-style)}
|
||||
[rn/text-input
|
||||
{:accessibility-label :address-text-input
|
||||
:style (style/input-text theme)
|
||||
:placeholder (i18n/label :t/name-ens-or-address)
|
||||
:placeholder-text-color placeholder-text-color
|
||||
:value @value
|
||||
:auto-complete (when platform/ios? :off)
|
||||
:auto-capitalize :none
|
||||
:auto-correct false
|
||||
:spell-check false
|
||||
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
|
||||
:on-focus on-focus
|
||||
:on-blur on-blur
|
||||
:on-change-text on-change}]
|
||||
(when (or (= status :default)
|
||||
(= status :active))
|
||||
[rn/view
|
||||
{:style style/buttons-container
|
||||
:accessibility-label :paste-scan-buttons-container}
|
||||
[button/button
|
||||
{:accessibility-label :paste-button
|
||||
:type :outline
|
||||
:size 24
|
||||
:container-style {:margin-right 8}
|
||||
:inner-style (style/accessory-button blur? theme)
|
||||
:on-press on-paste}
|
||||
(i18n/label :t/paste)]
|
||||
(when on-scan
|
||||
[button/button
|
||||
{:accessibility-label :scan-button
|
||||
:icon-only? true
|
||||
:type :outline
|
||||
:size 24
|
||||
:inner-style (style/accessory-button blur? theme)
|
||||
:on-press on-scan}
|
||||
:main-icons/scan])])
|
||||
(when (= status :typing)
|
||||
[rn/view
|
||||
{:style style/buttons-container
|
||||
:accessibility-label :clear-button-container}
|
||||
[clear-button
|
||||
{:on-press on-clear
|
||||
:blur? blur?
|
||||
:theme theme}]])
|
||||
(when (and (= status :loading) (not valid-ens-or-address?))
|
||||
[rn/view
|
||||
{:style style/buttons-container
|
||||
:accessibility-label :loading-button-container}
|
||||
[loading-icon blur? theme]])
|
||||
(when (and (= status :loading) valid-ens-or-address?)
|
||||
[rn/view
|
||||
{:style style/buttons-container
|
||||
:accessibility-label :positive-button-container}
|
||||
[positive-state-icon theme]])]))
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.platform :as platform]))
|
||||
|
||||
(defn- label-&-counter
|
||||
[{:keys [label current-chars char-limit variant-colors]}]
|
||||
@@ -61,99 +60,104 @@
|
||||
:container-style])
|
||||
|
||||
(defn- base-input
|
||||
[{:keys [on-change-text on-char-limit-reach weight default-value]}]
|
||||
(let [status (reagent/atom :default)
|
||||
internal-on-focus #(reset! status :focus)
|
||||
internal-on-blur #(reset! status :default)
|
||||
multiple-lines? (reagent/atom false)
|
||||
set-multiple-lines! #(let [height (oops/oget % "nativeEvent.contentSize.height")
|
||||
;; In Android height comes with padding
|
||||
min-height (if platform/android? 40 22)]
|
||||
(if (> height min-height)
|
||||
(reset! multiple-lines? true)
|
||||
(reset! multiple-lines? false)))
|
||||
char-count (reagent/atom (count default-value))
|
||||
update-char-limit! (fn [new-text char-limit]
|
||||
(when on-change-text (on-change-text new-text))
|
||||
(let [amount-chars (count new-text)]
|
||||
(reset! char-count amount-chars)
|
||||
(when (and (>= amount-chars char-limit) on-char-limit-reach)
|
||||
(on-char-limit-reach amount-chars))))]
|
||||
(fn [{:keys [blur? theme error? right-icon left-icon disabled? small? button
|
||||
label char-limit multiline? clearable? on-focus on-blur container-style]
|
||||
:as props}]
|
||||
(let [status-kw (cond
|
||||
disabled? :disabled
|
||||
error? :error
|
||||
:else @status)
|
||||
colors-by-status (style/status-colors status-kw blur? theme)
|
||||
variant-colors (style/variants-colors blur? theme)
|
||||
clean-props (apply dissoc props custom-props)]
|
||||
[rn/view {:style container-style}
|
||||
(when (or label char-limit)
|
||||
[label-&-counter
|
||||
{:variant-colors variant-colors
|
||||
:label label
|
||||
:current-chars @char-count
|
||||
:char-limit char-limit}])
|
||||
[rn/view {:style (style/input-container colors-by-status small? disabled?)}
|
||||
(when-let [{:keys [icon-name]} left-icon]
|
||||
[left-accessory
|
||||
{:variant-colors variant-colors
|
||||
:small? small?
|
||||
:icon-name icon-name}])
|
||||
[rn/text-input
|
||||
(cond-> {:style (style/input colors-by-status small? @multiple-lines? weight)
|
||||
:accessibility-label :input
|
||||
:placeholder-text-color (:placeholder colors-by-status)
|
||||
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
|
||||
:cursor-color (:cursor variant-colors)
|
||||
:editable (not disabled?)
|
||||
:on-focus (fn []
|
||||
(when on-focus (on-focus))
|
||||
(internal-on-focus))
|
||||
:on-blur (fn []
|
||||
(when on-blur (on-blur))
|
||||
(internal-on-blur))}
|
||||
:always (merge clean-props)
|
||||
multiline? (assoc :multiline true
|
||||
:on-content-size-change set-multiple-lines!)
|
||||
char-limit (assoc :on-change-text #(update-char-limit! % char-limit)))]
|
||||
(when-let [{:keys [on-press icon-name style-fn]} right-icon]
|
||||
[right-accessory
|
||||
{:variant-colors variant-colors
|
||||
:small? small?
|
||||
:disabled? disabled?
|
||||
:icon-style-fn style-fn
|
||||
:icon-name icon-name
|
||||
:on-press (fn []
|
||||
(when clearable? (reset! char-count 0))
|
||||
(on-press))}])
|
||||
(when-let [{:keys [on-press text]} button]
|
||||
[right-button
|
||||
{:colors-by-status colors-by-status
|
||||
:variant-colors variant-colors
|
||||
:small? small?
|
||||
:disabled? disabled?
|
||||
:on-press on-press
|
||||
:text text}])]]))))
|
||||
[{:keys [blur? theme error? right-icon left-icon disabled? small? button
|
||||
label char-limit multiline? clearable? on-focus on-blur container-style
|
||||
on-change-text on-char-limit-reach weight default-value]
|
||||
:as props}]
|
||||
(let [[status set-status] (rn/use-state :default)
|
||||
internal-on-focus (rn/use-callback #(set-status :focus))
|
||||
internal-on-blur (rn/use-callback #(set-status :default))
|
||||
[multiple-lines?
|
||||
set-multiple-lines] (rn/use-state false)
|
||||
on-content-size-change (rn/use-callback
|
||||
(fn [event]
|
||||
(let [height (oops/oget event "nativeEvent.contentSize.height")
|
||||
;; In Android height comes with padding
|
||||
min-height (if platform/android? 40 22)]
|
||||
(if (> height min-height)
|
||||
(set-multiple-lines true)
|
||||
(set-multiple-lines false)))))
|
||||
[char-count
|
||||
set-char-count] (rn/use-state (count default-value))
|
||||
on-change-text (rn/use-callback
|
||||
(fn [new-text]
|
||||
(when on-change-text (on-change-text new-text))
|
||||
(let [amount-chars (count new-text)]
|
||||
(set-char-count amount-chars)
|
||||
(when (and (>= amount-chars char-limit) on-char-limit-reach)
|
||||
(on-char-limit-reach amount-chars)))))
|
||||
status-kw (cond
|
||||
disabled? :disabled
|
||||
error? :error
|
||||
:else status)
|
||||
colors-by-status (style/status-colors status-kw blur? theme)
|
||||
variant-colors (style/variants-colors blur? theme)
|
||||
clean-props (apply dissoc props custom-props)]
|
||||
[rn/view {:style container-style}
|
||||
(when (or label char-limit)
|
||||
[label-&-counter
|
||||
{:variant-colors variant-colors
|
||||
:label label
|
||||
:current-chars char-count
|
||||
:char-limit char-limit}])
|
||||
[rn/view {:style (style/input-container colors-by-status small? disabled?)}
|
||||
(when-let [{:keys [icon-name]} left-icon]
|
||||
[left-accessory
|
||||
{:variant-colors variant-colors
|
||||
:small? small?
|
||||
:icon-name icon-name}])
|
||||
[rn/text-input
|
||||
(cond-> {:style (style/input colors-by-status small? multiple-lines? weight)
|
||||
:accessibility-label :input
|
||||
:placeholder-text-color (:placeholder colors-by-status)
|
||||
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
|
||||
:cursor-color (:cursor variant-colors)
|
||||
:editable (not disabled?)
|
||||
:on-focus (fn []
|
||||
(when on-focus (on-focus))
|
||||
(internal-on-focus))
|
||||
:on-blur (fn []
|
||||
(when on-blur (on-blur))
|
||||
(internal-on-blur))}
|
||||
:always (merge clean-props)
|
||||
multiline? (assoc :multiline true
|
||||
:on-content-size-change on-content-size-change)
|
||||
char-limit (assoc :on-change-text on-change-text))]
|
||||
(when-let [{:keys [on-press icon-name style-fn]} right-icon]
|
||||
[right-accessory
|
||||
{:variant-colors variant-colors
|
||||
:small? small?
|
||||
:disabled? disabled?
|
||||
:icon-style-fn style-fn
|
||||
:icon-name icon-name
|
||||
:on-press (fn []
|
||||
(when clearable? (set-char-count 0))
|
||||
(on-press))}])
|
||||
(when-let [{:keys [on-press text]} button]
|
||||
[right-button
|
||||
{:colors-by-status colors-by-status
|
||||
:variant-colors variant-colors
|
||||
:small? small?
|
||||
:disabled? disabled?
|
||||
:on-press on-press
|
||||
:text text}])]]))
|
||||
|
||||
(defn- password-input
|
||||
[{:keys [default-shown?]
|
||||
:or {default-shown? false}}]
|
||||
(let [password-shown? (reagent/atom default-shown?)]
|
||||
(fn [props]
|
||||
[base-input
|
||||
(assoc props
|
||||
:accessibility-label :password-input
|
||||
:auto-capitalize :none
|
||||
:auto-complete :password
|
||||
:secure-text-entry (not @password-shown?)
|
||||
:right-icon {:style-fn style/password-icon
|
||||
:icon-name (if @password-shown? :i/hide-password :i/reveal)
|
||||
:on-press #(swap! password-shown? not)})])))
|
||||
:or {default-shown? false}
|
||||
:as props}]
|
||||
(let [[password-shown? set-password-shown] (rn/use-state default-shown?)]
|
||||
[base-input
|
||||
(assoc props
|
||||
:accessibility-label :password-input
|
||||
:auto-capitalize :none
|
||||
:auto-complete :password
|
||||
:secure-text-entry (not password-shown?)
|
||||
:right-icon {:style-fn style/password-icon
|
||||
:icon-name (if password-shown? :i/hide-password :i/reveal)
|
||||
:on-press #(set-password-shown (not password-shown?))})]))
|
||||
|
||||
(defn input-internal
|
||||
(defn input
|
||||
"This input supports the following properties:
|
||||
- :type - Can be `:text`(default) or `:password`.
|
||||
- :blur? - Boolean to set the blur color variant.
|
||||
@@ -177,21 +181,15 @@
|
||||
- :on-change-text
|
||||
...
|
||||
"
|
||||
[{:keys [type clearable? on-clear on-change-text icon-name]
|
||||
[{:keys [type clearable? on-clear icon-name]
|
||||
:or {type :text}
|
||||
:as props}]
|
||||
(let [base-props (cond-> props
|
||||
icon-name (assoc-in [:left-icon :icon-name] icon-name)
|
||||
clearable? (assoc :right-icon
|
||||
{:style-fn style/clear-icon
|
||||
:icon-name :i/clear
|
||||
:on-press #(when on-clear (on-clear))})
|
||||
on-change-text (assoc :on-change-text
|
||||
(fn [new-text]
|
||||
(on-change-text new-text)
|
||||
(reagent/flush))))]
|
||||
icon-name (assoc-in [:left-icon :icon-name] icon-name)
|
||||
clearable? (assoc :right-icon
|
||||
{:style-fn style/clear-icon
|
||||
:icon-name :i/clear
|
||||
:on-press #(when on-clear (on-clear))}))]
|
||||
(if (= type :password)
|
||||
[password-input base-props]
|
||||
[base-input base-props])))
|
||||
|
||||
(def input (quo.theme/with-theme input-internal))
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
{:keys [ok-words error-words]} (group-by #(if (string? %) :ok-words :error-words)
|
||||
children-text-nodes)]
|
||||
(h/is-equal (apply str ok-words) "Text with some that the ")
|
||||
(h/is-truthy (= (map #(-> % :props :argv second) error-words)
|
||||
(h/is-truthy (= (map #(-> % :props :argv first) error-words)
|
||||
["error" "words" "don't" "satisfy" "predicate"]))))
|
||||
|
||||
(h/test "Marked when words exceed the limit given"
|
||||
@@ -55,7 +55,7 @@
|
||||
(h/is-equal (string/trim (apply str ok-words))
|
||||
"these are ok words,")
|
||||
(h/is-equal (->> error-words
|
||||
(map #(-> % :props :argv second))
|
||||
(map #(-> % :props :argv first))
|
||||
(interpose " ")
|
||||
(apply str))
|
||||
"these words exceed the limit")))))
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
[clojure.string :as string]
|
||||
[quo.components.inputs.recovery-phrase.style :as style]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(def ^:private custom-props
|
||||
[:customization-color :theme :blur? :cursor-color :multiline :on-focus :on-blur
|
||||
@@ -38,42 +37,42 @@
|
||||
:idx 0})
|
||||
:result)))
|
||||
|
||||
(defn recovery-phrase-input-internal
|
||||
[_ _]
|
||||
(let [state (reagent/atom :default)
|
||||
set-focused #(reset! state :focused)
|
||||
set-default #(reset! state :default)]
|
||||
(fn [{:keys [customization-color theme blur? on-focus on-blur mark-errors?
|
||||
error-pred-current-word error-pred-written-words word-limit
|
||||
container-style]
|
||||
:or {customization-color :blue
|
||||
word-limit ##Inf
|
||||
error-pred-current-word (constantly false)
|
||||
error-pred-written-words (constantly false)}
|
||||
:as props}
|
||||
text]
|
||||
(let [extra-props (apply dissoc props custom-props)]
|
||||
[rn/view {:style (style/container container-style)}
|
||||
[rn/text-input
|
||||
(merge {:accessibility-label :recovery-phrase-input
|
||||
:style (style/input)
|
||||
:placeholder-text-color (style/placeholder-color @state theme blur?)
|
||||
:cursor-color (style/cursor-color customization-color theme)
|
||||
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
|
||||
:multiline true
|
||||
:on-focus (fn []
|
||||
(set-focused)
|
||||
(when on-focus (on-focus)))
|
||||
:on-blur (fn []
|
||||
(set-default)
|
||||
(when on-blur (on-blur)))}
|
||||
extra-props)
|
||||
(if mark-errors?
|
||||
(mark-error-words {:pred-last-word error-pred-current-word
|
||||
:pred-previous-words error-pred-written-words
|
||||
:text text
|
||||
:word-limit word-limit
|
||||
:theme theme})
|
||||
text)]]))))
|
||||
|
||||
(def recovery-phrase-input (quo.theme/with-theme recovery-phrase-input-internal))
|
||||
(defn recovery-phrase-input
|
||||
[{:keys [customization-color blur? on-focus on-blur mark-errors?
|
||||
error-pred-current-word error-pred-written-words word-limit
|
||||
container-style]
|
||||
:or {customization-color :blue
|
||||
word-limit ##Inf
|
||||
error-pred-current-word (constantly false)
|
||||
error-pred-written-words (constantly false)}
|
||||
:as props}
|
||||
text]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
[state set-state] (rn/use-state :default)
|
||||
on-focus (rn/use-callback
|
||||
(fn []
|
||||
(set-state :focused)
|
||||
(when on-focus (on-focus))))
|
||||
on-blur (rn/use-callback
|
||||
(fn []
|
||||
(set-state :default)
|
||||
(when on-blur (on-blur))))
|
||||
extra-props (apply dissoc props custom-props)]
|
||||
[rn/view {:style (style/container container-style)}
|
||||
[rn/text-input
|
||||
(merge {:accessibility-label :recovery-phrase-input
|
||||
:style (style/input)
|
||||
:placeholder-text-color (style/placeholder-color state theme blur?)
|
||||
:cursor-color (style/cursor-color customization-color theme)
|
||||
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
|
||||
:multiline true
|
||||
:on-focus on-focus
|
||||
:on-blur on-blur}
|
||||
extra-props)
|
||||
(if mark-errors?
|
||||
(mark-error-words {:pred-last-word error-pred-current-word
|
||||
:pred-previous-words error-pred-written-words
|
||||
:text text
|
||||
:word-limit word-limit
|
||||
:theme theme})
|
||||
text)]]))
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
[quo.components.icon :as icon]
|
||||
[quo.components.inputs.search-input.style :as style]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(def ^:private tag-separator [rn/view {:style style/tag-separator}])
|
||||
|
||||
@@ -41,54 +40,52 @@
|
||||
text-input))
|
||||
|
||||
(defn search-input
|
||||
[{:keys [value]}]
|
||||
(let [state (reagent/atom :default)
|
||||
set-active #(reset! state :active)
|
||||
set-default #(reset! state :default)
|
||||
scroll-view-ref (atom nil)
|
||||
use-value? (boolean value)]
|
||||
(fn [{:keys [value tags disabled? blur? on-change-text customization-color
|
||||
on-clear on-focus on-blur override-theme container-style]
|
||||
:or {customization-color :blue}
|
||||
:as props}
|
||||
& children]
|
||||
(let [clean-props (apply dissoc props props-to-remove)]
|
||||
[rn/view
|
||||
{:accessibility-label :search-input
|
||||
:style (style/container container-style)}
|
||||
[rn/scroll-view
|
||||
{:ref #(reset! scroll-view-ref %)
|
||||
:style style/scroll-container
|
||||
:content-container-style style/scroll-content
|
||||
:horizontal true
|
||||
:shows-horizontal-scroll-indicator false}
|
||||
(when (seq tags)
|
||||
[inner-tags tags])
|
||||
|
||||
(add-children
|
||||
[rn/text-input
|
||||
(cond-> {:style (style/input-text disabled?)
|
||||
:cursor-color (style/cursor customization-color override-theme)
|
||||
:placeholder-text-color (style/placeholder-color @state blur? override-theme)
|
||||
:editable (not disabled?)
|
||||
:on-key-press #(handle-backspace % @scroll-view-ref)
|
||||
:keyboard-appearance (colors/theme-colors :light :dark override-theme)
|
||||
:on-change-text (fn [new-text]
|
||||
(when on-change-text
|
||||
(on-change-text new-text))
|
||||
(reagent/flush))
|
||||
:on-focus (fn []
|
||||
(set-active)
|
||||
(when on-focus (on-focus)))
|
||||
:on-blur (fn []
|
||||
(set-default)
|
||||
(when on-blur (on-blur)))}
|
||||
use-value? (assoc :value value)
|
||||
(seq clean-props) (merge clean-props))]
|
||||
(when-not use-value? children))]
|
||||
|
||||
(when (or (seq value) (seq children))
|
||||
[clear-button
|
||||
{:on-press on-clear
|
||||
:blur? blur?
|
||||
:override-theme override-theme}])]))))
|
||||
[{:keys [value tags disabled? blur? on-change-text customization-color
|
||||
on-clear on-focus on-blur override-theme container-style]
|
||||
:or {customization-color :blue}
|
||||
:as props}
|
||||
& children]
|
||||
(let [[state set-state] (rn/use-state :default)
|
||||
on-focus (rn/use-callback
|
||||
(fn []
|
||||
(set-state :active)
|
||||
(when on-focus (on-focus))))
|
||||
on-blur (rn/use-callback
|
||||
(fn []
|
||||
(set-state :default)
|
||||
(when on-blur (on-blur))))
|
||||
scroll-view-ref (rn/use-ref-atom nil)
|
||||
on-scroll-view-ref (rn/use-callback #(reset! scroll-view-ref %))
|
||||
on-key-press (rn/use-callback #(handle-backspace % @scroll-view-ref))
|
||||
use-value? (boolean value)
|
||||
clean-props (apply dissoc props props-to-remove)]
|
||||
[rn/view
|
||||
{:accessibility-label :search-input
|
||||
:style (style/container container-style)}
|
||||
[rn/scroll-view
|
||||
{:ref on-scroll-view-ref
|
||||
:style style/scroll-container
|
||||
:content-container-style style/scroll-content
|
||||
:horizontal true
|
||||
:shows-horizontal-scroll-indicator false}
|
||||
(when (seq tags)
|
||||
[inner-tags tags])
|
||||
(add-children
|
||||
[rn/text-input
|
||||
(cond-> {:style (style/input-text disabled?)
|
||||
:cursor-color (style/cursor customization-color override-theme)
|
||||
:placeholder-text-color (style/placeholder-color state blur? override-theme)
|
||||
:editable (not disabled?)
|
||||
:on-key-press on-key-press
|
||||
:keyboard-appearance (colors/theme-colors :light :dark override-theme)
|
||||
:on-change-text on-change-text
|
||||
:on-focus on-focus
|
||||
:on-blur on-blur}
|
||||
use-value? (assoc :value value)
|
||||
(seq clean-props) (merge clean-props))]
|
||||
(when-not use-value? children))]
|
||||
(when (or (seq value) (seq children))
|
||||
[clear-button
|
||||
{:on-press on-clear
|
||||
:blur? blur?
|
||||
:override-theme override-theme}])]))
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
[quo.components.inputs.title-input.style :as style]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- pad-0
|
||||
[value]
|
||||
@@ -13,81 +12,70 @@
|
||||
(str 0 value)
|
||||
value))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [blur?
|
||||
on-change-text
|
||||
auto-focus
|
||||
placeholder
|
||||
max-length
|
||||
default-value
|
||||
return-key-type
|
||||
size
|
||||
theme
|
||||
on-focus
|
||||
on-blur
|
||||
container-style]
|
||||
(defn view
|
||||
[{:keys [blur? on-change-text auto-focus placeholder max-length default-value return-key-type
|
||||
size on-focus on-blur container-style customization-color disabled?]
|
||||
:or {max-length 0
|
||||
auto-focus false
|
||||
default-value ""}}]
|
||||
(let [focused? (reagent/atom auto-focus)
|
||||
value (reagent/atom default-value)
|
||||
input-ref (atom nil)
|
||||
on-change (fn [v]
|
||||
(reset! value v)
|
||||
(when on-change-text
|
||||
(on-change-text v)))]
|
||||
(fn [{:keys [customization-color disabled?]}]
|
||||
[rn/view
|
||||
{:style (merge (style/container disabled?) container-style)}
|
||||
[rn/view {:style style/text-input-container}
|
||||
[rn/text-input
|
||||
{:style
|
||||
(text/text-style
|
||||
{:size (or size :heading-1)
|
||||
:weight :semi-bold
|
||||
:style (style/title-text theme)})
|
||||
:default-value default-value
|
||||
:accessibility-label :profile-title-input
|
||||
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
|
||||
:return-key-type return-key-type
|
||||
:on-focus (fn []
|
||||
(when (fn? on-focus)
|
||||
(on-focus))
|
||||
(reset! focused? true))
|
||||
:on-blur (fn []
|
||||
(when (fn? on-blur)
|
||||
(on-blur))
|
||||
(reset! focused? false))
|
||||
:auto-focus auto-focus
|
||||
:input-mode :text
|
||||
:on-change-text on-change
|
||||
:editable (not disabled?)
|
||||
:max-length max-length
|
||||
:placeholder placeholder
|
||||
:ref #(reset! input-ref %)
|
||||
:selection-color (style/get-selection-color customization-color blur? theme)
|
||||
:placeholder-text-color (if @focused?
|
||||
(style/get-focused-placeholder-color blur? theme)
|
||||
(style/get-placeholder-color blur? theme))}]]
|
||||
[rn/view
|
||||
{:style (style/counter-container @focused?)}
|
||||
(if @focused?
|
||||
[text/text
|
||||
[text/text
|
||||
{:style (style/char-count blur? theme)
|
||||
:size :paragraph-2}
|
||||
(pad-0
|
||||
(str
|
||||
(count @value)))]
|
||||
[text/text
|
||||
{:style (style/char-count blur? theme)
|
||||
:size :paragraph-2}
|
||||
(str "/"
|
||||
(pad-0
|
||||
(str max-length)))]]
|
||||
[rn/pressable
|
||||
{:on-press #(when-not disabled?
|
||||
(.focus ^js @input-ref))}
|
||||
[icon/icon :i/edit {:color (style/get-char-count-color blur? theme)}]])]])))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
[focused? set-focused] (rn/use-state auto-focus)
|
||||
[value set-value] (rn/use-state default-value)
|
||||
input-ref (rn/use-ref-atom nil)
|
||||
on-inpur-ref (rn/use-callback #(reset! input-ref %))
|
||||
on-press (rn/use-callback
|
||||
#(when-not disabled? (.focus ^js @input-ref))
|
||||
[disabled?])
|
||||
on-change (rn/use-callback
|
||||
(fn [v]
|
||||
(set-value v)
|
||||
(when on-change-text (on-change-text v))))
|
||||
on-focus (rn/use-callback
|
||||
(fn []
|
||||
(when (fn? on-focus) (on-focus))
|
||||
(set-focused true)))
|
||||
on-blur (rn/use-callback
|
||||
(fn []
|
||||
(when (fn? on-blur) (on-blur))
|
||||
(set-focused false)))]
|
||||
[rn/view
|
||||
{:style (merge (style/container disabled?) container-style)}
|
||||
[rn/view {:style style/text-input-container}
|
||||
[rn/text-input
|
||||
{:style (text/text-style
|
||||
{:size (or size :heading-1)
|
||||
:weight :semi-bold
|
||||
:style (style/title-text theme)})
|
||||
:default-value default-value
|
||||
:accessibility-label :profile-title-input
|
||||
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
|
||||
:return-key-type return-key-type
|
||||
:on-focus on-focus
|
||||
:on-blur on-blur
|
||||
:auto-focus auto-focus
|
||||
:input-mode :text
|
||||
:on-change-text on-change
|
||||
:editable (not disabled?)
|
||||
:max-length max-length
|
||||
:placeholder placeholder
|
||||
:ref on-inpur-ref
|
||||
:selection-color (style/get-selection-color customization-color blur? theme)
|
||||
:placeholder-text-color (if focused?
|
||||
(style/get-focused-placeholder-color blur? theme)
|
||||
(style/get-placeholder-color blur? theme))}]]
|
||||
[rn/view
|
||||
{:style (style/counter-container focused?)}
|
||||
(if focused?
|
||||
[text/text
|
||||
[text/text
|
||||
{:style (style/char-count blur? theme)
|
||||
:size :paragraph-2}
|
||||
(pad-0
|
||||
(str
|
||||
(count value)))]
|
||||
[text/text
|
||||
{:style (style/char-count blur? theme)
|
||||
:size :paragraph-2}
|
||||
(str "/" (pad-0 (str max-length)))]]
|
||||
[rn/pressable {:on-press on-press}
|
||||
[icon/icon :i/edit {:color (style/get-char-count-color blur? theme)}]])]]))
|
||||
|
||||
@@ -122,6 +122,8 @@
|
||||
|
||||
(def memo react/memo)
|
||||
|
||||
(def use-state react/useState)
|
||||
|
||||
(def create-ref react/createRef)
|
||||
|
||||
(def use-ref react/useRef)
|
||||
@@ -179,6 +181,10 @@
|
||||
([handler deps]
|
||||
(react/useCallback handler (get-js-deps deps))))
|
||||
|
||||
(defn use-memo
|
||||
[handler deps]
|
||||
(react/useMemo handler (get-js-deps deps)))
|
||||
|
||||
(def layout-animation (.-LayoutAnimation ^js react-native))
|
||||
(def configure-next (.-configureNext ^js layout-animation))
|
||||
|
||||
@@ -204,3 +210,5 @@
|
||||
(def linking (.-Linking react-native))
|
||||
|
||||
(defn open-url [link] (.openURL ^js linking link))
|
||||
|
||||
(def set-status-bar-style react-native/StatusBar.setBarStyle)
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.hooks :as hooks]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.bottom-sheet.style :as style]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -57,83 +56,77 @@
|
||||
(show translate-y bg-opacity)
|
||||
(hide translate-y bg-opacity window-height on-close))))))
|
||||
|
||||
(defn- f-view
|
||||
[_ _]
|
||||
(let [sheet-height (reagent/atom 0)
|
||||
item-height (reagent/atom 0)]
|
||||
(fn [{:keys [hide? insets theme]}
|
||||
{:keys [content selected-item padding-bottom-override border-radius on-close shell?
|
||||
gradient-cover? customization-color hide-handle? blur-radius]
|
||||
:or {border-radius 12}}]
|
||||
(let [{window-height :height} (rn/get-window)
|
||||
bg-opacity (reanimated/use-shared-value 0)
|
||||
translate-y (reanimated/use-shared-value window-height)
|
||||
sheet-gesture (get-sheet-gesture translate-y
|
||||
bg-opacity
|
||||
window-height
|
||||
on-close)
|
||||
selected-item-smaller-than-sheet? (< @item-height
|
||||
(- window-height
|
||||
@sheet-height
|
||||
(:top insets)
|
||||
(:bottom insets)
|
||||
bottom-margin))
|
||||
top (- window-height (:top insets) @sheet-height)
|
||||
bottom (if selected-item-smaller-than-sheet?
|
||||
(+ @sheet-height bottom-margin)
|
||||
(:bottom insets))]
|
||||
(rn/use-effect
|
||||
#(if hide?
|
||||
(hide translate-y bg-opacity window-height on-close)
|
||||
(show translate-y bg-opacity))
|
||||
[hide?])
|
||||
(hooks/use-back-handler (fn []
|
||||
(when (fn? on-close)
|
||||
(on-close))
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
true))
|
||||
[rn/view {:style {:flex 1}}
|
||||
;; backdrop
|
||||
[rn/pressable
|
||||
{:on-press #(rf/dispatch [:hide-bottom-sheet])
|
||||
:style {:flex 1}}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity bg-opacity}
|
||||
{:flex 1 :background-color colors/neutral-100-opa-70})}]]
|
||||
;; sheet
|
||||
[gesture/gesture-detector {:gesture sheet-gesture}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:transform [{:translateY translate-y}]}
|
||||
(style/sheet insets window-height selected-item))}
|
||||
(when shell?
|
||||
[blur/ios-view
|
||||
{:style style/shell-bg
|
||||
:blur-radius (or blur-radius 20)
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent}])
|
||||
(when selected-item
|
||||
[rn/view
|
||||
{:on-layout #(reset! item-height (.-nativeEvent.layout.height ^js %))
|
||||
:style
|
||||
(style/selected-item theme top bottom selected-item-smaller-than-sheet? border-radius)}
|
||||
[selected-item]])
|
||||
(defn view
|
||||
[{:keys [hide? insets]}
|
||||
{:keys [content selected-item padding-bottom-override border-radius on-close shell?
|
||||
gradient-cover? customization-color hide-handle? blur-radius]
|
||||
:or {border-radius 12}}]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
sheet-height (rn/use-ref-atom 0)
|
||||
item-height (rn/use-ref-atom 0)
|
||||
{window-height :height} (rn/get-window)
|
||||
bg-opacity (reanimated/use-shared-value 0)
|
||||
translate-y (reanimated/use-shared-value window-height)
|
||||
sheet-gesture (get-sheet-gesture translate-y
|
||||
bg-opacity
|
||||
window-height
|
||||
on-close)
|
||||
selected-item-smaller-than-sheet? (< @item-height
|
||||
(- window-height
|
||||
@sheet-height
|
||||
(:top insets)
|
||||
(:bottom insets)
|
||||
bottom-margin))
|
||||
top (- window-height (:top insets) @sheet-height)
|
||||
bottom (if selected-item-smaller-than-sheet?
|
||||
(+ @sheet-height bottom-margin)
|
||||
(:bottom insets))]
|
||||
(rn/use-effect
|
||||
#(if hide?
|
||||
(hide translate-y bg-opacity window-height on-close)
|
||||
(show translate-y bg-opacity))
|
||||
[hide?])
|
||||
(hooks/use-back-handler (fn []
|
||||
(when (fn? on-close)
|
||||
(on-close))
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
true))
|
||||
[rn/view {:style {:flex 1}}
|
||||
;; backdrop
|
||||
[rn/pressable
|
||||
{:on-press #(rf/dispatch [:hide-bottom-sheet])
|
||||
:style {:flex 1}}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity bg-opacity}
|
||||
{:flex 1 :background-color colors/neutral-100-opa-70})}]]
|
||||
;; sheet
|
||||
[gesture/gesture-detector {:gesture sheet-gesture}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:transform [{:translateY translate-y}]}
|
||||
(style/sheet insets window-height selected-item))}
|
||||
(when shell?
|
||||
[blur/ios-view
|
||||
{:style style/shell-bg
|
||||
:blur-radius (or blur-radius 20)
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent}])
|
||||
(when selected-item
|
||||
[rn/view
|
||||
{:on-layout #(reset! item-height (.-nativeEvent.layout.height ^js %))
|
||||
:style
|
||||
(style/selected-item theme top bottom selected-item-smaller-than-sheet? border-radius)}
|
||||
[selected-item]])
|
||||
|
||||
[rn/view
|
||||
{:style (style/sheet-content theme padding-bottom-override insets shell? bottom-margin)
|
||||
:on-layout #(reset! sheet-height (.-nativeEvent.layout.height ^js %))}
|
||||
(when (and gradient-cover? customization-color)
|
||||
[rn/view {:style style/gradient-bg}
|
||||
[quo/gradient-cover
|
||||
{:customization-color customization-color
|
||||
:opacity 0.4}]])
|
||||
(when-not hide-handle?
|
||||
[quo/drawer-bar])
|
||||
[content]]]]]))))
|
||||
|
||||
(defn- internal-view
|
||||
[args sheet]
|
||||
[:f> f-view args sheet])
|
||||
|
||||
(def view (quo.theme/with-theme internal-view))
|
||||
[rn/view
|
||||
{:style (style/sheet-content theme padding-bottom-override insets shell? bottom-margin)
|
||||
:on-layout #(reset! sheet-height (.-nativeEvent.layout.height ^js %))}
|
||||
(when (and gradient-cover? customization-color)
|
||||
[rn/view {:style style/gradient-bg}
|
||||
[quo/gradient-cover
|
||||
{:customization-color customization-color
|
||||
:opacity 0.4}]])
|
||||
(when-not hide-handle?
|
||||
[quo/drawer-bar])
|
||||
[content]]]]]))
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
(ns status-im.common.scalable-avatar.style)
|
||||
|
||||
(defn wrapper
|
||||
[{:keys [scale margin-top margin border-color]}]
|
||||
[{:transform [{:scale scale}]
|
||||
:margin-top margin-top
|
||||
:margin-left margin
|
||||
:margin-bottom margin}
|
||||
{:border-width 4
|
||||
:border-color border-color
|
||||
:border-radius 100}])
|
||||
+9
-12
@@ -1,16 +1,14 @@
|
||||
(ns status-im.contexts.profile.settings.header.avatar
|
||||
(ns status-im.common.scalable-avatar.view
|
||||
(:require [quo.core :as quo]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.contexts.profile.settings.header.style :as style]))
|
||||
|
||||
[status-im.common.scalable-avatar.style :as style]))
|
||||
(def scroll-animation-input-range [0 50])
|
||||
(def header-extrapolation-option
|
||||
{:extrapolateLeft "clamp"
|
||||
:extrapolateRight "clamp"})
|
||||
|
||||
(defn f-avatar
|
||||
[{:keys [scroll-y full-name online? profile-picture customization-color]}]
|
||||
[{:keys [scroll-y full-name online? profile-picture customization-color border-color]}]
|
||||
(let [image-scale-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[1 0.4]
|
||||
@@ -21,14 +19,13 @@
|
||||
header-extrapolation-option)
|
||||
image-side-margin-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[0 -20]
|
||||
header-extrapolation-option)
|
||||
theme (quo.theme/get-theme)]
|
||||
[-4 -20]
|
||||
header-extrapolation-option)]
|
||||
[reanimated/view
|
||||
{:style (style/avatar-container theme
|
||||
image-scale-animation
|
||||
image-top-margin-animation
|
||||
image-side-margin-animation)}
|
||||
{:style (style/wrapper {:scale image-scale-animation
|
||||
:margin-top image-top-margin-animation
|
||||
:margin image-side-margin-animation
|
||||
:border-color border-color})}
|
||||
[quo/user-avatar
|
||||
{:full-name full-name
|
||||
:online? online?
|
||||
@@ -75,3 +75,9 @@
|
||||
:width picture-diameter
|
||||
:height picture-diameter
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
|
||||
|
||||
(defn cover-background
|
||||
[cover-color]
|
||||
{:background-color cover-color
|
||||
:height 167
|
||||
:margin-bottom -16})
|
||||
|
||||
@@ -98,9 +98,9 @@
|
||||
(defn scroll-page
|
||||
[_ _ _]
|
||||
(let [scroll-height (reagent/atom negative-scroll-position-0)]
|
||||
(fn [{:keys [theme cover-image logo on-scroll
|
||||
(fn [{:keys [theme cover-image cover-color logo on-scroll
|
||||
collapsed? height top-nav title-colum background-color navigate-back? page-nav-props
|
||||
overlay-shown? sticky-header]}
|
||||
overlay-shown? sticky-header children-style]}
|
||||
children]
|
||||
[:<>
|
||||
[:f> f-scroll-page-header
|
||||
@@ -125,6 +125,8 @@
|
||||
"nativeEvent.contentOffset.y")))
|
||||
(when on-scroll
|
||||
(on-scroll @scroll-height)))}
|
||||
(when cover-color
|
||||
[rn/view {:style (style/cover-background cover-color)}])
|
||||
(when cover-image
|
||||
[rn/view {:style {:height (if collapsed? 110 151)}}
|
||||
[rn/image
|
||||
@@ -136,8 +138,10 @@
|
||||
:flex 1}}]])
|
||||
(when children
|
||||
[rn/view
|
||||
{:style (style/children-container {:border-radius (diff-with-max-min @scroll-height 16 0)
|
||||
:background-color background-color})}
|
||||
{:style (style/children-container (merge
|
||||
children-style
|
||||
{:border-radius (diff-with-max-min @scroll-height 16 0)
|
||||
:background-color background-color}))}
|
||||
(when (and (not collapsed?) cover-image)
|
||||
[:f> f-display-picture @scroll-height logo theme])
|
||||
children])]])))
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(defn get-error-message
|
||||
(defn- get-error-message
|
||||
[error]
|
||||
(if (and (some? error)
|
||||
(or (= error "file is not a database")
|
||||
@@ -21,31 +21,62 @@
|
||||
(i18n/label :t/oops-wrong-password)
|
||||
error))
|
||||
|
||||
(defn- on-change-password
|
||||
[entered-password]
|
||||
(debounce/debounce-and-dispatch [:profile/on-password-input-changed
|
||||
{:password (security/mask-data entered-password)
|
||||
:error ""}]
|
||||
100))
|
||||
(defn- error-info
|
||||
[error-message processing shell?]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
on-press (rn/use-callback
|
||||
(fn []
|
||||
(rn/dismiss-keyboard!)
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:content #(forgot-password-doc/view {:shell? shell?})
|
||||
:theme theme
|
||||
:shell? shell?}]))
|
||||
[theme])]
|
||||
[rn/view {:style style/error-message}
|
||||
[quo/info-message
|
||||
{:type :error
|
||||
:size :default
|
||||
:icon :i/info}
|
||||
error-message]
|
||||
[rn/pressable
|
||||
{:hit-slop {:top 6 :bottom 20 :left 0 :right 0}
|
||||
:disabled processing
|
||||
:on-press on-press}
|
||||
[rn/text
|
||||
{:style {:text-decoration-line :underline
|
||||
:color (colors/resolve-color :danger theme)}
|
||||
:size :paragraph-2
|
||||
:suppress-highlighting true}
|
||||
(i18n/label :t/forgot-password)]]]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [default-password theme shell? on-press-biometrics blur?]}]
|
||||
(defn view
|
||||
[{:keys [shell? on-press-biometrics blur?]}]
|
||||
(let [{:keys [error processing]} (rf/sub [:profile/login])
|
||||
error-message (get-error-message error)
|
||||
error? (boolean (seq error-message))]
|
||||
error-message (rn/use-memo #(get-error-message error) [error])
|
||||
error? (boolean (seq error-message))
|
||||
default-value (rn/use-ref-atom "") ;;bug on Android
|
||||
;;https://github.com/status-im/status-mobile/issues/19004
|
||||
on-change-password (rn/use-callback
|
||||
(fn [entered-password]
|
||||
(reset! default-value entered-password)
|
||||
(debounce/debounce-and-dispatch [:profile/on-password-input-changed
|
||||
{:password (security/mask-data
|
||||
entered-password)
|
||||
:error ""}]
|
||||
100)))]
|
||||
[:<>
|
||||
[rn/view {:style {:flex-direction :row}}
|
||||
[quo/input
|
||||
{:container-style {:flex 1}
|
||||
:type :password
|
||||
:default-value @default-value
|
||||
:blur? blur?
|
||||
:disabled? processing
|
||||
:placeholder (i18n/label :t/type-your-password)
|
||||
:auto-focus true
|
||||
:error? error?
|
||||
:label (i18n/label :t/profile-password)
|
||||
:on-change-text on-change-password
|
||||
:default-value (security/safe-unmask-data default-password)}]
|
||||
:on-change-text on-change-password}]
|
||||
(when on-press-biometrics
|
||||
[quo/button
|
||||
{:container-style style/auth-button
|
||||
@@ -55,26 +86,5 @@
|
||||
:type :outline}
|
||||
:i/face-id])]
|
||||
(when error?
|
||||
[rn/view {:style style/error-message}
|
||||
[quo/info-message
|
||||
{:type :error
|
||||
:size :default
|
||||
:icon :i/info}
|
||||
error-message]
|
||||
[rn/pressable
|
||||
{:hit-slop {:top 6 :bottom 20 :left 0 :right 0}
|
||||
:disabled processing
|
||||
:on-press (fn []
|
||||
(rn/dismiss-keyboard!)
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:content #(forgot-password-doc/view {:shell? shell?})
|
||||
:theme theme
|
||||
:shell? shell?}]))}
|
||||
[rn/text
|
||||
{:style {:text-decoration-line :underline
|
||||
:color (colors/resolve-color :danger theme)}
|
||||
:size :paragraph-2
|
||||
:suppress-highlighting true}
|
||||
(i18n/label :t/forgot-password)]]])]))
|
||||
[error-info error-message processing shell?])]))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
@@ -3,45 +3,32 @@
|
||||
[quo.core :as quo]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.standard-authentication.standard-auth.authorize :as authorize]
|
||||
[status-im.constants :as constants]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- view-internal
|
||||
[_]
|
||||
(let [reset-slider? (reagent/atom false)
|
||||
on-close (fn []
|
||||
(js/setTimeout
|
||||
#(reset! reset-slider? true)
|
||||
200))
|
||||
(defn view
|
||||
[{:keys [track-text customization-color auth-button-label on-auth-success on-auth-fail
|
||||
auth-button-icon-left size blur? container-style]
|
||||
:or {container-style {:flex 1}}}]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
auth-method (rf/sub [:auth-method])
|
||||
biometric-auth? (= auth-method constants/auth-method-biometric)]
|
||||
(fn [{:keys [track-text
|
||||
customization-color
|
||||
auth-button-label
|
||||
on-auth-success
|
||||
on-auth-fail
|
||||
auth-button-icon-left
|
||||
size
|
||||
theme
|
||||
blur?
|
||||
container-style]
|
||||
:or {container-style {:flex 1}}}]
|
||||
[rn/view {:style container-style}
|
||||
[quo/slide-button
|
||||
{:size size
|
||||
:customization-color customization-color
|
||||
:on-reset (when @reset-slider? #(reset! reset-slider? false))
|
||||
:on-complete #(authorize/authorize {:on-close on-close
|
||||
:auth-button-icon-left auth-button-icon-left
|
||||
:theme theme
|
||||
:blur? blur?
|
||||
:biometric-auth? biometric-auth?
|
||||
:on-auth-success on-auth-success
|
||||
:on-auth-fail on-auth-fail
|
||||
:auth-button-label auth-button-label})
|
||||
:track-icon (if biometric-auth? :i/face-id :password)
|
||||
:track-text track-text}]])))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
biometric-auth? (= auth-method constants/auth-method-biometric)
|
||||
on-complete (rn/use-callback
|
||||
(fn [reset]
|
||||
(authorize/authorize {:on-close #(js/setTimeout reset 200)
|
||||
:auth-button-icon-left auth-button-icon-left
|
||||
:theme theme
|
||||
:blur? blur?
|
||||
:biometric-auth? biometric-auth?
|
||||
:on-auth-success on-auth-success
|
||||
:on-auth-fail on-auth-fail
|
||||
:auth-button-label auth-button-label}))
|
||||
[theme])]
|
||||
[quo/slide-button
|
||||
{:container-style container-style
|
||||
:size size
|
||||
:customization-color customization-color
|
||||
:on-complete on-complete
|
||||
:track-icon (if biometric-auth? :i/face-id :password)
|
||||
:track-text track-text}]))
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
[status-im.contexts.chat.messenger.messages.delete-message-for-me.events :as delete-for-me]
|
||||
[status-im.contexts.chat.messenger.messages.delete-message.events :as delete-message]
|
||||
[status-im.contexts.chat.messenger.messages.list.state :as chat.state]
|
||||
[status-im.feature-flags :as ff]
|
||||
[status-im.navigation.events :as navigation]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.datetime :as datetime]
|
||||
@@ -431,3 +432,20 @@
|
||||
[:chat/inputs current-chat-id :metadata :sending-image]
|
||||
dissoc
|
||||
(:uri original))})))
|
||||
|
||||
(rf/reg-event-fx :chat.ui/show-profile
|
||||
(fn [{:keys [db]} [public-key ens-name]]
|
||||
(let [my-public-key (get-in db [:profile/profile :public-key])]
|
||||
(if (not= my-public-key public-key)
|
||||
{:db (-> db
|
||||
(assoc :contacts/identity public-key)
|
||||
(assoc :contacts/ens-name ens-name))
|
||||
:dispatch [:contacts/build-contact
|
||||
{:pubkey public-key
|
||||
:ens ens-name
|
||||
:success-fn (fn [_]
|
||||
{:dispatch [:open-modal
|
||||
(if (ff/enabled? ::ff/profile.new-contact-ui)
|
||||
:contact-profile
|
||||
:profile)]})}]}
|
||||
{:dispatch [:navigate-to :my-profile]}))))
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[status-im.contexts.onboarding.new-to-status.style :as style]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.i18n :as i18n]
|
||||
[native-module.core :as native-module]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn sign-in-options
|
||||
@@ -95,7 +96,8 @@
|
||||
|
||||
(defn new-to-status
|
||||
[]
|
||||
(let [{:keys [top]} (safe-area/get-insets)]
|
||||
(let [{:keys [top]} (safe-area/get-insets)
|
||||
_ (native-module/request-local-network-access (fn [response] (log/info "request-local-network-access ->" response)))]
|
||||
[rn/view {:style style/content-container}
|
||||
[quo/page-nav
|
||||
{:margin-top top
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
:disabled? (:disabled? @state)
|
||||
:blur? @blur?
|
||||
:type (:type @state)
|
||||
:on-complete (fn []
|
||||
:on-complete (fn [_]
|
||||
(js/setTimeout (fn [] (reset! complete? true))
|
||||
1000)
|
||||
(js/alert "I don't wanna slide anymore"))}]
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
:blur? (:blur? @state)
|
||||
:show-blur-background? true}
|
||||
[quo/address-input
|
||||
(merge @state
|
||||
{:on-scan #(js/alert "Not implemented yet")
|
||||
(merge (dissoc @state :scanned-value)
|
||||
{:on-scan (fn [on-result] (on-result (:scanned-value @state)))
|
||||
:ens-regex constants/regx-ens
|
||||
:on-detect-ens (fn [_]
|
||||
(swap! state assoc :valid-ens-or-address? false)
|
||||
|
||||
@@ -535,7 +535,7 @@
|
||||
[quo/button
|
||||
{:type :outline
|
||||
:container-style {:margin-vertical 8}
|
||||
:on-press #(rf/dispatch [:navigate-to category-name])}
|
||||
:on-press #(rf/dispatch [:open-modal category-name])}
|
||||
(name category-name)]))])))
|
||||
|
||||
(defn- main-screen
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
(ns status-im.contexts.profile.contact.header.style
|
||||
(:require [quo.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(def avatar-wrapper
|
||||
{:margin-top -40
|
||||
:padding-left 20
|
||||
:align-items :flex-start})
|
||||
|
||||
(defn header-container
|
||||
[border-radius theme margin-top]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:border-top-left-radius border-radius
|
||||
:border-top-right-radius border-radius}
|
||||
{:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
|
||||
:padding-horizontal 20
|
||||
:margin-top margin-top}))
|
||||
@@ -0,0 +1,34 @@
|
||||
(ns status-im.contexts.profile.contact.header.view
|
||||
(:require [quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.scalable-avatar.view :as avatar]
|
||||
[status-im.contexts.profile.contact.header.style :as style]
|
||||
[status-im.contexts.profile.utils :as profile.utils]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn view
|
||||
[{:keys [scroll-y]}]
|
||||
(let [{:keys [public-key customization-color
|
||||
emoji-hash bio]
|
||||
:as profile} (rf/sub [:contacts/current-contact])
|
||||
customization-color (or customization-color :blue)
|
||||
full-name (profile.utils/displayed-name profile)
|
||||
profile-picture (profile.utils/photo profile)
|
||||
online? (rf/sub [:visibility-status-updates/online? public-key])
|
||||
theme (quo.theme/use-theme-value)]
|
||||
[rn/view {:style style/header-container}
|
||||
[rn/view {:style style/avatar-wrapper}
|
||||
[avatar/view
|
||||
{:scroll-y scroll-y
|
||||
:full-name full-name
|
||||
:online? online?
|
||||
:profile-picture profile-picture
|
||||
:border-color (colors/theme-colors colors/white colors/neutral-95 theme)
|
||||
:customization-color customization-color}]]
|
||||
[quo/page-top
|
||||
{:title full-name
|
||||
:description :text
|
||||
:description-text bio
|
||||
:emoji-dash emoji-hash}]]))
|
||||
@@ -0,0 +1,24 @@
|
||||
(ns status-im.contexts.profile.contact.view
|
||||
(:require [quo.foundations.colors :as colors]
|
||||
[quo.theme]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.common.not-implemented :as not-implemented]
|
||||
[status-im.common.scroll-page.view :as scroll-page]
|
||||
[status-im.contexts.profile.contact.header.view :as contact-header]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [{:keys [customization-color]} (rf/sub [:contacts/current-contact])
|
||||
scroll-y (reanimated/use-shared-value 0)
|
||||
theme (quo.theme/use-theme-value)]
|
||||
[scroll-page/scroll-page
|
||||
{:navigate-back? true
|
||||
:height 148
|
||||
:on-scroll #(reanimated/set-shared-value scroll-y %)
|
||||
;TODO remove colors/primary-50 when #18733 merged.
|
||||
:cover-color (or customization-color colors/primary-50)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
|
||||
:page-nav-props {:right-side [{:icon-name :i/options
|
||||
:on-press not-implemented/alert}]}}
|
||||
[contact-header/view {:scroll-y scroll-y}]]))
|
||||
@@ -5,7 +5,6 @@
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.confirmation-drawer.view :as confirmation-drawer]
|
||||
[status-im.common.standard-authentication.core :as standard-authentication]
|
||||
[status-im.config :as config]
|
||||
@@ -133,8 +132,8 @@
|
||||
[:profile.login/login-with-biometric-if-available key-uid])
|
||||
(when-not keycard-pairing (set-hide-profiles)))}]))
|
||||
|
||||
(defn- f-profiles-section
|
||||
[{:keys [set-hide-profiles]}]
|
||||
(defn- profiles-section
|
||||
[{:keys [hide-profiles]}]
|
||||
(let [profiles (vals (rf/sub [:profile/profiles-overview]))
|
||||
translate-x (reanimated/use-shared-value @translate-x-atom)]
|
||||
(rn/use-mount (fn []
|
||||
@@ -165,42 +164,38 @@
|
||||
:key-fn :key-uid
|
||||
:content-container-style {:padding-bottom 20}
|
||||
:render-data {:last-index (dec (count profiles))
|
||||
:set-hide-profiles set-hide-profiles}
|
||||
:set-hide-profiles hide-profiles}
|
||||
:render-fn profile-card}]]))
|
||||
|
||||
(defn profiles-section
|
||||
[props]
|
||||
[:f> f-profiles-section props])
|
||||
|
||||
(defn password-input
|
||||
[]
|
||||
(let [password (rf/sub [:profile/login-password])
|
||||
auth-method (rf/sub [:auth-method])]
|
||||
(let [auth-method (rf/sub [:auth-method])
|
||||
on-press-biometrics (when (= auth-method constants/auth-method-biometric)
|
||||
(rn/use-callback
|
||||
(fn []
|
||||
(rf/dispatch [:biometric/authenticate
|
||||
{:on-success #(rf/dispatch
|
||||
[:profile.login/biometric-success])
|
||||
:on-fail #(rf/dispatch
|
||||
[:profile.login/biometric-auth-fail
|
||||
%])}]))))]
|
||||
[standard-authentication/password-input
|
||||
{:shell? true
|
||||
:blur? true
|
||||
:on-press-biometrics (when (= auth-method constants/auth-method-biometric)
|
||||
(fn []
|
||||
(rf/dispatch [:biometric/authenticate
|
||||
{:on-success #(rf/dispatch
|
||||
[:profile.login/biometric-success])
|
||||
:on-fail #(rf/dispatch
|
||||
[:profile.login/biometric-auth-fail
|
||||
%])}])))
|
||||
:default-password password}]))
|
||||
:on-press-biometrics on-press-biometrics}]))
|
||||
|
||||
(defn login-section
|
||||
[{:keys [set-show-profiles]}]
|
||||
(let [processing (rf/sub [:profile/login-processing])
|
||||
{:keys [key-uid name customization-color]} (rf/sub [:profile/login-profile])
|
||||
sign-in-enabled? (rf/sub [:sign-in-enabled?])
|
||||
profile-picture (rf/sub [:profile/login-profiles-picture key-uid])
|
||||
login-multiaccount #(rf/dispatch [:profile.login/login])]
|
||||
[{:keys [show-profiles]}]
|
||||
(let [processing (rf/sub [:profile/login-processing])
|
||||
{:keys [key-uid name
|
||||
customization-color]} (rf/sub [:profile/login-profile])
|
||||
sign-in-enabled? (rf/sub [:sign-in-enabled?])
|
||||
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))}
|
||||
[rn/view
|
||||
{:style style/multi-profile-button-container}
|
||||
[rn/view {:style style/multi-profile-button-container}
|
||||
(when config/quo-preview-enabled?
|
||||
[quo/button
|
||||
{:size 32
|
||||
@@ -217,7 +212,7 @@
|
||||
:type :grey
|
||||
:background :blur
|
||||
:icon-only? true
|
||||
:on-press set-show-profiles
|
||||
:on-press show-profiles
|
||||
:disabled? processing
|
||||
:accessibility-label :show-profiles}
|
||||
:i/multi-profile]]
|
||||
@@ -241,16 +236,13 @@
|
||||
:container-style {:margin-bottom (+ (safe-area/get-bottom) 12)}}
|
||||
(i18n/label :t/log-in)]]))
|
||||
|
||||
;; we had to register it here, because of hotreload, overwise on hotreload it will be reseted
|
||||
(defonce show-profiles? (reagent/atom false))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [set-show-profiles #(reset! show-profiles? true)
|
||||
set-hide-profiles #(reset! show-profiles? false)]
|
||||
(fn []
|
||||
[:<>
|
||||
[background/view true]
|
||||
(if @show-profiles?
|
||||
[profiles-section {:set-hide-profiles set-hide-profiles}]
|
||||
[login-section {:set-show-profiles set-show-profiles}])])))
|
||||
(let [[show-profiles? set-show-profiles] (rn/use-state false)
|
||||
show-profiles (rn/use-callback #(set-show-profiles true))
|
||||
hide-profiles (rn/use-callback #(set-show-profiles false))]
|
||||
[:<>
|
||||
[background/view true]
|
||||
(if show-profiles?
|
||||
[profiles-section {:hide-profiles hide-profiles}]
|
||||
[login-section {:show-profiles show-profiles}])]))
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
(ns status-im.contexts.profile.settings.header.view
|
||||
(:require [clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[quo.theme :as quo.theme]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.contexts.profile.settings.header.avatar :as header.avatar]
|
||||
[status-im.common.scalable-avatar.view :as avatar]
|
||||
[status-im.contexts.profile.settings.header.header-shape :as header.shape]
|
||||
[status-im.contexts.profile.settings.header.style :as style]
|
||||
[status-im.contexts.profile.settings.header.utils :as header.utils]
|
||||
@@ -13,28 +14,31 @@
|
||||
|
||||
(defn- f-view
|
||||
[{:keys [theme scroll-y]}]
|
||||
(let [{:keys [public-key emoji-hash] :as profile} (rf/sub [:profile/profile-with-image])
|
||||
online? (rf/sub [:visibility-status-updates/online?
|
||||
public-key])
|
||||
status (rf/sub
|
||||
[:visibility-status-updates/visibility-status-update
|
||||
public-key])
|
||||
customization-color (rf/sub [:profile/customization-color])
|
||||
bio (:bio profile)
|
||||
full-name (profile.utils/displayed-name profile)
|
||||
profile-picture (profile.utils/photo profile)
|
||||
emoji-string (string/join emoji-hash)
|
||||
{:keys [status-title status-icon]} (header.utils/visibility-status-type-data status)]
|
||||
(let [{:keys [public-key emoji-hash bio] :as profile} (rf/sub [:profile/profile-with-image])
|
||||
online? (rf/sub [:visibility-status-updates/online?
|
||||
public-key])
|
||||
status (rf/sub
|
||||
[:visibility-status-updates/visibility-status-update
|
||||
public-key])
|
||||
customization-color (rf/sub [:profile/customization-color])
|
||||
full-name (profile.utils/displayed-name profile)
|
||||
profile-picture (profile.utils/photo profile)
|
||||
emoji-string (string/join emoji-hash)
|
||||
{:keys [status-title status-icon]} (header.utils/visibility-status-type-data status)
|
||||
border-theme (quo.theme/get-theme)]
|
||||
[:<>
|
||||
[header.shape/view
|
||||
{:scroll-y scroll-y
|
||||
:customization-color customization-color
|
||||
:theme theme}]
|
||||
[rn/view {:style style/avatar-row-wrapper}
|
||||
[header.avatar/view
|
||||
[avatar/view
|
||||
{:scroll-y scroll-y
|
||||
:display-name full-name
|
||||
:online? online?
|
||||
:border-color (colors/theme-colors colors/border-avatar-light
|
||||
colors/neutral-80-opa-80
|
||||
border-theme)
|
||||
:customization-color customization-color
|
||||
:profile-picture profile-picture}]
|
||||
[rn/view {:style {:margin-bottom 4}}
|
||||
|
||||
@@ -21,10 +21,14 @@
|
||||
(h/fire-event :change-text
|
||||
(h/get-by-label-text :add-address-to-watch)
|
||||
"0x12E838Ae1f769147b12956485dc56e57138f3AC8")
|
||||
(h/is-truthy (h/get-by-translation-text :t/address-already-in-use)))
|
||||
(-> (h/wait-for #(h/get-by-translation-text :t/address-already-in-use))
|
||||
(.then (fn []
|
||||
(h/is-truthy (h/get-by-translation-text :t/address-already-in-use))))))
|
||||
|
||||
(h/test "validation messages show for invalid address"
|
||||
(h/render [add-address-to-watch/view])
|
||||
(h/is-falsy (h/query-by-label-text :error-message))
|
||||
(h/fire-event :change-text (h/get-by-label-text :add-address-to-watch) "0x12E838Ae1f769147b")
|
||||
(h/is-truthy (h/get-by-translation-text :t/invalid-address))))
|
||||
(-> (h/wait-for #(h/get-by-translation-text :t/invalid-address))
|
||||
(.then (fn []
|
||||
(h/is-truthy (h/get-by-translation-text :t/invalid-address)))))))
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
(react-native-shake/add-shake-listener #(re-frame/dispatch [:shake-event]))
|
||||
(universal-links/initialize)
|
||||
(interceptors/register-global-interceptors)
|
||||
; (native-module/request-local-network-access (fn [response] (log/info "request-local-network-access ->" response)))
|
||||
|
||||
;; Shell
|
||||
(async-storage/get-item :selected-stack-id #(shell.utils/change-selected-stack-id % nil nil))
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
{::wallet.edit-default-keypair (enabled-in-env? :FLAG_EDIT_DEFAULT_KEYPAIR_ENABLED)
|
||||
::wallet.bridge-token (enabled-in-env? :FLAG_BRIDGE_TOKEN_ENABLED)
|
||||
::wallet.remove-account (enabled-in-env? :FLAG_REMOVE_ACCOUNT_ENABLED)
|
||||
::wallet.network-filter (enabled-in-env? :FLAG_NETWORK_FILTER_ENABLED)}))
|
||||
::wallet.network-filter (enabled-in-env? :FLAG_NETWORK_FILTER_ENABLED)
|
||||
::profile.new-contact-ui (enabled-in-env? :FLAG_NEW_CONTACT_UI_ENABLED)}))
|
||||
|
||||
(defn feature-flags [] @feature-flags-config)
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
(ns status-im.navigation.effects
|
||||
(:require
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.navigation :as navigation]
|
||||
[status-im.navigation.options :as options]
|
||||
[status-im.navigation.roots :as roots]
|
||||
@@ -8,10 +10,20 @@
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- set-status-bar-color
|
||||
[theme]
|
||||
(rn/set-status-bar-style
|
||||
(if (= theme :dark)
|
||||
"light-content"
|
||||
"dark-content")
|
||||
true))
|
||||
|
||||
(rf/reg-fx :set-view-id-fx
|
||||
(fn [view-id]
|
||||
(rf/dispatch [:screens/on-will-focus view-id])
|
||||
(when-let [{:keys [on-focus]} (get views/screens view-id)]
|
||||
(when-let [{:keys [on-focus options]} (get views/screens view-id)]
|
||||
(set-status-bar-color (or (:theme options)
|
||||
(quo.theme/get-theme)))
|
||||
(when on-focus
|
||||
(rf/dispatch on-focus)))))
|
||||
|
||||
@@ -117,11 +129,12 @@
|
||||
|
||||
(defn open-modal
|
||||
[component]
|
||||
(let [{:keys [options]} (get views/screens component)
|
||||
sheet? (:sheet? options)]
|
||||
(let [{:keys [options name]} (get views/screens component)
|
||||
sheet? (:sheet? options)]
|
||||
(if @state/dissmissing
|
||||
(reset! state/dissmissing component)
|
||||
(do
|
||||
(set-view-id name) ; TODO https://github.com/status-im/status-mobile/issues/18811
|
||||
(reset! state/curr-modal true)
|
||||
(swap! state/modals conj component)
|
||||
(navigation/show-modal
|
||||
|
||||
@@ -137,9 +137,6 @@
|
||||
:decelerate
|
||||
:factor 1.5}}]}}})
|
||||
|
||||
(def camera-screen
|
||||
{:navigationBar {:backgroundColor colors/black}})
|
||||
|
||||
(defn merge-top-bar
|
||||
[root-options options]
|
||||
(let [options (:topBar options)]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns status-im.navigation.screens
|
||||
(:require
|
||||
[legacy.status-im.ui.screens.screens :as old-screens]
|
||||
[quo.foundations.colors :as colors]
|
||||
[status-im.common.emoji-picker.view :as emoji-picker]
|
||||
[status-im.common.lightbox.view :as lightbox]
|
||||
[status-im.config :as config]
|
||||
@@ -36,6 +37,7 @@
|
||||
[status-im.contexts.preview.quo.component-preview.view :as component-preview]
|
||||
[status-im.contexts.preview.quo.main :as quo.preview]
|
||||
[status-im.contexts.preview.status-im.main :as status-im-preview]
|
||||
[status-im.contexts.profile.contact.view :as contact-profile]
|
||||
[status-im.contexts.profile.edit.accent-colour.view :as edit-accent-colour]
|
||||
[status-im.contexts.profile.edit.bio.view :as edit-bio]
|
||||
[status-im.contexts.profile.edit.name.view :as edit-name]
|
||||
@@ -141,7 +143,8 @@
|
||||
:component photo-selector/photo-selector}
|
||||
|
||||
{:name :camera-screen
|
||||
:options options/camera-screen
|
||||
:options {:navigationBar {:backgroundColor colors/black}
|
||||
:theme :dark}
|
||||
:component camera-screen/camera-screen}
|
||||
|
||||
{:name :new-contact
|
||||
@@ -202,6 +205,10 @@
|
||||
:options options/transparent-modal-screen-options
|
||||
:component edit-bio/view}
|
||||
|
||||
{:name :contact-profile
|
||||
:options {:modalPresentationStyle :overCurrentContext}
|
||||
:component contact-profile/view}
|
||||
|
||||
{:name :new-to-status
|
||||
:options {:theme :dark
|
||||
:layout options/onboarding-transparent-layout
|
||||
@@ -428,6 +435,13 @@
|
||||
:options options/transparent-modal-screen-options
|
||||
:component settings-password/view}]
|
||||
|
||||
[{:name :shell
|
||||
:options {:theme :dark}}
|
||||
{:name :communities-stack}
|
||||
{:name :chats-stack}
|
||||
{:name :wallet-stack}
|
||||
{:name :browser-stack}]
|
||||
|
||||
(when js/goog.DEBUG
|
||||
[{:name :dev-component-preview
|
||||
:options {:sheet? true}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
[status-im.setup.hot-reload :as reloader]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def functional-compiler (reagent/create-compiler {:function-components true}))
|
||||
|
||||
(defn get-screens
|
||||
[]
|
||||
(reduce
|
||||
@@ -72,7 +74,8 @@
|
||||
(when js/goog.DEBUG
|
||||
[:<>
|
||||
[reloader/reload-view]
|
||||
[schema.view/view]])]))))
|
||||
[schema.view/view]])]))
|
||||
functional-compiler))
|
||||
|
||||
(def bottom-sheet
|
||||
(reagent/reactify-component
|
||||
@@ -90,7 +93,8 @@
|
||||
:keyboard-vertical-offset (- (max 20 (:bottom insets)))}
|
||||
(when sheet
|
||||
[bottom-sheet/view {:insets insets :hide? hide?}
|
||||
sheet])]]))))
|
||||
sheet])]]))
|
||||
functional-compiler))
|
||||
|
||||
(def toasts (reagent/reactify-component toasts/toasts))
|
||||
|
||||
@@ -103,7 +107,8 @@
|
||||
[inactive]
|
||||
[popover/popover]
|
||||
(when js/goog.DEBUG
|
||||
[reloader/reload-view])])))
|
||||
[reloader/reload-view])])
|
||||
functional-compiler))
|
||||
|
||||
(def visibility-status-popover-comp
|
||||
(reagent/reactify-component
|
||||
@@ -113,7 +118,8 @@
|
||||
[inactive]
|
||||
[visibility-status-views/visibility-status-popover]
|
||||
(when js/goog.DEBUG
|
||||
[reloader/reload-view])])))
|
||||
[reloader/reload-view])])
|
||||
functional-compiler))
|
||||
|
||||
(def sheet-comp-old
|
||||
(reagent/reactify-component
|
||||
@@ -121,7 +127,8 @@
|
||||
^{:key (str "sheet-old" @reloader/cnt)}
|
||||
[:<>
|
||||
[inactive]
|
||||
[bottom-sheets-old/bottom-sheet]])))
|
||||
[bottom-sheets-old/bottom-sheet]])
|
||||
functional-compiler))
|
||||
|
||||
(def signing-comp
|
||||
(reagent/reactify-component
|
||||
@@ -131,4 +138,5 @@
|
||||
[inactive]
|
||||
[signing/signing]
|
||||
(when js/goog.DEBUG
|
||||
[reloader/reload-view])])))
|
||||
[reloader/reload-view])])
|
||||
functional-compiler))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns test-helpers.component-tests-preload
|
||||
{:dev/always true}
|
||||
(:require
|
||||
[reagent.core :as reagent]
|
||||
;; NOTE: Do NOT sort i18n-resources because it MUST be loaded first.
|
||||
[status-im.setup.i18n-resources :as i18n-resources]
|
||||
#_{:clj-kondo/ignore [:unsorted-required-namespaces]}
|
||||
@@ -15,6 +16,7 @@
|
||||
should never be directly required. However, it will be loaded automatically
|
||||
before any component test runs."
|
||||
[]
|
||||
(reagent/set-default-compiler! (reagent/create-compiler {:function-components true}))
|
||||
(interceptors/register-global-interceptors)
|
||||
(i18n/set-language "en")
|
||||
(i18n-resources/load-language "en"))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.175.4",
|
||||
"commit-sha1": "5304406079a5e3e1cfea285fef892ac56f2c3aad",
|
||||
"src-sha256": "08xprnmpw0nz2zjm1wp8ypjhdyfvgmh7pqrlk69prgr23xa31ady"
|
||||
"version": "v0.176.2",
|
||||
"commit-sha1": "00f50a4112ef80cc29737f8edf34cdb1cf181709",
|
||||
"src-sha256": "12z9qf0hhb2pc7ask6f86pah0wjp3kyxyv32rpfnkjl42maw0bw6"
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ class TestActivityCenterContactRequestMultipleDevicePR(MultipleSharedDeviceTestC
|
||||
self.errors.append("Push notification with text was received for new message in activity centre")
|
||||
self.device_1.click_system_back_button()
|
||||
self.device_1.driver.activate_app(app_package)
|
||||
self.device_1.wait_for_application_to_be_running(app_package)
|
||||
|
||||
self.device_1.just_fyi('Device1 verifies pending contact request')
|
||||
self.home_1.contacts_tab.click()
|
||||
|
||||
@@ -271,8 +271,6 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
|
||||
self.home_1.jump_to_messages_home()
|
||||
self.home_1.profile_button.click()
|
||||
self.profile_1.edit_profile_picture(image_index=2)
|
||||
self.profile_1.navigate_back_to_home_view()
|
||||
self.profile_1.chats_tab.click()
|
||||
|
||||
self.chat_2.just_fyi("Send messages with non-latin symbols")
|
||||
self.home_1.jump_to_card_by_text(self.username_2)
|
||||
|
||||
@@ -6,6 +6,7 @@ import time
|
||||
from datetime import datetime
|
||||
|
||||
from appium.webdriver import WebElement
|
||||
from appium.webdriver.applicationstate import ApplicationState
|
||||
from appium.webdriver.common.touch_action import TouchAction
|
||||
from selenium.common.exceptions import NoSuchElementException, TimeoutException
|
||||
from selenium.webdriver.support import expected_conditions
|
||||
@@ -658,13 +659,24 @@ class BaseView(object):
|
||||
self.click_on_floating_jump_to()
|
||||
self.element_by_text(text).click()
|
||||
|
||||
def wait_for_application_to_be_running(self, app_package: str, wait_time: int = 3):
|
||||
for _ in range(wait_time):
|
||||
if self.driver.query_app_state(app_package) == ApplicationState.RUNNING_IN_FOREGROUND:
|
||||
return
|
||||
time.sleep(1)
|
||||
raise TimeoutException(msg="Status app is not running in foreground after %s sec" % wait_time)
|
||||
|
||||
def wait_for_application_to_not_run(self, app_package: str, wait_time: int = 3):
|
||||
for _ in range(wait_time):
|
||||
if self.driver.query_app_state(app_package) == ApplicationState.NOT_RUNNING:
|
||||
return
|
||||
time.sleep(1)
|
||||
raise TimeoutException(msg="Status app is not terminated after %s sec" % wait_time)
|
||||
|
||||
def reopen_app(self, password=common_password, sign_in=True):
|
||||
app_package = self.driver.current_package
|
||||
self.driver.terminate_app(app_package)
|
||||
for _ in range(3):
|
||||
if self.driver.query_app_state(app_package) == 1:
|
||||
break
|
||||
time.sleep(1)
|
||||
self.wait_for_application_to_not_run(app_package=app_package)
|
||||
self.driver.activate_app(app_package)
|
||||
if sign_in:
|
||||
sign_in_view = self.get_sign_in_view()
|
||||
|
||||
@@ -199,9 +199,9 @@ class ProfileView(BaseView):
|
||||
self.online_indicator = Button(self.driver, accessibility_id="online-profile-photo-dot")
|
||||
self.edit_picture_button = Button(self.driver, accessibility_id="edit-profile-photo-button")
|
||||
self.confirm_edit_button = Button(self.driver, accessibility_id="done-button")
|
||||
self.select_from_gallery_button = Button(self.driver, translation_id="profile-pic-pick")
|
||||
self.select_from_gallery_button_old = Button(self.driver, translation_id="profile-pic-pick")
|
||||
self.capture_button = Button(self.driver, translation_id="image-source-make-photo")
|
||||
self.take_photo_button = Button(self.driver, accessibility_id="take-photo")
|
||||
self.take_photo_button_old = Button(self.driver, accessibility_id="take-photo")
|
||||
self.crop_photo_button = Button(self.driver, accessibility_id="Crop")
|
||||
self.decline_photo_crop = Button(self.driver, accessibility_id="Navigate up")
|
||||
self.shutter_button = Button(self.driver, accessibility_id="Shutter")
|
||||
@@ -354,6 +354,12 @@ class ProfileView(BaseView):
|
||||
self.confirm_logout_button = Button(self.driver, translation_id="logout", uppercase=True)
|
||||
|
||||
# New profile
|
||||
self.edit_profile_button = Button(self.driver, accessibility_id="icon, Edit Profile, label-component, icon")
|
||||
self.change_profile_photo_button = Button(
|
||||
self.driver,
|
||||
xpath="//*[@content-desc='user-avatar']/following-sibling::android.view.ViewGroup[@content-desc='icon']")
|
||||
self.take_photo_button = Button(self.driver, accessibility_id="take-photo-button")
|
||||
self.select_from_gallery_button = Button(self.driver, accessibility_id="select-from-gallery-button")
|
||||
self.profile_password_button = Button(self.driver, accessibility_id="icon, Password, label-component, icon")
|
||||
self.profile_legacy_button = Button(self.driver,
|
||||
accessibility_id="icon, Legacy settings, label-component, icon")
|
||||
@@ -438,10 +444,8 @@ class ProfileView(BaseView):
|
||||
self.driver.info("## Setting custom profile image", device=False)
|
||||
if not AbstractTestCase().environment == 'sauce':
|
||||
raise NotImplementedError('Test case is implemented to run on SauceLabs only')
|
||||
## pointing to legacy profile until new feature is implemented
|
||||
self.logout_button.scroll_to_element()
|
||||
self.profile_legacy_button.click()
|
||||
self.profile_picture.click()
|
||||
self.edit_profile_button.click()
|
||||
self.change_profile_photo_button.click()
|
||||
if update_by == "Gallery":
|
||||
self.select_from_gallery_button.click()
|
||||
self.select_photo_from_gallery_by_index(image_index)
|
||||
@@ -454,6 +458,7 @@ class ProfileView(BaseView):
|
||||
self.accept_photo_button.click()
|
||||
self.crop_photo_button.click()
|
||||
self.driver.info("## Custom profile image has been set", device=False)
|
||||
self.click_system_back_button()
|
||||
|
||||
def take_photo(self):
|
||||
self.take_photo_button.click()
|
||||
|
||||
Reference in New Issue
Block a user