Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d80021b6a3 | ||
|
|
8f3fbc22d4 | ||
|
|
9ddea4805a | ||
|
|
4d029f06e8 | ||
|
|
3bad3324ae | ||
|
|
bfb074eca3 | ||
|
|
b87b3083a3 | ||
|
|
3e8647d626 | ||
|
|
9e5c7cdea5 | ||
|
|
fa21c22fab | ||
|
|
7c54537cb3 | ||
|
|
14653f387b | ||
|
|
40bb07a99b | ||
|
|
7f00f96b0c | ||
|
|
3ca120ca94 | ||
|
|
46351543f1 | ||
|
|
05b8dd563e | ||
|
|
5d0b739cc0 | ||
|
|
8bf5abdf3d | ||
|
|
a0f2481406 | ||
|
|
4fefa629fc | ||
|
|
41e2fffb87 | ||
|
|
7c101716d7 | ||
|
|
53cea00c67 | ||
|
|
512af400d7 | ||
|
|
45b501816a | ||
|
|
9b756544fd | ||
|
|
8af7011eb4 | ||
|
|
f6d74df80b | ||
|
|
6c1929096c | ||
|
|
d902fb10d1 | ||
|
|
270f5ab258 | ||
|
|
c3e52dbf47 |
@@ -282,6 +282,17 @@ else
|
||||
npx react-native run-ios
|
||||
endif
|
||||
|
||||
show-ios-devices: export TARGET := ios
|
||||
show-ios-devices: ##@other shows connected ios device and its name
|
||||
xcrun xctrace list devices
|
||||
|
||||
run-ios-device: export TARGET := ios
|
||||
run-ios-device: ##@run iOS app and start it on a connected device by its name
|
||||
ifndef DEVICE_NAME
|
||||
$(error Usage: make run-ios-device DEVICE_NAME=your-device-name)
|
||||
endif
|
||||
react-native run-ios --device "$(DEVICE_NAME)"
|
||||
|
||||
#--------------
|
||||
# Tests
|
||||
#--------------
|
||||
|
||||
@@ -32,7 +32,10 @@
|
||||
android:theme="@style/Theme.AppSplash"
|
||||
android:name=".MainApplication"
|
||||
android:largeHeap="true"
|
||||
android:usesCleartextTraffic="true">
|
||||
android:usesCleartextTraffic="true"
|
||||
android:extractNativeLibs="true">
|
||||
<!-- After upgrading Android Gradle Plugin to 4.2.0 and above we must get rid of `extractNativeLibs="true"`
|
||||
and use`useLegacyPackaging` flag in our app's `build.gradle`-->
|
||||
<meta-data android:name="commitHash" android:value="${commitHash}"/>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
@@ -4,7 +4,7 @@ library 'status-jenkins-lib@v1.6.9'
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
pipeline {
|
||||
agent { label 'macos && arm64 && nix-2.11 && xcode-14.2' }
|
||||
agent { label 'macos && arm64 && nix-2.11 && xcode-14.3' }
|
||||
|
||||
parameters {
|
||||
string(
|
||||
|
||||
@@ -23,13 +23,13 @@ setups and runs the test suite once.
|
||||
setups and runs the test suite and watches for code changes will then retrigger the test suite.
|
||||
|
||||
## Writing Tests
|
||||
New test files will need their namespace added to either the file "src/quo2/core_spec.cljs" or "src/status_im2/core_spec.cljs. These locations may update overtime but it is dependent on the entrypoint in shadowcljs config discussed below.
|
||||
New test files will need their namespace added to either the file "src/quo2/core_spec.cljs" or "src/status_im2/core_spec.cljs. These locations may update overtime but it is dependent on the entrypoint in shadow-cljs config discussed below.
|
||||
|
||||
|
||||
### Best practices
|
||||
For the moment we will keep best practices for tests in our other guidelines document:
|
||||
|
||||
To that point these guidelines will follow the conventions of Jest and React Native Testing Library recomendations and Status mobile will just stack their preferences on top.
|
||||
To that point these guidelines will follow the conventions of Jest and React Native Testing Library recommendations and Status mobile will just stack their preferences on top.
|
||||
|
||||
### Utilities
|
||||
There is a file of utility functions defined in "src/test_helpers/component.cljs" and "src/test_helpers/component.clj". It will be great to use these utilities and to add any common testing tools to these files as it should make writing tests easier and faster.
|
||||
@@ -52,4 +52,4 @@ It's worth knowing that our tests are compiled to JS and then run in the tempora
|
||||
### Jest
|
||||
There is also further configuration for Jest in "test/jest". There is a jest config file which has some mostly standard configuration pieces, where the tests live, what environment variables are set etc. This is documented by Jest here: https://jestjs.io/docs/configuration
|
||||
|
||||
There is also a setup file which is used to set some global and default values. Additionally this file is used to mock some of the react native (among other) dependencies
|
||||
There is also a setup file which is used to set some global and default values. Additionally this file is used to mock some of the react native (among other) dependencies
|
||||
|
||||
@@ -417,9 +417,7 @@
|
||||
TestTargetID = 13B07F861A680F5B00A75B9A;
|
||||
};
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
DevelopmentTeam = 8B5X2M6H2Y;
|
||||
LastSwiftMigration = 1140;
|
||||
ProvisioningStyle = Manual;
|
||||
SystemCapabilities = {
|
||||
com.apple.BackgroundModes = {
|
||||
enabled = 1;
|
||||
@@ -544,7 +542,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = "/usr/bin/env sh";
|
||||
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\" --openssl-legacy-provider --max-old-space-size=16384 \"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ./logs/react-native-xcode.log 2>&1";
|
||||
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\"${NODE_ARGS:- --max-old-space-size=16384 }\"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ./logs/react-native-xcode.log 2>&1";
|
||||
};
|
||||
3AAD2AD524A3A60E0075D594 /* Bundle React Native code and images */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -558,7 +556,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = "/usr/bin/env sh";
|
||||
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\" --openssl-legacy-provider --max-old-space-size=16384 \"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ./logs/react-native-xcode.log 2>&1";
|
||||
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\"${NODE_ARGS:- --max-old-space-size=16384 }\"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ./logs/react-native-xcode.log 2>&1";
|
||||
};
|
||||
3AAD2AD724A3A60E0075D594 /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -843,9 +841,8 @@
|
||||
BUNDLE_ID_SUFFIX = .debug;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = StatusIm/StatusIm.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CUSTOM_PRODUCT_NAME = "Status Debug";
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8B5X2M6H2Y;
|
||||
@@ -899,8 +896,7 @@
|
||||
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = im.status.ethereum;
|
||||
PRODUCT_NAME = StatusIm;
|
||||
PROVISIONING_PROFILE = "9da75626-9594-43d9-a827-0f6d43c28f54";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match Development im.status.ethereum";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "StatusIm-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
||||
@@ -15,6 +15,7 @@ stdenv.mkDerivation {
|
||||
"patchYogaNodePackagePhase"
|
||||
"patchReactNativePhase"
|
||||
"patchPodPhase"
|
||||
"patchReactNativeXcodeScriptPhase"
|
||||
"installPhase"
|
||||
];
|
||||
|
||||
@@ -106,6 +107,18 @@ stdenv.mkDerivation {
|
||||
'[RCTConvert UIColor:options.tintColor() ? @(*options.tintColor()) : nil];'
|
||||
'';
|
||||
|
||||
# Patch React Native Xcode Script that searches for nvm
|
||||
# FIXME: Remove this once we upgrade react-native to 0.69.x
|
||||
patchReactNativeXcodeScriptPhase = ''
|
||||
substituteInPlace ./node_modules/react-native/scripts/find-node.sh --replace \
|
||||
'# Define NVM_DIR and source the nvm.sh setup script' \
|
||||
'<<MULTI_LINE_COMMENT${"\n"}# Define NVM_DIR and source the nvm.sh setup script'
|
||||
|
||||
substituteInPlace ./node_modules/react-native/scripts/find-node.sh --replace \
|
||||
'# Set up the nodenv node version manager if present' \
|
||||
'MULTI_LINE_COMMENT${"\n"}# Set up the nodenv node version manager if present'
|
||||
'';
|
||||
|
||||
|
||||
# The ELF types are incompatible with the host platform, so let's not even try
|
||||
# TODO: Use Android NDK to strip binaries manually
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
{ callPackage, lib, mkShell, pkgs
|
||||
{ callPackage, lib, mkShell, pkgs, stdenv
|
||||
, status-go, fastlane }:
|
||||
|
||||
assert lib.assertMsg stdenv.isDarwin
|
||||
"iOS development shell supported only on OSX.";
|
||||
|
||||
let
|
||||
inherit (lib) catAttrs unique;
|
||||
|
||||
@@ -17,6 +20,7 @@ in {
|
||||
buildInputs = with pkgs; [
|
||||
xcodeWrapper watchman procps
|
||||
flock # used in nix/scripts/node_modules.sh
|
||||
ios-deploy # used in 'make run-ios-device'
|
||||
];
|
||||
|
||||
# WARNING: Executes shellHook in reverse order.
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{ mkShell, nodejs, deps }:
|
||||
|
||||
mkShell {
|
||||
# Check if node modules changed and if so install them.
|
||||
shellHook = ''
|
||||
# Fix for ERR_OSSL_EVP_UNSUPPORTED error.
|
||||
export NODE_OPTIONS="--openssl-legacy-provider";
|
||||
|
||||
# Same fix but for Xcode React Native script.
|
||||
export NODE_ARGS="--openssl-legacy-provider --max-old-space-size=16384";
|
||||
# Fix Xcode using system Node.js version.
|
||||
export NODE_BINARY="${nodejs}/bin/node";
|
||||
|
||||
export STATUS_MOBILE_HOME=$(git rev-parse --show-toplevel)
|
||||
# Check if node modules changed and if so install them.
|
||||
"$STATUS_MOBILE_HOME/nix/scripts/node_modules.sh" ${deps.nodejs-patched}
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -44,13 +44,25 @@ in {
|
||||
});
|
||||
};
|
||||
|
||||
# Fix for missing libarclite_macosx.a in Xcode 14.3.
|
||||
# https://github.com/ios-control/ios-deploy/issues/580
|
||||
ios-deploy = super.darwin.ios-deploy.overrideAttrs (old: rec {
|
||||
version = "1.12.2";
|
||||
src = super.fetchFromGitHub {
|
||||
owner = "ios-control";
|
||||
repo = "ios-deploy";
|
||||
rev = version;
|
||||
sha256 = "sha256-TVGC+f+1ow3b93CK3PhIL70le5SZxxb2ug5OkIg8XCA";
|
||||
};
|
||||
});
|
||||
|
||||
# Package version adjustments
|
||||
gradle = super.gradle_6;
|
||||
nodejs = super.nodejs-18_x;
|
||||
yarn = super.yarn.override { nodejs = super.nodejs-18_x; };
|
||||
openjdk = super.openjdk8_headless;
|
||||
xcodeWrapper = callPackage ./pkgs/xcodeenv/compose-xcodewrapper.nix { } {
|
||||
version = "13.3";
|
||||
version = "14.0";
|
||||
allowHigher = true;
|
||||
};
|
||||
go = super.go_1_19;
|
||||
|
||||
@@ -12,22 +12,19 @@ let
|
||||
# the default shell with most commonly used tools
|
||||
default = callPackage ./shell.nix { };
|
||||
|
||||
# Combines with many other shells
|
||||
nodejs-sh = targets.mobile.ios.nodejs-sh;
|
||||
|
||||
# An attrset for easier merging with default shell
|
||||
shells = {
|
||||
inherit default;
|
||||
|
||||
nodejs = nodejs-sh;
|
||||
|
||||
# for calling clojure targets in CI or Makefile
|
||||
clojure = mkShell {
|
||||
buildInputs = with pkgs; [ clojure flock maven openjdk ];
|
||||
inputsFrom = [ nodejs-sh ];
|
||||
# CLASSPATH from clojure deps with 'src' appended to find local sources.
|
||||
shellHook = with pkgs; ''
|
||||
export CLASS_PATH="$(find ${deps.clojure} -iname '*.jar' | tr '\n' ':')src"
|
||||
|
||||
# Check if node modules changed and if so install them.
|
||||
"$STATUS_MOBILE_HOME/nix/scripts/node_modules.sh" ${pkgs.deps.nodejs-patched}
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 539 KiB After Width: | Height: | Size: 539 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 377 KiB |
|
Before Width: | Height: | Size: 738 KiB |
@@ -71,7 +71,7 @@
|
||||
{:background-color (:ui-background @colors/theme)
|
||||
:flex 1}
|
||||
[rn/flat-list
|
||||
{:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]]))
|
||||
{:flex 1
|
||||
:keyboard-should-persist-taps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]]))
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
{:background-color (:ui-background @colors/theme)
|
||||
:flex 1}
|
||||
[rn/flat-list
|
||||
{:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]])
|
||||
{:flex 1
|
||||
:keyboard-should-persist-taps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]])
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
{:background-color (:ui-background @colors/theme)
|
||||
:flex 1}
|
||||
[rn/flat-list
|
||||
{:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
||||
{:flex 1
|
||||
:keyboard-should-persist-taps :always
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
||||
|
||||
@@ -113,9 +113,9 @@
|
||||
{:background-color (:ui-background @colors/theme)
|
||||
:flex 1}
|
||||
[rn/flat-list
|
||||
{:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:header [cool-preview]
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
||||
{:flex 1
|
||||
:keyboard-should-persist-taps :always
|
||||
:header [cool-preview]
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
||||
|
||||
@@ -91,9 +91,9 @@
|
||||
{:background-color (:ui-background @colors/theme)
|
||||
:flex 1}
|
||||
[rn/flat-list
|
||||
{:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:header [cool-preview]
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
||||
{:flex 1
|
||||
:keyboard-should-persist-taps :always
|
||||
:header [cool-preview]
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
||||
|
||||
@@ -83,9 +83,9 @@
|
||||
{:background-color (:ui-background @colors/theme)
|
||||
:flex 1}
|
||||
[rn/flat-list
|
||||
{:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:header [cool-preview]
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
||||
{:flex 1
|
||||
:keyboard-should-persist-taps :always
|
||||
:header [cool-preview]
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
{:background-color (:ui-background @colors/theme)
|
||||
:flex 1}
|
||||
[rn/flat-list
|
||||
{:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
||||
{:flex 1
|
||||
:keyboard-should-persist-taps :always
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[utils.number :as utils-number]))
|
||||
[utils.number]))
|
||||
|
||||
(def themes
|
||||
{:light {:default colors/primary-50
|
||||
@@ -31,7 +31,7 @@
|
||||
(= type :default))
|
||||
colors/white
|
||||
colors/neutral-100))
|
||||
value (utils-number/parse-int value)
|
||||
value (utils.number/parse-int value)
|
||||
label (if (> value max-value)
|
||||
(str max-value "+")
|
||||
(str value))
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
(ns quo2.components.counter.step.component-spec
|
||||
(:require [quo2.components.counter.step.view :as step]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "step component"
|
||||
(h/test "default render of step component"
|
||||
(h/render [step/step {} nil])
|
||||
(-> (h/expect (h/query-by-label-text :step-counter))
|
||||
(h/is-truthy)))
|
||||
|
||||
(h/test "renders step with a string value"
|
||||
(h/render [step/step {} "1"])
|
||||
(-> (h/expect (h/get-by-text "1"))
|
||||
(h/is-truthy)))
|
||||
|
||||
(h/test "renders step with an integer value"
|
||||
(h/render [step/step {} 1])
|
||||
(-> (h/expect (h/get-by-text "1"))
|
||||
(h/is-truthy))))
|
||||
@@ -0,0 +1,41 @@
|
||||
(ns quo2.components.counter.step.style
|
||||
(:require
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(def container-base
|
||||
{:align-items :center
|
||||
:justify-content :center
|
||||
:border-radius 6
|
||||
:height 20})
|
||||
|
||||
(defn neutral-border-color
|
||||
[in-blur-view? override-theme]
|
||||
(if in-blur-view?
|
||||
(colors/theme-colors colors/white-opa-10 colors/neutral-80-opa-5 override-theme)
|
||||
(colors/theme-colors colors/neutral-20 colors/neutral-80 override-theme)))
|
||||
|
||||
(def active-background-color (colors/custom-color :blue 50 10))
|
||||
(def complete-background-color (colors/custom-color :blue 50))
|
||||
|
||||
(defn container
|
||||
[size type in-blur-view? override-theme]
|
||||
(cond-> container-base
|
||||
(#{1 2} size) (assoc :width 20)
|
||||
(= size 3) (assoc :width 28)
|
||||
|
||||
(= type :neutral)
|
||||
(assoc :border-width 1
|
||||
:border-color (neutral-border-color in-blur-view? override-theme))
|
||||
|
||||
(= type :active)
|
||||
(assoc :background-color active-background-color)
|
||||
|
||||
(= type :complete)
|
||||
(assoc :background-color complete-background-color)))
|
||||
|
||||
(defn text-color
|
||||
([type] (text-color type nil))
|
||||
([type override-theme]
|
||||
(case type
|
||||
(:neutral :active) (colors/theme-colors colors/neutral-100-opa-100 colors/white override-theme)
|
||||
:complete colors/white)))
|
||||
@@ -0,0 +1,21 @@
|
||||
(ns quo2.components.counter.step.view
|
||||
(:require
|
||||
[quo2.components.counter.step.style :as style]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[react-native.core :as rn]
|
||||
[utils.number]))
|
||||
|
||||
(defn step
|
||||
[{:keys [type accessibility-label override-theme in-blur-view?]} value]
|
||||
(let [type (or type :neutral)
|
||||
value (utils.number/parse-int value)
|
||||
label (str value)
|
||||
size (count label)]
|
||||
[rn/view
|
||||
{:accessible true
|
||||
:accessibility-label (or accessibility-label :step-counter)
|
||||
:style (style/container size type in-blur-view? override-theme)}
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :label
|
||||
:style {:color (style/text-color type override-theme)}} label]]))
|
||||
@@ -132,8 +132,9 @@
|
||||
:text text}])]]))))
|
||||
|
||||
(defn- password-input
|
||||
[_]
|
||||
(let [password-shown? (reagent/atom false)]
|
||||
[{:keys [default-shown?]
|
||||
:or {default-shown? false}}]
|
||||
(let [password-shown? (reagent/atom default-shown?)]
|
||||
(fn [props]
|
||||
[base-input
|
||||
(assoc props
|
||||
@@ -161,6 +162,7 @@
|
||||
- :label - A string to set as label for this input.
|
||||
- :char-limit - A number to set a maximum char limit for this input.
|
||||
- :on-char-limit-reach - Function executed each time char limit is reached or exceeded.
|
||||
- :default-shown? - boolean to show password input initially
|
||||
and supports the usual React Native's TextInput properties to control its behaviour:
|
||||
- :value
|
||||
- :default-value
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
(ns quo2.components.inputs.search-input.style
|
||||
(:require [quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn placeholder-color
|
||||
[state blur? override-theme]
|
||||
(cond
|
||||
(and blur? (= state :active))
|
||||
(colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-20 override-theme)
|
||||
|
||||
blur? ; state is default
|
||||
(colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-30 override-theme)
|
||||
|
||||
(= state :active)
|
||||
(colors/theme-colors colors/neutral-30 colors/neutral-60 override-theme)
|
||||
|
||||
:else ; Not blur and state is default
|
||||
(colors/theme-colors colors/neutral-40 colors/neutral-50 override-theme)))
|
||||
|
||||
(def clear-icon-container
|
||||
{:justify-content :center
|
||||
:align-items :center
|
||||
:margin-left 8
|
||||
:height 20
|
||||
:width 20})
|
||||
|
||||
(defn clear-icon
|
||||
[blur? override-theme]
|
||||
(if blur?
|
||||
(colors/theme-colors colors/neutral-80-opa-30 colors/white-opa-10 override-theme)
|
||||
(colors/theme-colors colors/neutral-40 colors/neutral-50 override-theme)))
|
||||
|
||||
(defn cursor
|
||||
[customization-color override-theme]
|
||||
(colors/theme-colors (colors/custom-color customization-color 50)
|
||||
(colors/custom-color customization-color 60)
|
||||
override-theme))
|
||||
|
||||
(def tag-separator {:width 8})
|
||||
|
||||
(def tag-container
|
||||
{:flex-direction :row
|
||||
:margin-right 8})
|
||||
|
||||
(def container
|
||||
{:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def scroll-container
|
||||
{:flex-direction :row
|
||||
:flex 1
|
||||
:height 32})
|
||||
|
||||
(def scroll-content
|
||||
{:flex-grow 1
|
||||
:align-items :center
|
||||
:justify-content :flex-start})
|
||||
|
||||
(defn input-text
|
||||
[disabled?]
|
||||
(assoc (text/text-style {:size :paragraph-1
|
||||
:weight :regular})
|
||||
:flex 1
|
||||
:padding-vertical 5
|
||||
:min-width 120
|
||||
:opacity (if disabled? 0.3 1)
|
||||
:min-height 32))
|
||||
@@ -0,0 +1,89 @@
|
||||
(ns quo2.components.inputs.search-input.view
|
||||
(:require [oops.core :as oops]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.components.inputs.search-input.style :as style]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def ^:private tag-separator [rn/view {:style style/tag-separator}])
|
||||
|
||||
(defn- inner-tags
|
||||
[tags-coll]
|
||||
(into [rn/view {:style style/tag-container}]
|
||||
(interpose tag-separator)
|
||||
tags-coll))
|
||||
|
||||
(defn- clear-button
|
||||
[{:keys [on-press blur? override-theme]}]
|
||||
[rn/touchable-opacity
|
||||
{:style style/clear-icon-container
|
||||
:on-press on-press}
|
||||
[icon/icon :i/clear
|
||||
{:color (style/clear-icon blur? override-theme)
|
||||
:size 20}]])
|
||||
|
||||
(defn- handle-backspace
|
||||
[event ^js/Object scroll-view-ref]
|
||||
(when (and (= (oops/oget event "nativeEvent.key") "Backspace")
|
||||
scroll-view-ref)
|
||||
(.scrollToEnd scroll-view-ref #js {:animated false})))
|
||||
|
||||
(def ^:private props-to-remove
|
||||
[:cursor-color :placeholder-text-color :editable :on-change-text :on-focus
|
||||
:on-blur :on-clear :value :tags :disabled? :blur? :customization-color :override-theme])
|
||||
|
||||
(defn- add-children
|
||||
[text-input children]
|
||||
(if (seq children)
|
||||
(into text-input children)
|
||||
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]
|
||||
:or {customization-color :blue}
|
||||
:as props}
|
||||
& children]
|
||||
(let [clean-props (apply dissoc props props-to-remove)]
|
||||
[rn/view {:style style/container}
|
||||
[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)
|
||||
: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}])]))))
|
||||
@@ -0,0 +1,49 @@
|
||||
(ns quo2.components.links.url-preview-list.component-spec
|
||||
(:require
|
||||
[oops.core :as oops]
|
||||
[quo2.components.links.url-preview-list.view :as view]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(def previews
|
||||
(->> (range 3)
|
||||
(map inc)
|
||||
(mapv (fn [index]
|
||||
{:title (str "Title " index)
|
||||
:body (str "status.im." index)
|
||||
:loading? false
|
||||
:url (str "status.im." index)}))))
|
||||
|
||||
(h/describe "Links - URL Preview List"
|
||||
(h/test "default render"
|
||||
(h/render [view/view
|
||||
{:data previews
|
||||
:key-fn :url
|
||||
:horizontal-spacing 10}])
|
||||
(-> (count (h/query-all-by-label-text :url-preview))
|
||||
(h/expect)
|
||||
(.toEqual 3))
|
||||
|
||||
(-> (map #(oops/oget % "props.children")
|
||||
(h/query-all-by-label-text :title))
|
||||
(clj->js)
|
||||
(h/expect)
|
||||
(.toStrictEqual #js ["Title 1" "Title 2" "Title 3"])))
|
||||
|
||||
(h/test "on-clear event is individually handled by each preview"
|
||||
(let [on-clear (h/mock-fn)]
|
||||
(h/render [view/view
|
||||
{:data previews
|
||||
:key-fn :url
|
||||
:on-clear on-clear}])
|
||||
(h/fire-event :press (first (h/get-all-by-label-text :button-clear-preview)))
|
||||
(h/fire-event :press (second (h/get-all-by-label-text :button-clear-preview)))
|
||||
(h/was-called-times on-clear 2)))
|
||||
|
||||
(h/test "previews have separate loading states"
|
||||
(h/render [view/view
|
||||
{:data (assoc-in previews [1 :loading?] true)
|
||||
:key-fn :url}])
|
||||
(h/is-truthy (h/get-by-label-text :url-preview-loading))
|
||||
(-> (count (h/query-all-by-label-text :url-preview))
|
||||
(h/expect)
|
||||
(.toEqual 2))))
|
||||
@@ -0,0 +1,8 @@
|
||||
(ns quo2.components.links.url-preview-list.style)
|
||||
|
||||
(def url-preview-gap 12)
|
||||
|
||||
(def url-preview-separator
|
||||
{:width url-preview-gap
|
||||
:padding-top 12
|
||||
:padding-bottom 8})
|
||||
@@ -0,0 +1,86 @@
|
||||
(ns quo2.components.links.url-preview-list.view
|
||||
(:require
|
||||
[oops.core :as oops]
|
||||
[quo2.components.links.url-preview-list.style :as style]
|
||||
[quo2.components.links.url-preview.view :as url-preview]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn- use-scroll-to-last-item
|
||||
[flat-list-ref item-count item-width]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(when (and (pos? item-count) (pos? item-width))
|
||||
;; We use a delay because calling `scrollToOffset` without a delay does
|
||||
;; nothing while the flatlist is still rendering its children.
|
||||
;; `scrollToEnd` doesn't work because it positions the item off-center
|
||||
;; and there's no argument to offset it.
|
||||
(let [timer-id (js/setTimeout
|
||||
(fn []
|
||||
(when (and @flat-list-ref (pos? item-count))
|
||||
(.scrollToOffset ^js @flat-list-ref
|
||||
#js
|
||||
{:animated true
|
||||
:offset (* (+ item-width style/url-preview-gap)
|
||||
(max 0 (dec item-count)))})))
|
||||
25)]
|
||||
(fn []
|
||||
(js/clearTimeout timer-id)))))
|
||||
[item-count item-width]))
|
||||
|
||||
(defn- separator
|
||||
[]
|
||||
[rn/view {:style style/url-preview-separator}])
|
||||
|
||||
(defn- item-component
|
||||
[{:keys [title body loading? logo]} _ _
|
||||
{:keys [width on-clear loading-message container-style]}]
|
||||
[url-preview/view
|
||||
{:logo logo
|
||||
:title title
|
||||
:body body
|
||||
:loading? loading?
|
||||
:loading-message loading-message
|
||||
:on-clear on-clear
|
||||
:container-style (merge container-style {:width width})}])
|
||||
|
||||
(defn- calculate-width
|
||||
[preview-width horizontal-spacing ^js e]
|
||||
(reset! preview-width
|
||||
(- (oops/oget e "nativeEvent.layout.width")
|
||||
(* 2 horizontal-spacing))))
|
||||
|
||||
(defn- view-component
|
||||
[]
|
||||
(let [preview-width (reagent/atom 0)
|
||||
flat-list-ref (atom nil)]
|
||||
(fn [{:keys [data key-fn horizontal-spacing on-clear loading-message
|
||||
container-style container-style-item]}]
|
||||
(use-scroll-to-last-item flat-list-ref (count data) @preview-width)
|
||||
;; We need to use a wrapping view expanded to 100% instead of "flex 1",
|
||||
;; otherwise `on-layout` will be triggered multiple times as the flat list
|
||||
;; renders its children.
|
||||
[rn/view
|
||||
{:style (merge container-style {:width "100%"})
|
||||
:accessibility-label :url-preview-list}
|
||||
[rn/flat-list
|
||||
{:ref #(reset! flat-list-ref %)
|
||||
:key-fn key-fn
|
||||
:on-layout #(calculate-width preview-width horizontal-spacing %)
|
||||
:horizontal true
|
||||
:deceleration-rate :fast
|
||||
:on-scroll-to-index-failed identity
|
||||
:content-container-style {:padding-horizontal horizontal-spacing}
|
||||
:separator [separator]
|
||||
:snap-to-interval (+ @preview-width style/url-preview-gap)
|
||||
:shows-horizontal-scroll-indicator false
|
||||
:data data
|
||||
:render-fn item-component
|
||||
:render-data {:width @preview-width
|
||||
:on-clear on-clear
|
||||
:loading-message loading-message
|
||||
:container-style container-style-item}}]])))
|
||||
|
||||
(defn view
|
||||
[props]
|
||||
[:f> view-component props])
|
||||
@@ -30,18 +30,18 @@
|
||||
(when on-press
|
||||
{:on-press on-press}))
|
||||
[rn/view
|
||||
{:style {:flex-direction :row
|
||||
:flex-grow 0
|
||||
:flex-shrink 1
|
||||
:padding-horizontal 20
|
||||
:align-items :center}}
|
||||
{:style (cond-> {:flex-direction :row
|
||||
:flex-grow 0
|
||||
:flex-shrink 1
|
||||
:align-items :center}
|
||||
icon (assoc :padding-horizontal 20))}
|
||||
[rn/view
|
||||
{:style {:width 20
|
||||
:height 20
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:margin-right 12}}
|
||||
[icons/icon icon {:color icon-color}]]
|
||||
{:style (cond-> {:width 20
|
||||
:height 20
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
icon (assoc :margin-right 12))}
|
||||
(when icon [icons/icon icon {:color icon-color}])]
|
||||
[rn/view
|
||||
[text/text
|
||||
{:weight :medium
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
{:no-color true})))
|
||||
|
||||
(defn left-section-view
|
||||
[{:keys [on-press icon accessibility-label type icon-background-color] :or {type :grey}}
|
||||
[{:keys [on-press icon accessibility-label type icon-background-color icon-override-theme]
|
||||
:or {type :grey}}
|
||||
put-middle-section-on-left?]
|
||||
[rn/view {:style (when put-middle-section-on-left? {:margin-right 5})}
|
||||
[button/button
|
||||
@@ -39,6 +40,7 @@
|
||||
:type type
|
||||
:size 32
|
||||
:accessibility-label accessibility-label
|
||||
:override-theme icon-override-theme
|
||||
:override-background-color icon-background-color}
|
||||
icon]])
|
||||
|
||||
@@ -150,7 +152,7 @@
|
||||
:justify-content :flex-end)}
|
||||
(let [last-icon-index (-> right-section-buttons count dec)]
|
||||
(map-indexed (fn [index
|
||||
{:keys [icon on-press type style icon-override-theme accessibility-label]
|
||||
{:keys [icon on-press type style icon-override-theme accessibility-label label]
|
||||
:or {type :grey}}]
|
||||
^{:key index}
|
||||
[rn/view
|
||||
@@ -161,11 +163,12 @@
|
||||
:accessible true))
|
||||
[button/button
|
||||
{:on-press on-press
|
||||
:icon true
|
||||
:icon (not label)
|
||||
:type type
|
||||
:before (when label icon)
|
||||
:size 32
|
||||
:override-theme icon-override-theme}
|
||||
icon]])
|
||||
(if label label icon)]])
|
||||
right-section-buttons))])
|
||||
|
||||
(defn page-nav
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
(ns quo2.components.notifications.notification.component-spec
|
||||
(:require
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.notifications.notification.view :as notification]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "notification"
|
||||
(h/test "empty notification"
|
||||
(h/render [notification/notification {}])
|
||||
(h/is-null (h/query-by-label-text :notification-avatar))
|
||||
(h/is-null (h/query-by-label-text :notification-header))
|
||||
(h/is-null (h/query-by-label-text :notification-body)))
|
||||
(h/test "notification with title and text"
|
||||
(h/render [notification/notification
|
||||
{:title "title"
|
||||
:title-weight :medium
|
||||
:text "text"}])
|
||||
(-> (h/expect (h/get-by-label-text :notification-header))
|
||||
(.toHaveTextContent "title"))
|
||||
(-> (h/expect (h/get-by-label-text :notification-body))
|
||||
(.toHaveTextContent "text")))
|
||||
(h/test "notification with custom input"
|
||||
(h/render [notification/notification
|
||||
{:header [text/text {:accessibility-label :header} "custom header"]
|
||||
:avatar [text/text {:accessibility-label :avatar} "custom avatar"]
|
||||
:body [text/text {:accessibility-label :body} "custom body"]}])
|
||||
(h/is-truthy (h/get-by-label-text :notification-avatar))
|
||||
(h/is-truthy (h/get-by-label-text :notification-header))
|
||||
(h/is-truthy (h/get-by-label-text :notification-body))
|
||||
(h/is-truthy (h/get-by-label-text :avatar))
|
||||
(h/is-truthy (h/get-by-label-text :header))
|
||||
(h/is-truthy (h/get-by-label-text :body))
|
||||
(-> (h/expect (h/get-by-label-text :notification-header))
|
||||
(.toHaveTextContent "custom header"))
|
||||
(-> (h/expect (h/get-by-label-text :notification-avatar))
|
||||
(.toHaveTextContent "custom avatar"))
|
||||
(-> (h/expect (h/get-by-label-text :notification-body))
|
||||
(.toHaveTextContent "custom body"))))
|
||||
@@ -0,0 +1,39 @@
|
||||
(ns quo2.components.notifications.notification.style
|
||||
(:require
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.shadows :as shadows]))
|
||||
|
||||
(def box-container
|
||||
{:margin-horizontal 8
|
||||
:border-radius 16
|
||||
:overflow :hidden})
|
||||
|
||||
(def blur-container
|
||||
{:height "100%"
|
||||
:width "100%"
|
||||
:position :absolute
|
||||
:padding-vertical 8
|
||||
:padding-left 10
|
||||
:padding-right 8
|
||||
:background-color :transparent})
|
||||
|
||||
(defn content-container
|
||||
[override-theme]
|
||||
(merge
|
||||
(:shadow-1 shadows/normal-scale)
|
||||
{:background-color (colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70 override-theme)
|
||||
:flex-direction :row
|
||||
:padding-vertical 8
|
||||
:padding-horizontal 12}))
|
||||
|
||||
(def right-side-container {:flex 1})
|
||||
|
||||
(defn title
|
||||
[override-theme]
|
||||
{:color (colors/theme-colors colors/white colors/neutral-100 override-theme)})
|
||||
|
||||
(defn text
|
||||
[override-theme]
|
||||
{:color (colors/theme-colors colors/white colors/neutral-100 override-theme)})
|
||||
|
||||
(def avatar-container {:margin-right 8 :margin-top 4})
|
||||
@@ -0,0 +1,74 @@
|
||||
(ns quo2.components.notifications.notification.view
|
||||
(:require [quo2.components.markdown.text :as text]
|
||||
[quo2.components.notifications.notification.style :as style]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn header-container
|
||||
[& children]
|
||||
[into
|
||||
[rn/view {:accessibility-label :notification-header}] children])
|
||||
|
||||
(defn body-container
|
||||
[& children]
|
||||
[into [rn/view {:accessibility-label :notification-body}] children])
|
||||
|
||||
(defn avatar-container
|
||||
[& children]
|
||||
[into
|
||||
[rn/view
|
||||
{:style style/avatar-container
|
||||
:accessibility-label :notification-avatar}]
|
||||
children])
|
||||
|
||||
(defn title
|
||||
([text weight] (title text weight nil))
|
||||
([text weight override-theme]
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:weight (or weight :semi-bold)
|
||||
:style (style/title override-theme)
|
||||
:accessibility-label :notification-title}
|
||||
text]))
|
||||
|
||||
(defn message
|
||||
[text override-theme]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:style (style/text override-theme)
|
||||
:accessibility-label :notification-content}
|
||||
text])
|
||||
|
||||
(defn- notification-container
|
||||
[{:keys [avatar header body container-style override-theme]}]
|
||||
[rn/view
|
||||
{:style (merge style/box-container container-style)}
|
||||
[blur/view
|
||||
{:style style/blur-container
|
||||
:blur-amount 13
|
||||
:blur-radius 10
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent}]
|
||||
[rn/view
|
||||
{:style (style/content-container override-theme)}
|
||||
avatar
|
||||
[rn/view
|
||||
{:style style/right-side-container}
|
||||
header
|
||||
body]]])
|
||||
|
||||
(defn notification
|
||||
[{title-text :title :keys [avatar header title-weight text body container-style override-theme]}]
|
||||
(let [header (or header
|
||||
(when title-text
|
||||
[title title-text title-weight override-theme]))
|
||||
header (when header [header-container header])
|
||||
body (or body (when text [message text override-theme]))
|
||||
body (when body [body-container body])
|
||||
avatar (when avatar [avatar-container avatar])]
|
||||
[notification-container
|
||||
{:avatar avatar
|
||||
:header header
|
||||
:body body
|
||||
:container-style container-style
|
||||
:override-theme override-theme}]))
|
||||
@@ -1,122 +0,0 @@
|
||||
(ns quo2.components.notifications.toast
|
||||
(:require [quo2.components.icon :as icon]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.notifications.count-down-circle :as count-down-circle]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.shadows :as shadows]
|
||||
[quo2.theme :as theme]
|
||||
[utils.i18n :as i18n]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(def ^:private themes
|
||||
{:container {:dark {:background-color colors/white-opa-70}
|
||||
:light {:background-color colors/neutral-80-opa-70}}
|
||||
:title {:dark {:color colors/neutral-100}
|
||||
:light {:color colors/white}}
|
||||
:text {:dark {:color colors/neutral-100}
|
||||
:light {:color colors/white}}
|
||||
:icon {:dark {:color colors/neutral-100}
|
||||
:light {:color colors/white}}
|
||||
:action-container {:dark {:background-color colors/neutral-80-opa-5}
|
||||
:light {:background-color colors/white-opa-5}}})
|
||||
|
||||
(defn- merge-theme-style
|
||||
[component-key styles override-theme]
|
||||
(merge (get-in themes [component-key (or override-theme (theme/get-theme))]) styles))
|
||||
|
||||
(defn toast-action-container
|
||||
[{:keys [on-press style]} & children]
|
||||
[rn/touchable-highlight
|
||||
{:on-press on-press
|
||||
:underlay-color :transparent}
|
||||
[into
|
||||
[rn/view
|
||||
{:style (merge
|
||||
{:flex-direction :row
|
||||
:padding-vertical 3
|
||||
:padding-horizontal 8
|
||||
:align-items :center
|
||||
:border-radius 8
|
||||
:background-color (get-in themes
|
||||
[:action-container (theme/get-theme)
|
||||
:background-color])}
|
||||
style)}]
|
||||
children]])
|
||||
|
||||
(defn toast-undo-action
|
||||
[duration on-press override-theme]
|
||||
[toast-action-container
|
||||
{:on-press on-press :accessibility-label :toast-undo-action}
|
||||
[rn/view {:style {:margin-right 5}}
|
||||
[count-down-circle/circle-timer {:duration duration}]]
|
||||
[text/text
|
||||
{:size :paragraph-2 :weight :medium :style (merge-theme-style :text {} override-theme)}
|
||||
[i18n/label :t/undo]]])
|
||||
|
||||
(defn- toast-container
|
||||
[{:keys [left title text right container-style override-theme]}]
|
||||
[rn/view
|
||||
{:style (merge {:margin-horizontal 12
|
||||
:border-radius 12
|
||||
:overflow :hidden}
|
||||
container-style)}
|
||||
[blur/view
|
||||
{:style {:height "100%"
|
||||
:width "100%"
|
||||
:position :absolute
|
||||
:padding-vertical 8
|
||||
:padding-left 10
|
||||
:padding-right 8
|
||||
:background-color :transparent}
|
||||
:blur-amount 13
|
||||
:blur-radius 10
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent}]
|
||||
[rn/view
|
||||
{:style (merge-theme-style :container
|
||||
(merge
|
||||
(:shadow-1 shadows/normal-scale)
|
||||
{:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:padding-vertical 8
|
||||
:padding-left 10
|
||||
:padding-right 8
|
||||
:border-radius 12})
|
||||
override-theme)}
|
||||
[rn/view {:style {:padding 2}} left]
|
||||
[rn/view {:style {:padding 4 :flex 1}}
|
||||
(when title
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style (merge-theme-style :title {} override-theme)
|
||||
:accessibility-label :toast-title}
|
||||
title])
|
||||
(when text
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (merge-theme-style :text {} override-theme)
|
||||
:accessibility-label :toast-content}
|
||||
text])]
|
||||
(when right right)]])
|
||||
|
||||
(defn toast
|
||||
[{:keys [icon icon-color title text action undo-duration undo-on-press container-style
|
||||
override-theme]}]
|
||||
[toast-container
|
||||
{:left (when icon
|
||||
[icon/icon icon
|
||||
{:container-style {:width 20 :height 20}
|
||||
:color (or icon-color
|
||||
(get-in themes
|
||||
[:icon (or override-theme (theme/get-theme))
|
||||
:color]))}])
|
||||
:title title
|
||||
:text text
|
||||
:right (if undo-duration
|
||||
[toast-undo-action undo-duration undo-on-press override-theme]
|
||||
action)
|
||||
:container-style container-style
|
||||
:override-theme override-theme}])
|
||||
@@ -0,0 +1,55 @@
|
||||
(ns quo2.components.notifications.toast.style
|
||||
(:require
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.shadows :as shadows]))
|
||||
|
||||
(def box-container
|
||||
{:margin-horizontal 12
|
||||
:border-radius 12
|
||||
:overflow :hidden})
|
||||
|
||||
(def blur-container
|
||||
{:height "100%"
|
||||
:width "100%"
|
||||
:position :absolute
|
||||
:padding-vertical 8
|
||||
:padding-left 10
|
||||
:padding-right 8
|
||||
:background-color :transparent})
|
||||
|
||||
(defn content-container
|
||||
[override-theme]
|
||||
(merge
|
||||
(:shadow-1 shadows/normal-scale)
|
||||
{:background-color (colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70 override-theme)
|
||||
:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:padding-vertical 8
|
||||
:padding-left 10
|
||||
:padding-right 8
|
||||
:border-radius 12}))
|
||||
|
||||
(defn title
|
||||
[override-theme]
|
||||
{:color (colors/theme-colors colors/white colors/neutral-100 override-theme)})
|
||||
|
||||
(defn text
|
||||
[override-theme]
|
||||
{:color (colors/theme-colors colors/white colors/neutral-100 override-theme)})
|
||||
|
||||
(defn icon
|
||||
[override-theme]
|
||||
{:color (colors/theme-colors colors/white colors/neutral-100 override-theme)
|
||||
:container-style {:width 20 :height 20}})
|
||||
|
||||
(def left-side-container {:padding 2})
|
||||
(def right-side-container {:padding 4 :flex 1})
|
||||
|
||||
(defn action-container
|
||||
[override-theme]
|
||||
{:background-color (colors/theme-colors colors/white-opa-5 colors/neutral-80-opa-5 override-theme)
|
||||
:flex-direction :row
|
||||
:padding-vertical 3
|
||||
:padding-horizontal 8
|
||||
:align-items :center
|
||||
:border-radius 8})
|
||||
@@ -0,0 +1,80 @@
|
||||
(ns quo2.components.notifications.toast.view
|
||||
(:require [quo2.components.avatars.user-avatar.view :as user-avatar]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.notifications.count-down-circle :as count-down-circle]
|
||||
[quo2.components.notifications.toast.style :as style]
|
||||
[quo2.theme :as theme]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.core :as rn]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(defn toast-action-container
|
||||
[{:keys [on-press style]} & children]
|
||||
[rn/touchable-highlight
|
||||
{:on-press on-press
|
||||
:underlay-color :transparent}
|
||||
[into
|
||||
[rn/view
|
||||
{:style (merge (style/action-container (theme/get-theme)) style)}]
|
||||
children]])
|
||||
|
||||
(defn toast-undo-action
|
||||
[duration on-press override-theme]
|
||||
[toast-action-container
|
||||
{:on-press on-press :accessibility-label :toast-undo-action}
|
||||
[rn/view {:style {:margin-right 5}}
|
||||
[count-down-circle/circle-timer {:duration duration}]]
|
||||
[text/text
|
||||
{:size :paragraph-2 :weight :medium :style (style/text override-theme)}
|
||||
[i18n/label :t/undo]]])
|
||||
|
||||
(defn- toast-container
|
||||
[{:keys [left title text right container-style override-theme]}]
|
||||
[rn/view
|
||||
{:style (merge style/box-container container-style)}
|
||||
[blur/view
|
||||
{:style style/blur-container
|
||||
:blur-amount 13
|
||||
:blur-radius 10
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent}]
|
||||
[rn/view
|
||||
{:style (style/content-container override-theme)}
|
||||
[rn/view {:style style/left-side-container} left]
|
||||
[rn/view {:style style/right-side-container}
|
||||
(when title
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style (style/title override-theme)
|
||||
:accessibility-label :toast-title}
|
||||
title])
|
||||
(when text
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/text override-theme)
|
||||
:accessibility-label :toast-content}
|
||||
text])]
|
||||
(when right right)]])
|
||||
|
||||
(defn toast
|
||||
[{:keys [icon icon-color title text action undo-duration undo-on-press container-style
|
||||
override-theme user]}]
|
||||
[toast-container
|
||||
{:left (cond icon
|
||||
[icon/icon icon
|
||||
(cond-> (style/icon override-theme)
|
||||
icon-color
|
||||
(assoc :color icon-color))]
|
||||
|
||||
user
|
||||
[user-avatar/user-avatar user])
|
||||
:title title
|
||||
:text text
|
||||
:right (if undo-duration
|
||||
[toast-undo-action undo-duration undo-on-press override-theme]
|
||||
action)
|
||||
:container-style container-style
|
||||
:override-theme override-theme}])
|
||||
@@ -1,4 +1,5 @@
|
||||
(ns quo2.components.share.qr-code.style)
|
||||
(ns quo2.components.share.qr-code.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def container
|
||||
{:flex-direction :row
|
||||
@@ -6,7 +7,8 @@
|
||||
|
||||
(defn image
|
||||
[width height]
|
||||
{:width width
|
||||
:height height
|
||||
:border-radius 12
|
||||
:aspect-ratio 1})
|
||||
{:width width
|
||||
:height height
|
||||
:background-color colors/white-opa-70
|
||||
:border-radius 12
|
||||
:aspect-ratio 1})
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
(ns quo2.components.tags.context-tag.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn base-tag
|
||||
[override-theme blur?]
|
||||
{:border-radius 100
|
||||
:padding-vertical 3
|
||||
:flex-direction :row
|
||||
:padding-right 8
|
||||
:padding-left 8
|
||||
:background-color (if blur?
|
||||
(colors/theme-colors colors/neutral-80-opa-5 colors/white-opa-5 override-theme)
|
||||
(colors/theme-colors colors/neutral-10 colors/neutral-90 override-theme))})
|
||||
|
||||
(def context-tag-image
|
||||
{:width 20
|
||||
:border-radius 10
|
||||
:background-color :white
|
||||
:height 20})
|
||||
|
||||
(defn context-tag-icon-color
|
||||
[blur?]
|
||||
(if blur?
|
||||
(colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-40)
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40)))
|
||||
|
||||
(def context-tag-text-container
|
||||
{:align-items :center
|
||||
:flex-direction :row})
|
||||
|
||||
(def audio-tag-container
|
||||
{:padding-left 2
|
||||
:padding-vertical 2})
|
||||
|
||||
(def audio-tag-icon-container
|
||||
{:width 20
|
||||
:height 20
|
||||
:border-radius 10
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:background-color colors/primary-50})
|
||||
|
||||
(def audio-tag-icon-color colors/white)
|
||||
|
||||
(defn audio-tag-text-color
|
||||
[override-theme]
|
||||
(colors/theme-colors colors/neutral-100 colors/white override-theme))
|
||||
|
||||
(def community-tag
|
||||
{:padding-vertical 2})
|
||||
|
||||
(defn community-tag-text
|
||||
[override-theme]
|
||||
{:margin-left 2
|
||||
:color (colors/theme-colors colors/neutral-100 colors/white override-theme)})
|
||||
@@ -0,0 +1,85 @@
|
||||
(ns quo2.components.tags.context-tag.view
|
||||
(:require [quo2.components.avatars.group-avatar :as group-avatar]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.tags.context-tag.style :as style]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn trim-public-key
|
||||
[pk]
|
||||
(str (subs pk 0 6) "..." (subs pk (- (count pk) 3))))
|
||||
|
||||
(defn base-tag
|
||||
[{:keys [override-theme style blur?]} & children]
|
||||
(into
|
||||
[rn/view {:style (merge (style/base-tag override-theme blur?) style)}]
|
||||
children))
|
||||
|
||||
(defn group-avatar-tag
|
||||
[label opts]
|
||||
[base-tag
|
||||
(-> opts
|
||||
(select-keys [:override-theme :style :blur?])
|
||||
(assoc-in [:style :padding-left] 3)
|
||||
(assoc-in [:style :padding-vertical] 2))
|
||||
[group-avatar/group-avatar opts]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (:text-style opts)}
|
||||
(str " " label)]])
|
||||
|
||||
(defn public-key-tag
|
||||
[params public-key]
|
||||
[base-tag params
|
||||
[text/text
|
||||
{:weight :monospace
|
||||
:size :paragraph-2}
|
||||
(trim-public-key public-key)]])
|
||||
|
||||
(defn context-tag
|
||||
[{:keys [text-style blur?] :as params} photo name channel-name]
|
||||
(let [text-params {:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (assoc text-style :justify-content :center)}]
|
||||
[base-tag (assoc-in params [:style :padding-left] 3)
|
||||
[rn/image
|
||||
{:style style/context-tag-image
|
||||
:source photo}]
|
||||
[rn/view {:style style/context-tag-text-container}
|
||||
[text/text text-params (str " " name)]
|
||||
(when channel-name
|
||||
[:<>
|
||||
[icons/icon
|
||||
:i/chevron-right
|
||||
{:color (style/context-tag-icon-color blur?)
|
||||
:size 16}]
|
||||
[text/text text-params (str "# " channel-name)]])]]))
|
||||
|
||||
(defn user-avatar-tag
|
||||
[params username photo]
|
||||
[context-tag params {:uri photo} username])
|
||||
|
||||
(defn audio-tag
|
||||
[duration params]
|
||||
[base-tag (merge {:style style/audio-tag-container} params)
|
||||
[rn/view {:style style/audio-tag-icon-container}
|
||||
[icons/icon
|
||||
:i/play
|
||||
{:color style/audio-tag-icon-color
|
||||
:size 12}]]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style {:margin-left 4
|
||||
:color (style/audio-tag-text-color (:override-theme params))}}
|
||||
duration]])
|
||||
|
||||
(defn community-tag
|
||||
[avatar community-name {:keys [override-theme] :as params}]
|
||||
[context-tag
|
||||
(merge {:style style/community-tag
|
||||
:text-style (style/community-tag-text override-theme)}
|
||||
params)
|
||||
avatar
|
||||
community-name])
|
||||
@@ -1,126 +0,0 @@
|
||||
(ns quo2.components.tags.context-tags
|
||||
(:require [quo2.components.avatars.group-avatar :as group-avatar]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as quo2.theme]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn trim-public-key
|
||||
[pk]
|
||||
(str (subs pk 0 6) "..." (subs pk (- (count pk) 3))))
|
||||
|
||||
(defn base-tag
|
||||
[_ _]
|
||||
(fn [{:keys [override-theme style]} & children]
|
||||
(let [theme (or override-theme (quo2.theme/get-theme))]
|
||||
(into
|
||||
[rn/view
|
||||
(merge
|
||||
{:border-radius 100
|
||||
:padding-vertical 3
|
||||
:flex-direction :row
|
||||
:padding-right 8
|
||||
:padding-left 8
|
||||
:background-color (if (= theme :light)
|
||||
colors/neutral-10
|
||||
colors/neutral-90)}
|
||||
style)]
|
||||
children))))
|
||||
|
||||
(defn group-avatar-tag
|
||||
[_ _]
|
||||
(fn [label opts]
|
||||
[base-tag
|
||||
(-> opts
|
||||
(select-keys [:override-theme :style])
|
||||
(assoc-in [:style :padding-left] 3)
|
||||
(assoc-in [:style :padding-vertical] 2))
|
||||
[group-avatar/group-avatar opts]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (:text-style opts)}
|
||||
(str " " label)]]))
|
||||
|
||||
(defn public-key-tag
|
||||
[_ _]
|
||||
(fn [params public-key]
|
||||
[base-tag params
|
||||
[text/text
|
||||
{:weight :monospace
|
||||
:size :paragraph-2}
|
||||
(trim-public-key public-key)]]))
|
||||
|
||||
(defn context-tag
|
||||
[_ _]
|
||||
(fn [params photo name channel-name]
|
||||
(let [text-style (:text-style params)
|
||||
text-params {:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (assoc text-style :justify-content :center)}
|
||||
icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)]
|
||||
[base-tag (assoc-in params [:style :padding-left] 3)
|
||||
[rn/image
|
||||
{:style {:width 20
|
||||
:border-radius 10
|
||||
:background-color :white
|
||||
:height 20}
|
||||
:source photo}]
|
||||
[rn/view
|
||||
{:style {:align-items :center
|
||||
:flex-direction :row}}
|
||||
[text/text text-params (str " " name)]
|
||||
(when channel-name
|
||||
[:<>
|
||||
[icons/icon
|
||||
:i/chevron-right
|
||||
{:color icon-color
|
||||
:size 16}]
|
||||
[text/text text-params (str "# " channel-name)]])]])))
|
||||
|
||||
(defn user-avatar-tag
|
||||
[]
|
||||
(fn [params username photo]
|
||||
[context-tag params {:uri photo} username]))
|
||||
|
||||
(defn audio-tag
|
||||
[duration params]
|
||||
[base-tag
|
||||
(merge
|
||||
{:style {:padding-left 2
|
||||
:padding-vertical 2}}
|
||||
params)
|
||||
[rn/view
|
||||
{:width 20
|
||||
:height 20
|
||||
:border-radius 10
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:background-color colors/primary-50}
|
||||
[icons/icon
|
||||
:i/play
|
||||
{:color colors/white
|
||||
:size 12}]]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style {:margin-left 4
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-100
|
||||
colors/white
|
||||
(:override-theme params))}}
|
||||
duration]])
|
||||
|
||||
(defn community-tag
|
||||
[avatar community-name params]
|
||||
[context-tag
|
||||
(merge
|
||||
{:style {:padding-vertical 2}
|
||||
:text-style {:margin-left 2
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-100
|
||||
colors/white
|
||||
(:override-theme params))}}
|
||||
params)
|
||||
avatar community-name])
|
||||
@@ -18,6 +18,7 @@
|
||||
quo2.components.community.icon
|
||||
quo2.components.community.token-gating
|
||||
quo2.components.counter.counter
|
||||
quo2.components.counter.step.view
|
||||
quo2.components.dividers.date
|
||||
quo2.components.dividers.divider-label
|
||||
quo2.components.dividers.new-messages
|
||||
@@ -31,12 +32,15 @@
|
||||
quo2.components.info.info-message
|
||||
quo2.components.info.information-box
|
||||
quo2.components.inputs.input.view
|
||||
quo2.components.inputs.title-input.view
|
||||
quo2.components.inputs.profile-input.view
|
||||
quo2.components.inputs.search-input.view
|
||||
quo2.components.inputs.title-input.view
|
||||
quo2.components.links.url-preview-list.view
|
||||
quo2.components.links.url-preview.view
|
||||
quo2.components.list-items.channel
|
||||
quo2.components.list-items.menu-item
|
||||
quo2.components.list-items.preview-list
|
||||
quo2.components.list-items.user-list
|
||||
quo2.components.loaders.skeleton
|
||||
quo2.components.markdown.text
|
||||
quo2.components.messages.author.view
|
||||
@@ -48,7 +52,9 @@
|
||||
quo2.components.notifications.count-down-circle
|
||||
quo2.components.notifications.info-count
|
||||
quo2.components.notifications.notification-dot
|
||||
quo2.components.notifications.toast
|
||||
quo2.components.notifications.notification.view
|
||||
quo2.components.notifications.toast.view
|
||||
quo2.components.onboarding.small-option-card.view
|
||||
quo2.components.password.tips.view
|
||||
quo2.components.profile.profile-card.view
|
||||
quo2.components.profile.select-profile.view
|
||||
@@ -62,26 +68,22 @@
|
||||
quo2.components.settings.privacy-option
|
||||
quo2.components.share.qr-code.view
|
||||
quo2.components.share.share-qr-code.view
|
||||
quo2.components.onboarding.small-option-card.view
|
||||
quo2.components.tabs.segmented-tab
|
||||
quo2.components.tabs.account-selector
|
||||
quo2.components.tabs.segmented-tab
|
||||
quo2.components.tabs.tabs
|
||||
quo2.components.tags.context-tags
|
||||
quo2.components.tags.status-tags
|
||||
quo2.components.tags.context-tag.view
|
||||
quo2.components.tags.permission-tag
|
||||
quo2.components.tags.status-tags
|
||||
quo2.components.tags.tag
|
||||
quo2.components.tags.tags
|
||||
quo2.components.tags.token-tag
|
||||
quo2.components.text-combinations.title.view
|
||||
quo2.components.list-items.user-list))
|
||||
quo2.components.text-combinations.title.view))
|
||||
|
||||
(def toast quo2.components.notifications.toast/toast)
|
||||
(def button quo2.components.buttons.button/button)
|
||||
(def dynamic-button quo2.components.buttons.dynamic-button/dynamic-button)
|
||||
(def text quo2.components.markdown.text/text)
|
||||
(def icon quo2.components.icon/icon)
|
||||
(def separator quo2.components.separator/separator)
|
||||
(def counter quo2.components.counter.counter/counter)
|
||||
(def header quo2.components.header/header)
|
||||
(def dropdown quo2.components.dropdowns.dropdown/dropdown)
|
||||
(def info-message quo2.components.info.info-message/info-message)
|
||||
@@ -90,12 +92,11 @@
|
||||
(def system-message quo2.components.messages.system-message/system-message)
|
||||
(def reaction quo2.components.reactions.reaction/reaction)
|
||||
(def add-reaction quo2.components.reactions.reaction/add-reaction)
|
||||
(def user-avatar-tag quo2.components.tags.context-tags/user-avatar-tag)
|
||||
(def context-tag quo2.components.tags.context-tags/context-tag)
|
||||
(def group-avatar-tag quo2.components.tags.context-tags/group-avatar-tag)
|
||||
(def audio-tag quo2.components.tags.context-tags/audio-tag)
|
||||
(def community-tag quo2.components.tags.context-tags/community-tag)
|
||||
|
||||
(def user-avatar-tag quo2.components.tags.context-tag.view/user-avatar-tag)
|
||||
(def context-tag quo2.components.tags.context-tag.view/context-tag)
|
||||
(def group-avatar-tag quo2.components.tags.context-tag.view/group-avatar-tag)
|
||||
(def audio-tag quo2.components.tags.context-tag.view/audio-tag)
|
||||
(def community-tag quo2.components.tags.context-tag.view/community-tag)
|
||||
(def floating-shell-button quo2.components.navigation.floating-shell-button/floating-shell-button)
|
||||
(def page-nav quo2.components.navigation.page-nav/page-nav)
|
||||
(def disclaimer quo2.components.selectors.disclaimer.view/view)
|
||||
@@ -136,6 +137,10 @@
|
||||
(def token-gating quo2.components.community.token-gating/token-gating)
|
||||
(def community-icon quo2.components.community.icon/community-icon)
|
||||
|
||||
;;;; COUNTER
|
||||
(def counter quo2.components.counter.counter/counter)
|
||||
(def step quo2.components.counter.step.view/step)
|
||||
|
||||
;;;; DIVIDERS
|
||||
(def divider-label quo2.components.dividers.divider-label/divider-label)
|
||||
(def new-messages quo2.components.dividers.new-messages/new-messages)
|
||||
@@ -149,6 +154,7 @@
|
||||
|
||||
;;;; INPUTS
|
||||
(def input quo2.components.inputs.input.view/input)
|
||||
(def search-input quo2.components.inputs.search-input.view/search-input)
|
||||
(def profile-input quo2.components.inputs.profile-input.view/profile-input)
|
||||
(def title-input quo2.components.inputs.title-input.view/title-input)
|
||||
|
||||
@@ -163,6 +169,8 @@
|
||||
(def info-count quo2.components.notifications.info-count/info-count)
|
||||
(def notification-dot quo2.components.notifications.notification-dot/notification-dot)
|
||||
(def count-down-circle quo2.components.notifications.count-down-circle/circle-timer)
|
||||
(def notification quo2.components.notifications.notification.view/notification)
|
||||
(def toast quo2.components.notifications.toast.view/toast)
|
||||
|
||||
;;;; PASSWORD
|
||||
(def tips quo2.components.password.tips.view/view)
|
||||
@@ -199,3 +207,4 @@
|
||||
|
||||
;;;; LINKS
|
||||
(def url-preview quo2.components.links.url-preview.view/view)
|
||||
(def url-preview-list quo2.components.links.url-preview-list.view/view)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
[quo2.components.buttons.--tests--.buttons-component-spec]
|
||||
[quo2.components.colors.color-picker.component-spec]
|
||||
[quo2.components.counter.--tests--.counter-component-spec]
|
||||
[quo2.components.counter.step.component-spec]
|
||||
[quo2.components.dividers.--tests--.divider-label-component-spec]
|
||||
[quo2.components.dividers.strength-divider.component-spec]
|
||||
[quo2.components.drawers.action-drawers.component-spec]
|
||||
@@ -13,8 +14,10 @@
|
||||
[quo2.components.inputs.input.component-spec]
|
||||
[quo2.components.inputs.profile-input.component-spec]
|
||||
[quo2.components.inputs.title-input.component-spec]
|
||||
[quo2.components.links.url-preview-list.component-spec]
|
||||
[quo2.components.links.url-preview.component-spec]
|
||||
[quo2.components.markdown.--tests--.text-component-spec]
|
||||
[quo2.components.notifications.notification.component-spec]
|
||||
[quo2.components.onboarding.small-option-card.component-spec]
|
||||
[quo2.components.password.tips.component-spec]
|
||||
[quo2.components.profile.select-profile.component-spec]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
(ns react-native.hooks
|
||||
(:require ["@react-native-community/hooks" :as hooks]))
|
||||
(:require ["@react-native-community/hooks" :as hooks]
|
||||
[react-native.core :as rn]
|
||||
[oops.core :as oops]))
|
||||
|
||||
(defn use-keyboard
|
||||
[]
|
||||
@@ -10,3 +12,21 @@
|
||||
(defn use-back-handler
|
||||
[handler]
|
||||
(.useBackHandler hooks handler))
|
||||
|
||||
(defn use-interval
|
||||
[cb cleanup-cb delay]
|
||||
(let [saved-callback (rn/use-ref)]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(oops/oset! saved-callback "current" cb))
|
||||
[cb])
|
||||
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(let [tick (oops/oget saved-callback "current")]
|
||||
(when delay
|
||||
(let [id (js/setInterval tick delay)]
|
||||
(fn []
|
||||
(cleanup-cb)
|
||||
(js/clearInterval id))))))
|
||||
[delay])))
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
(def clippath (reagent/adapt-react-class Svg/ClipPath))
|
||||
(def defs (reagent/adapt-react-class Svg/Defs))
|
||||
(def circle (reagent/adapt-react-class Svg/Circle))
|
||||
|
||||
(def svgxml (reagent/adapt-react-class Svg/SvgXml))
|
||||
|
||||
@@ -87,23 +87,27 @@
|
||||
(:communities/community-id-input db))
|
||||
|
||||
(defn- handle-my-request
|
||||
[db {:keys [community-id state] :as request}]
|
||||
{:db (if (= constants/community-request-to-join-state-pending state)
|
||||
(assoc-in db [:communities/my-pending-requests-to-join community-id] request)
|
||||
(update-in db [:communities/my-pending-requests-to-join] dissoc community-id))})
|
||||
[db {:keys [community-id state deleted] :as request}]
|
||||
(if (and (= constants/community-request-to-join-state-pending state) (not deleted))
|
||||
(assoc-in db [:communities/my-pending-requests-to-join community-id] request)
|
||||
(update-in db [:communities/my-pending-requests-to-join] dissoc community-id)))
|
||||
|
||||
(defn handle-admin-request
|
||||
[db {:keys [id community-id] :as request}]
|
||||
{:db (assoc-in db [:communities/requests-to-join community-id id] request)})
|
||||
[db {:keys [id community-id deleted] :as request}]
|
||||
(if deleted
|
||||
(update-in db [:communities/requests-to-join community-id] dissoc id)
|
||||
(assoc-in db [:communities/requests-to-join community-id id] request)))
|
||||
|
||||
(rf/defn handle-request-to-join
|
||||
[{:keys [db]} r]
|
||||
(let [my-public-key (get-in db [:multiaccount :public-key])
|
||||
{:keys [id community-id public-key] :as request} (<-request-to-join-community-rpc r)
|
||||
my-request? (= my-public-key public-key)]
|
||||
(if my-request?
|
||||
(handle-my-request db request)
|
||||
(handle-admin-request db request))))
|
||||
(rf/defn handle-requests-to-join
|
||||
[{:keys [db]} requests]
|
||||
(let [my-public-key (get-in db [:multiaccount :public-key])]
|
||||
{:db (reduce (fn [db {:keys [public-key] :as request}]
|
||||
(let [my-request? (= my-public-key public-key)]
|
||||
(if my-request?
|
||||
(handle-my-request db request)
|
||||
(handle-admin-request db request))))
|
||||
db
|
||||
requests)}))
|
||||
|
||||
(rf/defn handle-removed-chats
|
||||
[{:keys [db]} chat-ids]
|
||||
@@ -317,6 +321,7 @@
|
||||
:on-error #(do
|
||||
(log/error "failed to invite-user community" %)
|
||||
(re-frame/dispatch [::failed-to-invite-people %]))}]})))
|
||||
|
||||
(rf/defn share-community
|
||||
{:events [::share-community-confirmation-pressed]}
|
||||
[cofx user-pk contacts]
|
||||
@@ -898,3 +903,16 @@
|
||||
:event :communities/toggle-collapsed-category
|
||||
:category-id category-id
|
||||
:collapse? collapse?})}]})
|
||||
|
||||
(rf/defn check-and-delete-pending-request-to-join
|
||||
{:events [:communities/check-and-delete-pending-request-to-join]}
|
||||
[_]
|
||||
{:json-rpc/call [{:method "wakuext_checkAndDeletePendingRequestToJoinCommunity"
|
||||
:params []
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/info
|
||||
"failed to fetch communities"
|
||||
{:error %
|
||||
:event
|
||||
:communities/check-and-delete-pending-request-to-join-community})}]})
|
||||
|
||||
@@ -60,7 +60,8 @@
|
||||
(:contacts/contacts db)
|
||||
public-key)
|
||||
(assoc :blocked? true
|
||||
:added? false))
|
||||
:added? false
|
||||
:active? false))
|
||||
from-one-to-one-chat? (not (get-in db [:chats (:current-chat-id db) :group-chat]))]
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:contacts/contacts public-key] contact)}
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
:identicon (.-identicon chat)
|
||||
:muted (.-muted chat)
|
||||
:joined (.-joined chat)
|
||||
|
||||
:muted-till (.-mutetill chat)
|
||||
:chat-id (.-id chat)
|
||||
:community-id (.-communityId chat)
|
||||
:synced-from (.-syncedFrom chat)
|
||||
|
||||
@@ -59,7 +59,8 @@
|
||||
status-im2.contexts.shell.events
|
||||
status-im2.contexts.onboarding.events
|
||||
status-im.chat.models.gaps
|
||||
[status-im2.navigation.events :as navigation]))
|
||||
[status-im2.navigation.events :as navigation]
|
||||
status-im2.contexts.chat.home.events))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:dismiss-keyboard
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
status-im2.navigation.core
|
||||
status-im2.subs.root ; so integration tests can run independently
|
||||
[taoensso.timbre :as log]
|
||||
[utils.security.core :as security]))
|
||||
[utils.security.core :as security]
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(def password "testabc")
|
||||
|
||||
@@ -295,7 +296,7 @@
|
||||
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
|
||||
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
|
||||
(is @(rf/subscribe [:chats/chat chat-id]))
|
||||
(rf/dispatch-sync [:chat.ui/mute chat-id true])
|
||||
(rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted])
|
||||
(rf-test/wait-for
|
||||
[:chat/mute-successfully]
|
||||
(is @(rf/subscribe [:chats/muted chat-id]))
|
||||
|
||||
@@ -365,9 +365,25 @@
|
||||
[_]
|
||||
(status/get-node-config #(re-frame/dispatch [::get-node-config-callback %])))
|
||||
|
||||
(rf/defn redirect-to-root
|
||||
"Decides which root should be initialised depending on user and app state"
|
||||
[{:keys [db] :as cofx}]
|
||||
(cond
|
||||
(get db :onboarding-2/new-account?)
|
||||
{:dispatch [:onboarding-2/finalize-setup]}
|
||||
|
||||
(get db :tos/accepted?)
|
||||
(rf/merge
|
||||
cofx
|
||||
(multiaccounts/switch-theme nil :shell-stack)
|
||||
(navigation/init-root :shell-stack))
|
||||
|
||||
:else
|
||||
{:dispatch [:init-root :tos]}))
|
||||
|
||||
(rf/defn get-settings-callback
|
||||
{:events [::get-settings-callback]}
|
||||
[{:keys [db] :as cofx} settings]
|
||||
[{:keys [db] :as cofx} settings pairing-in-progress?]
|
||||
(let [{:networks/keys [current-network networks]
|
||||
:as settings}
|
||||
(data-store.settings/rpc->settings settings)
|
||||
@@ -394,10 +410,13 @@
|
||||
(get-node-config)
|
||||
(communities/fetch)
|
||||
(communities/fetch-collapsed-community-categories)
|
||||
(communities/check-and-delete-pending-request-to-join)
|
||||
(logging/set-log-level (:log-level multiaccount))
|
||||
(activity-center/notifications-fetch-pending-contact-requests)
|
||||
(activity-center/update-seen-state)
|
||||
(activity-center/notifications-fetch-unread-count))))
|
||||
(activity-center/notifications-fetch-unread-count)
|
||||
(when-not pairing-in-progress?
|
||||
(redirect-to-root)))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::open-last-chat
|
||||
@@ -472,19 +491,6 @@
|
||||
keychain/auth-method-biometric
|
||||
keychain/auth-method-password))))
|
||||
|
||||
(defn redirect-to-root
|
||||
"Decides which root should be initialised depending on user and app state"
|
||||
[db]
|
||||
(cond
|
||||
(get db :onboarding-2/new-account?)
|
||||
(re-frame/dispatch [:onboarding-2/finalize-setup])
|
||||
|
||||
(get db :tos/accepted?)
|
||||
(re-frame/dispatch [:init-root :shell-stack])
|
||||
|
||||
:else
|
||||
(re-frame/dispatch [:init-root :tos])))
|
||||
|
||||
(rf/defn login-only-events
|
||||
[{:keys [db] :as cofx} key-uid password save-password?]
|
||||
(let [auth-method (:auth-method db)
|
||||
@@ -497,9 +503,7 @@
|
||||
{:db (assoc db :chats/loading? true)
|
||||
:json-rpc/call
|
||||
[{:method "settings_getSettings"
|
||||
:on-success #(do (re-frame/dispatch [::get-settings-callback %])
|
||||
(when-not pairing-in-progress?
|
||||
(redirect-to-root db)))}]}
|
||||
:on-success #(re-frame/dispatch [::get-settings-callback % pairing-in-progress?])}]}
|
||||
(notifications/load-notification-preferences)
|
||||
(when save-password?
|
||||
(keychain/save-user-password key-uid password))
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
(ns status-im.search.core
|
||||
(:require [utils.re-frame :as rf]))
|
||||
|
||||
(rf/defn home-filter-changed
|
||||
{:events [:search/home-filter-changed]}
|
||||
[cofx search-filter]
|
||||
{:db (assoc-in (:db cofx) [:ui/search :home-filter] search-filter)})
|
||||
|
||||
(rf/defn currency-filter-changed
|
||||
{:events [:search/currency-filter-changed]}
|
||||
[cofx search-filter]
|
||||
|
||||
@@ -124,10 +124,13 @@
|
||||
(models.communities/handle-removed-chats removed-chats-clj)))
|
||||
|
||||
(seq requests-to-join-community)
|
||||
(let [request-js (types/js->clj (.pop requests-to-join-community))]
|
||||
(let [requests (->> requests-to-join-community
|
||||
types/js->clj
|
||||
(map models.communities/<-request-to-join-community-rpc))]
|
||||
(js-delete response-js "requestsToJoinCommunity")
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.communities/handle-request-to-join request-js)))
|
||||
(models.communities/handle-requests-to-join requests)))
|
||||
|
||||
(seq emoji-reactions)
|
||||
(let [reactions (types/js->clj emoji-reactions)]
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
(ns status-im.ui.components.qr-code-viewer.views
|
||||
(:require ["qrcode" :as qr-code-js]
|
||||
["react-native-svg" :refer (SvgXml)]
|
||||
[cljs-bean.core :as bean]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.ui.components.qr-code-viewer.styles :as styles]
|
||||
[status-im.ui.components.react :as react]))
|
||||
|
||||
(def svgxml (reagent/adapt-react-class SvgXml))
|
||||
|
||||
(defn qr-code
|
||||
[{:keys [size value]}]
|
||||
(let [uri (reagent/atom nil)]
|
||||
(.toString
|
||||
qr-code-js
|
||||
value
|
||||
(bean/->js {:margin 0 :width size})
|
||||
#(reset! uri %2))
|
||||
(fn []
|
||||
(when @uri
|
||||
[svgxml {:xml @uri :width size :height size}]))))
|
||||
|
||||
(defn qr-code-view
|
||||
"Qr Code view including the frame.
|
||||
Note: `size` includes frame with `styles/qr-code-padding.`"
|
||||
[size value]
|
||||
(when (and size value)
|
||||
[react/view
|
||||
{:style (styles/qr-code-container size)
|
||||
:accessibility-label :qr-code-image}
|
||||
[qr-code
|
||||
{:value value
|
||||
:size (- size (* styles/qr-code-padding 2))}]]))
|
||||
@@ -331,13 +331,13 @@
|
||||
[rn/view
|
||||
{:style {:height height}}
|
||||
[list/flat-list
|
||||
{:keyboardShouldPersistTaps :always
|
||||
:footer [rn/view {:style {:height 8}}]
|
||||
:header [rn/view {:style {:height 8}}]
|
||||
:data suggestions
|
||||
:key-fn first
|
||||
:render-data text-input-ref
|
||||
:render-fn mention-item}]]]))))
|
||||
{:keyboard-should-persist-taps :always
|
||||
:footer [rn/view {:style {:height 8}}]
|
||||
:header [rn/view {:style {:height 8}}]
|
||||
:data suggestions
|
||||
:key-fn first
|
||||
:render-data text-input-ref
|
||||
:render-fn mention-item}]]]))))
|
||||
|
||||
(defn on-chat-toolbar-layout
|
||||
[^js ev]
|
||||
|
||||
@@ -65,9 +65,9 @@
|
||||
[]
|
||||
(letsubs [blocked-contacts [:contacts/blocked]]
|
||||
[list.views/flat-list
|
||||
{:data blocked-contacts
|
||||
:key-fn :address
|
||||
:render-fn contacts-list-item
|
||||
:default-separator? true
|
||||
:enableEmptySections true
|
||||
:keyboardShouldPersistTaps :always}]))
|
||||
{:data blocked-contacts
|
||||
:key-fn :address
|
||||
:render-fn contacts-list-item
|
||||
:default-separator? true
|
||||
:enableEmptySections true
|
||||
:keyboard-should-persist-taps :always}]))
|
||||
|
||||
@@ -44,10 +44,10 @@
|
||||
:on-change (fn [text]
|
||||
(re-frame/dispatch [:search/currency-filter-changed text]))}]]
|
||||
[list/flat-list
|
||||
{:data (->> currencies
|
||||
vals
|
||||
(sort #(compare (:code %1) (:code %2))))
|
||||
:key-fn :code
|
||||
:render-data currency-id
|
||||
:render-fn render-currency
|
||||
:keyboardShouldPersistTaps :always}]]]))
|
||||
{:data (->> currencies
|
||||
vals
|
||||
(sort #(compare (:code %1) (:code %2))))
|
||||
:key-fn :code
|
||||
:render-data currency-id
|
||||
:render-fn render-currency
|
||||
:keyboard-should-persist-taps :always}]]]))
|
||||
|
||||
@@ -219,11 +219,11 @@
|
||||
[:ens/search-screen]]
|
||||
[react/keyboard-avoiding-view {:flex 1}
|
||||
[react/scroll-view
|
||||
{:style {:flex 1}
|
||||
{:style {:flex 1}
|
||||
;;NOTE required so that switching custom-domain
|
||||
;;works on first tap and persists keyboard
|
||||
;;instead of dismissing keyboard and requiring two taps
|
||||
:keyboardShouldPersistTaps :always}
|
||||
:keyboard-should-persist-taps :always}
|
||||
[react/view {:style {:flex 1}}
|
||||
[react/view
|
||||
{:style {:flex 1
|
||||
|
||||
@@ -76,11 +76,11 @@
|
||||
(defn toggle-list
|
||||
[{:keys [contacts render-fn render-data]}]
|
||||
[list/flat-list
|
||||
{:data contacts
|
||||
:key-fn :public-key
|
||||
:render-data render-data
|
||||
:render-fn render-fn
|
||||
:keyboardShouldPersistTaps :always}])
|
||||
{:data contacts
|
||||
:key-fn :public-key
|
||||
:render-data render-data
|
||||
:render-fn render-fn
|
||||
:keyboard-should-persist-taps :always}])
|
||||
|
||||
(defn no-contacts
|
||||
[{:keys [no-contacts]}]
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
banner-hidden [:keycard/banner-hidden]]
|
||||
[react/keyboard-avoiding-view {:style ast/multiaccounts-view}
|
||||
[react/scroll-view
|
||||
{:keyboardShouldPersistTaps :always
|
||||
:style styles/login-view}
|
||||
{:keyboard-should-persist-taps :always
|
||||
:style styles/login-view}
|
||||
[react/view styles/login-badge-container
|
||||
[multiaccount-login-badge multiaccount]
|
||||
[react/view {:style styles/password-container}
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.screens.profile.components.sheets :as sheets]
|
||||
[utils.re-frame :as rf]))
|
||||
[utils.re-frame :as rf]
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(defn actions
|
||||
[{:keys [public-key added? blocked? ens-name mutual?] :as contact} muted?]
|
||||
@@ -43,7 +44,9 @@
|
||||
:selected muted?
|
||||
:disabled blocked?
|
||||
:action (when-not blocked?
|
||||
#(re-frame/dispatch [:chat.ui/mute public-key (not muted?)]))}]
|
||||
#(re-frame/dispatch [:chat.ui/mute public-key (not muted?)
|
||||
(when-not muted?
|
||||
constants/mute-till-unmuted)]))}]
|
||||
[{:label (i18n/label (if blocked? :t/unblock :t/block))
|
||||
:negative true
|
||||
:selected blocked?
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
[status-im.ui.components.copyable-text :as copyable-text]
|
||||
[status-im.ui.components.list-selection :as list-selection]
|
||||
[status-im.ui.components.profile-header.view :as profile-header]
|
||||
[status-im.ui.components.qr-code-viewer.views :as qr-code-viewer]
|
||||
[status-im2.common.qr-code-viewer.view :as qr-code-viewer]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.profile.user.edit-picture :as edit]
|
||||
[status-im.ui.screens.profile.user.styles :as styles]
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[status-im.ethereum.eip681 :as eip681]
|
||||
[utils.i18n :as i18n]
|
||||
[status-im.ui.components.copyable-text :as copyable-text]
|
||||
[status-im.ui.components.qr-code-viewer.views :as qr-code-viewer]
|
||||
[status-im2.common.qr-code-viewer.view :as qr-code-viewer]
|
||||
[status-im.ui.components.react :as react])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
(defn mute-chat-action
|
||||
[chat-id]
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true]))
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true constants/mute-till-unmuted]))
|
||||
|
||||
(defn unmute-chat-action
|
||||
[chat-id]
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
(ns status-im2.common.mute-chat-drawer.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn header-text
|
||||
[]
|
||||
{:margin-left 20
|
||||
:margin-bottom 10
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)})
|
||||
@@ -0,0 +1,65 @@
|
||||
(ns status-im2.common.mute-chat-drawer.view
|
||||
(:require [utils.i18n :as i18n]
|
||||
[quo2.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im2.common.mute-chat-drawer.style :as style]))
|
||||
|
||||
(defn hide-sheet-and-dispatch
|
||||
[event]
|
||||
(rf/dispatch [:bottom-sheet/hide])
|
||||
(rf/dispatch event))
|
||||
|
||||
(defn mute-chat-drawer
|
||||
[chat-id accessibility-label]
|
||||
[rn/view {:accessibility-label accessibility-label}
|
||||
[quo/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (style/header-text)} (i18n/label :t/mute-channel)]
|
||||
[quo/menu-item
|
||||
{:type :transparent
|
||||
:title (i18n/label :t/mute-for-15-mins)
|
||||
:icon-bg-color :transparent
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true
|
||||
constants/mute-for-15-mins-type]))}]
|
||||
[quo/menu-item
|
||||
{:type :transparent
|
||||
:title (i18n/label :t/mute-for-1-hour)
|
||||
:icon-bg-color :transparent
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true
|
||||
constants/mute-for-1-hour-type]))}]
|
||||
[quo/menu-item
|
||||
{:type :transparent
|
||||
:title (i18n/label :t/mute-for-8-hours)
|
||||
:icon-bg-color :transparent
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true
|
||||
constants/mute-for-8-hours-type]))}]
|
||||
[quo/menu-item
|
||||
{:type :transparent
|
||||
:title (i18n/label :t/mute-for-1-week)
|
||||
:icon-bg-color :transparent
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true
|
||||
constants/mute-for-1-week]))}]
|
||||
[quo/menu-item
|
||||
{:type :transparent
|
||||
:title (i18n/label :t/mute-till-unmute)
|
||||
:icon-bg-color :transparent
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true
|
||||
constants/mute-till-unmuted]))}]])
|
||||
@@ -1,4 +1,4 @@
|
||||
(ns status-im.ui.components.qr-code-viewer.styles
|
||||
(ns status-im2.common.qr-code-viewer.style
|
||||
(:require [quo.design-system.colors :as colors]))
|
||||
|
||||
(def qr-code-padding 16)
|
||||
@@ -14,4 +14,4 @@
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:border-width 1
|
||||
:border-radius 8})
|
||||
:border-radius 8})
|
||||
@@ -0,0 +1,31 @@
|
||||
(ns status-im2.common.qr-code-viewer.view
|
||||
(:require ["qrcode" :as qr-code-js]
|
||||
[cljs-bean.core :as bean]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.qr-code-viewer.style :as style]
|
||||
[react-native.core :as rn]
|
||||
[react-native.svg :as svg]))
|
||||
|
||||
(defn qr-code
|
||||
[{:keys [size value]}]
|
||||
(let [uri (reagent/atom nil)]
|
||||
(.toString
|
||||
qr-code-js
|
||||
value
|
||||
(bean/->js {:margin 0 :width size})
|
||||
#(reset! uri %2))
|
||||
(fn []
|
||||
(when @uri
|
||||
[svg/svgxml {:xml @uri :width size :height size}]))))
|
||||
|
||||
(defn qr-code-view
|
||||
"Qr Code view including the frame.
|
||||
Note: `size` includes frame with `style/qr-code-padding.`"
|
||||
[size value]
|
||||
(when (and size value)
|
||||
[rn/view
|
||||
{:style (style/qr-code-container size)
|
||||
:accessibility-label :qr-code-image}
|
||||
[qr-code
|
||||
{:value value
|
||||
:size (- size (* style/qr-code-padding 2))}]]))
|
||||
@@ -5,11 +5,12 @@
|
||||
:lifestyle (js/require "../resources/images/ui2/lifestyle.png")
|
||||
:music (js/require "../resources/images/ui2/music.png")
|
||||
:podcasts (js/require "../resources/images/ui2/podcasts.png")
|
||||
:sync-device (js/require "../resources/images/ui2/sync-new-device-cover-background.png")
|
||||
:generate-keys (js/require "../resources/images/ui2/generate_keys.png")
|
||||
:ethereum-address (js/require "../resources/images/ui2/ethereum_address.png")
|
||||
:generate-keys (js/require "../resources/images/ui2/generate-keys.png")
|
||||
:ethereum-address (js/require "../resources/images/ui2/ethereum-address.png")
|
||||
:use-keycard (js/require "../resources/images/ui2/keycard.png")
|
||||
:onboarding-illustration (js/require "../resources/images/ui2/onboarding_illustration.png")})
|
||||
:onboarding-illustration (js/require "../resources/images/ui2/onboarding_illustration.png")
|
||||
:qr-code (js/require "../resources/images/ui2/qr-code.png")
|
||||
})
|
||||
|
||||
(def mock-images
|
||||
{:coinbase (js/require "../resources/images/mock2/coinbase.png")
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
|
||||
(defn toast
|
||||
[id]
|
||||
(let [toast-opts (rf/sub [:toasts/toast id])]
|
||||
[quo/toast toast-opts]))
|
||||
(let [{:keys [type] :as toast-opts} (rf/sub [:toasts/toast id])]
|
||||
(if (= type :notification)
|
||||
[quo/notification toast-opts]
|
||||
[quo/toast toast-opts])))
|
||||
|
||||
(defn container
|
||||
[id]
|
||||
@@ -41,7 +43,7 @@
|
||||
[:f>
|
||||
(fn []
|
||||
(let [duration (or (rf/sub [:toasts/toast-cursor id :duration]) 3000)
|
||||
on-dismissed #((or (rf/sub [:toasts/toast-cursor id :on-dismissed]) identity) id)
|
||||
on-dismissed (or (rf/sub [:toasts/toast-cursor id :on-dismissed]) identity)
|
||||
create-timer (fn []
|
||||
(reset! timer (utils.utils/set-timeout close! duration)))
|
||||
translate-y (reanimated/use-shared-value 0)
|
||||
@@ -78,10 +80,10 @@
|
||||
(create-timer)))))]
|
||||
;; create auto dismiss timer, clear timer when unmount or duration changed
|
||||
(rn/use-effect (fn [] (create-timer) clear-timer) [duration])
|
||||
(rn/use-unmount on-dismissed)
|
||||
(rn/use-unmount #(on-dismissed id))
|
||||
[gesture/gesture-detector {:gesture pan}
|
||||
[reanimated/view
|
||||
{;; TODO: this will eanble layout animation at runtime and causing flicker on android
|
||||
{;; TODO: this will enable layout animation at runtime and causing flicker on android
|
||||
;; we need to resolve this and re-enable layout animation
|
||||
;; issue at https://github.com/status-im/status-mobile/issues/14752
|
||||
;; :entering slide-in-up-animation
|
||||
@@ -98,4 +100,5 @@
|
||||
[into
|
||||
[rn/view
|
||||
{:style style/outmost-transparent-container}]
|
||||
(map (fn [id] ^{:key id} [container id]) toasts-ordered)]))
|
||||
(doall
|
||||
(map (fn [id] ^{:key id} [container id]) toasts-ordered))]))
|
||||
|
||||
@@ -36,6 +36,12 @@
|
||||
(def ^:const activity-center-membership-status-accepted 2)
|
||||
(def ^:const activity-center-membership-status-declined 3)
|
||||
|
||||
(def ^:const mute-for-15-mins-type 1)
|
||||
(def ^:const mute-for-1-hour-type 2)
|
||||
(def ^:const mute-for-8-hours-type 3)
|
||||
(def ^:const mute-for-1-week 4)
|
||||
(def ^:const mute-till-unmuted 5)
|
||||
|
||||
(def ^:const activity-center-mark-all-as-read-undo-time-limit-ms 4000)
|
||||
(def ^:const activity-center-max-unread-count 99)
|
||||
|
||||
|
||||
@@ -532,29 +532,33 @@
|
||||
[{:keys [db]} new-notifications]
|
||||
(let [my-public-key (get-in db [:multiaccount :public-key])]
|
||||
(reduce (fn [cofx {:keys [author type accepted dismissed message name] :as x}]
|
||||
(cond
|
||||
(and (not= author my-public-key)
|
||||
(= type types/contact-request)
|
||||
(not accepted)
|
||||
(not dismissed))
|
||||
(toasts/upsert cofx
|
||||
{:icon :placeholder
|
||||
:icon-color colors/primary-50-opa-40
|
||||
:title (i18n/label :t/contact-request-sent-toast
|
||||
{:name name})
|
||||
:text (get-in message [:content :text])})
|
||||
(let [user-avatar {:full-name name
|
||||
:status-indicator? true
|
||||
:online? nil
|
||||
:size :small
|
||||
:ring? false}]
|
||||
(cond
|
||||
(and (not= author my-public-key)
|
||||
(= type types/contact-request)
|
||||
(not accepted)
|
||||
(not dismissed))
|
||||
(toasts/upsert cofx
|
||||
{:user user-avatar
|
||||
:icon-color colors/primary-50-opa-40
|
||||
:title (i18n/label :t/contact-request-sent-toast
|
||||
{:name name})
|
||||
:text (get-in message [:content :text])})
|
||||
|
||||
(and (= author my-public-key) ;; we show it for user who sent the request
|
||||
(= type types/contact-request)
|
||||
accepted
|
||||
(not dismissed))
|
||||
(toasts/upsert cofx
|
||||
{:icon :placeholder
|
||||
:icon-color colors/primary-50-opa-40
|
||||
:title (i18n/label :t/contact-request-accepted-toast
|
||||
{:name (or name (:alias message))})})
|
||||
|
||||
:else
|
||||
cofx))
|
||||
(and (= author my-public-key) ;; we show it for user who sent the request
|
||||
(= type types/contact-request)
|
||||
accepted
|
||||
(not dismissed))
|
||||
(toasts/upsert cofx
|
||||
{:user user-avatar
|
||||
:icon-color colors/success-50-opa-40
|
||||
:title (i18n/label :t/contact-request-accepted-toast
|
||||
{:name (or name (:alias message))})})
|
||||
:else
|
||||
cofx)))
|
||||
{:db db}
|
||||
new-notifications)))
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def tag-params
|
||||
{:size :small
|
||||
:override-theme :dark
|
||||
:color colors/primary-50
|
||||
:style style/user-avatar-tag
|
||||
:text-style style/user-avatar-tag-text})
|
||||
|
||||
(defn user-avatar-tag
|
||||
[user-id]
|
||||
(let [contact (rf/sub [:contacts/contact-by-identity user-id])]
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
(ns status-im2.contexts.activity-center.notification.community-request.view
|
||||
(:require [quo2.core :as quo]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.activity-center.notification.common.style :as common-style]
|
||||
[status-im2.contexts.activity-center.notification.common.view :as common]
|
||||
[utils.datetime :as datetime]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- swipeable
|
||||
[{:keys [active-swipeable extra-fn]} child]
|
||||
[common/swipeable
|
||||
{:left-button common/swipe-button-read-or-unread
|
||||
:left-on-press common/swipe-on-press-toggle-read
|
||||
:right-button common/swipe-button-delete
|
||||
:right-on-press common/swipe-on-press-delete
|
||||
:active-swipeable active-swipeable
|
||||
:extra-fn extra-fn}
|
||||
child])
|
||||
|
||||
(defn- get-header-text-and-context
|
||||
[community membership-status]
|
||||
(let [community-name (:name community)
|
||||
community-image (get-in community [:images :thumbnail :uri])
|
||||
community-context-tag [quo/context-tag common/tag-params {:uri community-image}
|
||||
community-name]]
|
||||
(cond
|
||||
(= membership-status constants/activity-center-membership-status-idle)
|
||||
{:header-text (i18n/label :t/community-request-not-accepted)
|
||||
:context [[quo/text {:style common-style/user-avatar-tag-text}
|
||||
(i18n/label :t/community-request-not-accepted-body-text-prefix)]
|
||||
community-context-tag
|
||||
[quo/text {:style common-style/user-avatar-tag-text}
|
||||
(i18n/label :t/community-request-not-accepted-body-text-suffix)]]}
|
||||
|
||||
(= membership-status constants/activity-center-membership-status-pending)
|
||||
{:header-text (i18n/label :t/community-request-pending)
|
||||
:context [[quo/text {:style common-style/user-avatar-tag-text}
|
||||
(i18n/label :t/community-request-pending-body-text)]
|
||||
community-context-tag]}
|
||||
|
||||
(= membership-status constants/activity-center-membership-status-accepted)
|
||||
{:header-text (i18n/label :t/community-request-accepted)
|
||||
:context [[quo/text {:style common-style/user-avatar-tag-text}
|
||||
(i18n/label :t/community-request-accepted-body-text)]
|
||||
community-context-tag]}
|
||||
|
||||
:else nil)))
|
||||
|
||||
(defn view
|
||||
[{:keys [notification set-swipeable-height] :as props}]
|
||||
(let [{:keys [community-id membership-status read
|
||||
timestamp]} notification
|
||||
community (rf/sub [:communities/community community-id])
|
||||
{:keys [header-text context]} (get-header-text-and-context community
|
||||
membership-status)]
|
||||
[swipeable props
|
||||
[quo/activity-log
|
||||
{:title header-text
|
||||
:icon :i/communities
|
||||
:on-layout set-swipeable-height
|
||||
:timestamp (datetime/timestamp->relative timestamp)
|
||||
:unread? (not read)
|
||||
:context context}]]))
|
||||
@@ -6,6 +6,7 @@
|
||||
(def ^:const mention 3)
|
||||
(def ^:const reply 4)
|
||||
(def ^:const contact-request 5)
|
||||
(def ^:const community-request 7)
|
||||
(def ^:const admin 8)
|
||||
(def ^:const contact-verification 10)
|
||||
|
||||
@@ -15,6 +16,7 @@
|
||||
mention
|
||||
reply
|
||||
contact-request
|
||||
community-request
|
||||
admin
|
||||
contact-verification})
|
||||
|
||||
@@ -26,4 +28,5 @@
|
||||
"Membership is like a logical group of notifications with different types, i.e.
|
||||
it doesn't have a corresponding type in the backend. Think of the collection
|
||||
as a composite key of actual types."
|
||||
#{private-group-chat})
|
||||
#{private-group-chat
|
||||
community-request})
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
[status-im2.contexts.activity-center.notification.membership.view :as membership]
|
||||
[status-im2.contexts.activity-center.notification.mentions.view :as mentions]
|
||||
[status-im2.contexts.activity-center.notification.reply.view :as reply]
|
||||
[status-im2.contexts.activity-center.notification.community-request.view :as
|
||||
community-request]
|
||||
[status-im2.contexts.activity-center.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
@@ -196,7 +198,12 @@
|
||||
[admin/view props]
|
||||
|
||||
(some types/membership [type])
|
||||
[membership/view props]
|
||||
(case type
|
||||
types/private-group-chat [membership/view props]
|
||||
|
||||
types/community-request [community-request/view props]
|
||||
|
||||
nil)
|
||||
|
||||
:else
|
||||
nil)]))))
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
[react-native.platform :as platform]
|
||||
[quo2.foundations.typography :as typography]))
|
||||
|
||||
(defn container-kbd
|
||||
[]
|
||||
{:style {:flex 1}
|
||||
:keyboardVerticalOffset 60})
|
||||
|
||||
(def container-image
|
||||
{:style {:flex 1
|
||||
:flex-direction :row}})
|
||||
|
||||
(def image
|
||||
{:flex 1})
|
||||
|
||||
(defn container-outer
|
||||
[]
|
||||
{:style {:flex (if platform/ios? 4.5 5)
|
||||
@@ -41,7 +29,8 @@
|
||||
[]
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:margin-bottom 6
|
||||
:style {:margin-top 32
|
||||
:margin-bottom 6
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-100
|
||||
colors/white)}})
|
||||
@@ -127,11 +116,8 @@
|
||||
:accessibility-label :new-contact-close-button
|
||||
:size 32
|
||||
:override-background-color (colors/theme-colors
|
||||
colors/white-opa-60
|
||||
colors/neutral-80-opa-60)
|
||||
:style {:position :absolute
|
||||
:left 20
|
||||
:top 20}})
|
||||
colors/neutral-10
|
||||
colors/neutral-90)})
|
||||
|
||||
(def button-qr
|
||||
{:type :outline
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
[react-native.core :as rn]
|
||||
[react-native.clipboard :as clipboard]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.resources :as resources]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im2.contexts.add-new-contact.style :as style]
|
||||
@@ -55,11 +54,9 @@
|
||||
show-paste-button? (and (not (string/blank? @clipboard))
|
||||
(string/blank? @default-value)
|
||||
(string/blank? input))]
|
||||
[rn/keyboard-avoiding-view (style/container-kbd)
|
||||
[rn/view style/container-image
|
||||
[rn/image
|
||||
{:source (resources/get-image :add-new-contact)
|
||||
:style style/image}]
|
||||
|
||||
[rn/view (style/container-outer)
|
||||
[rn/view style/container-inner
|
||||
[quo/button
|
||||
(merge (style/button-close)
|
||||
{:on-press
|
||||
@@ -67,56 +64,54 @@
|
||||
(reset! clipboard nil)
|
||||
(reset! default-value nil)
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back]))}) :i/close]]
|
||||
[rn/view (style/container-outer)
|
||||
[rn/view style/container-inner
|
||||
[quo/text (style/text-title)
|
||||
(i18n/label :t/add-a-contact)]
|
||||
[quo/text (style/text-subtitle)
|
||||
(i18n/label :t/find-your-friends)]
|
||||
[quo/text (style/text-description)
|
||||
(i18n/label :t/ens-or-chat-key)]
|
||||
[rn/view style/container-text-input
|
||||
[rn/view (style/text-input-container invalid?)
|
||||
[rn/text-input
|
||||
(merge (style/text-input)
|
||||
{:default-value (or scanned @default-value input)
|
||||
:placeholder (i18n/label :t/type-some-chat-key)
|
||||
:on-change-text (fn [v]
|
||||
(reset! default-value v)
|
||||
(debounce/debounce-and-dispatch
|
||||
[:contacts/set-new-identity v nil]
|
||||
600))})]
|
||||
(when show-paste-button?
|
||||
[quo/button
|
||||
(merge style/button-paste
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! default-value @clipboard)
|
||||
(rf/dispatch
|
||||
[:contacts/set-new-identity @clipboard nil]))})
|
||||
(i18n/label :t/paste)])]
|
||||
[quo/button
|
||||
(merge style/button-qr
|
||||
{:on-press #(rf/dispatch
|
||||
[::qr-scanner/scan-code
|
||||
{:handler :contacts/qr-code-scanned}])})
|
||||
:i/scan]]
|
||||
(when invalid?
|
||||
[rn/view style/container-invalid
|
||||
[quo/icon :i/alert style/icon-invalid]
|
||||
[quo/text style/text-invalid
|
||||
(i18n/label (or msg :t/invalid-ens-or-key))]])
|
||||
(when (= state :valid)
|
||||
[found-contact public-key])]
|
||||
[rn/view
|
||||
(rf/dispatch [:navigate-back]))}) :i/close]
|
||||
[quo/text (style/text-title)
|
||||
(i18n/label :t/add-a-contact)]
|
||||
[quo/text (style/text-subtitle)
|
||||
(i18n/label :t/find-your-friends)]
|
||||
[quo/text (style/text-description)
|
||||
(i18n/label :t/ens-or-chat-key)]
|
||||
[rn/view style/container-text-input
|
||||
[rn/view (style/text-input-container invalid?)
|
||||
[rn/text-input
|
||||
(merge (style/text-input)
|
||||
{:default-value (or scanned @default-value input)
|
||||
:placeholder (i18n/label :t/type-some-chat-key)
|
||||
:on-change-text (fn [v]
|
||||
(reset! default-value v)
|
||||
(debounce/debounce-and-dispatch
|
||||
[:contacts/set-new-identity v nil]
|
||||
600))})]
|
||||
(when show-paste-button?
|
||||
[quo/button
|
||||
(merge style/button-paste
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! default-value @clipboard)
|
||||
(rf/dispatch
|
||||
[:contacts/set-new-identity @clipboard nil]))})
|
||||
(i18n/label :t/paste)])]
|
||||
[quo/button
|
||||
(merge (style/button-view-profile state)
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! clipboard nil)
|
||||
(reset! default-value nil)
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:chat.ui/show-profile public-key ens]))})
|
||||
(i18n/label :t/view-profile)]]]]))))
|
||||
(merge style/button-qr
|
||||
{:on-press #(rf/dispatch
|
||||
[::qr-scanner/scan-code
|
||||
{:handler :contacts/qr-code-scanned}])})
|
||||
:i/scan]]
|
||||
(when invalid?
|
||||
[rn/view style/container-invalid
|
||||
[quo/icon :i/alert style/icon-invalid]
|
||||
[quo/text style/text-invalid
|
||||
(i18n/label (or msg :t/invalid-ens-or-key))]])
|
||||
(when (= state :valid)
|
||||
[found-contact public-key])]
|
||||
[rn/view
|
||||
[quo/button
|
||||
(merge (style/button-view-profile state)
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! clipboard nil)
|
||||
(reset! default-value nil)
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:chat.ui/show-profile public-key ens]))})
|
||||
(i18n/label :t/view-profile)]]]))))
|
||||
|
||||
@@ -64,13 +64,15 @@
|
||||
(defn map-chats
|
||||
[{:keys [db] :as cofx}]
|
||||
(fn [val]
|
||||
(assoc
|
||||
(merge
|
||||
(or (get (:chats db) (:chat-id val))
|
||||
(create-new-chat (:chat-id val) cofx))
|
||||
val)
|
||||
:invitation-admin
|
||||
(:invitation-admin val))))
|
||||
(let [chat (or (get (:chats db) (:chat-id val))
|
||||
(create-new-chat (:chat-id val) cofx))]
|
||||
(assoc
|
||||
(merge
|
||||
(cond-> chat
|
||||
(comp not :muted) (dissoc chat :muted-till))
|
||||
val)
|
||||
:invitation-admin
|
||||
(:invitation-admin val)))))
|
||||
|
||||
(rf/defn leave-removed-chat
|
||||
[{{:keys [view-id current-chat-id chats]} :db
|
||||
@@ -292,18 +294,20 @@
|
||||
|
||||
(rf/defn mute-chat-toggled-successfully
|
||||
{:events [:chat/mute-successfully]}
|
||||
[_ chat-id]
|
||||
(log/debug "muted chat successfully" chat-id))
|
||||
[{:keys [db]} chat-id muted-till]
|
||||
(log/debug "muted chat successfully" chat-id " for" muted-till)
|
||||
{:db (assoc-in db [:chats chat-id :muted-till] muted-till)})
|
||||
|
||||
(rf/defn mute-chat
|
||||
{:events [:chat.ui/mute]}
|
||||
[{:keys [db]} chat-id muted?]
|
||||
(let [method (if muted? "wakuext_muteChat" "wakuext_unmuteChat")]
|
||||
[{:keys [db]} chat-id muted? mute-type]
|
||||
(let [method (if muted? "wakuext_muteChatV2" "wakuext_unmuteChat")
|
||||
params (if muted? [{:chatId chat-id :mutedType mute-type}] [chat-id])]
|
||||
{:db (assoc-in db [:chats chat-id :muted] muted?)
|
||||
:json-rpc/call [{:method method
|
||||
:params [chat-id]
|
||||
:params params
|
||||
:on-error #(rf/dispatch [:chat/mute-failed chat-id muted? %])
|
||||
:on-success #(rf/dispatch [:chat/mute-successfully chat-id])}]}))
|
||||
:on-success #(rf/dispatch [:chat/mute-successfully chat-id %])}]}))
|
||||
|
||||
(rf/defn show-clear-history-confirmation
|
||||
{:events [:chat.ui/show-clear-history-confirmation]}
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
[status-im2.common.contact-list-item.view :as contact-list-item]
|
||||
[status-im2.common.home.actions.view :as actions]
|
||||
[utils.re-frame :as rf]
|
||||
[reagent.core :as reagent]))
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(defn back-button
|
||||
[]
|
||||
@@ -191,7 +192,8 @@
|
||||
[rn/touchable-opacity
|
||||
{:style (style/action-container color)
|
||||
:accessibility-label :toggle-mute
|
||||
:on-press #(rf/dispatch [:chat.ui/mute chat-id (not muted)])}
|
||||
:on-press #(rf/dispatch [:chat.ui/mute chat-id (not muted)
|
||||
(when-not muted constants/mute-till-unmuted)])}
|
||||
[quo/icon (if muted :i/muted :i/activity-center)
|
||||
{:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}]
|
||||
[quo/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium}
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
[chat-id]
|
||||
(fn []
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch [:chat/navigate-to-chat chat-id])
|
||||
(rf/dispatch [:search/home-filter-changed nil])))
|
||||
(rf/dispatch [:chat/navigate-to-chat chat-id])))
|
||||
|
||||
(defn truncate-literal
|
||||
[literal]
|
||||
@@ -118,7 +117,7 @@
|
||||
|
||||
(defn chat-list-item
|
||||
[{:keys [chat-id group-chat color name unviewed-messages-count unviewed-mentions-count
|
||||
timestamp last-message]
|
||||
timestamp last-message muted]
|
||||
:as item}]
|
||||
(let [display-name (if group-chat
|
||||
name
|
||||
@@ -143,8 +142,9 @@
|
||||
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
|
||||
(get-in last-message [:content :text])]
|
||||
[render-subheader (get-in last-message [:content :parsed-text])])]
|
||||
(if (> unviewed-mentions-count 0)
|
||||
[quo/info-count {:style {:top 16}}
|
||||
unviewed-mentions-count]
|
||||
(when (> unviewed-messages-count 0)
|
||||
[rn/view {:style (style/count-container)}]))]))
|
||||
(when-not muted
|
||||
(if (> unviewed-mentions-count 0)
|
||||
[quo/info-count {:style {:top 16}}
|
||||
unviewed-mentions-count]
|
||||
(when (> unviewed-messages-count 0)
|
||||
[rn/view {:style (style/count-container)}])))]))
|
||||
|
||||
@@ -45,8 +45,12 @@
|
||||
[rn/view {:style (style/contact-requests-icon)}
|
||||
[quo/icon :i/pending-user {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]]
|
||||
[rn/view {:style {:margin-left 8}}
|
||||
[rn/text {:weight :semi-bold} (i18n/label :t/pending-requests)]
|
||||
[rn/text
|
||||
[quo/text
|
||||
{:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:color (colors/theme-colors colors/neutral-100 colors/white)}}
|
||||
(i18n/label :t/pending-requests)]
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
|
||||
(requests-summary requests)]]
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
(ns status-im2.contexts.chat.home.events
|
||||
(:require
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/defn messages-home-select-tab-event
|
||||
{:events [:messages-home/select-tab]}
|
||||
[{:keys [db]} tab]
|
||||
{:db (assoc db :messages-home/selected-tab tab)})
|
||||
@@ -3,7 +3,6 @@
|
||||
[quo2.core :as quo]
|
||||
[re-frame.core :as re-frame]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.contact-list.view :as contact-list]
|
||||
[status-im2.common.home.view :as common.home]
|
||||
[status-im2.contexts.chat.home.chat-list-item.view :as chat-list-item]
|
||||
@@ -24,7 +23,7 @@
|
||||
|
||||
(defn filter-items-by-tab
|
||||
[tab items]
|
||||
(if (= tab :groups)
|
||||
(if (= tab :tab/groups)
|
||||
(filter :group-chat items)
|
||||
(filter :chat-id items)))
|
||||
|
||||
@@ -37,10 +36,9 @@
|
||||
|
||||
(defn chats
|
||||
[selected-tab top]
|
||||
(let [{:keys [items search-filter]} (rf/sub [:home-items])
|
||||
items (filter-items-by-tab selected-tab items)]
|
||||
(if (and (empty? items)
|
||||
(empty? search-filter))
|
||||
(let [unfiltered-items (rf/sub [:chats-stack-items])
|
||||
items (filter-items-by-tab selected-tab unfiltered-items)]
|
||||
(if (empty? items)
|
||||
[welcome-blank-chats]
|
||||
[rn/flat-list
|
||||
{:key-fn #(or (:chat-id %) (:public-key %) (:id %))
|
||||
@@ -93,42 +91,43 @@
|
||||
|
||||
(defn get-tabs-data
|
||||
[dot?]
|
||||
[{:id :recent :label (i18n/label :t/recent) :accessibility-label :tab-recent}
|
||||
{:id :groups :label (i18n/label :t/groups) :accessibility-label :tab-groups}
|
||||
{:id :contacts
|
||||
[{:id :tab/recent :label (i18n/label :t/recent) :accessibility-label :tab-recent}
|
||||
{:id :tab/groups :label (i18n/label :t/groups) :accessibility-label :tab-groups}
|
||||
{:id :tab/contacts
|
||||
:label (i18n/label :t/contacts)
|
||||
:accessibility-label :tab-contacts
|
||||
:notification-dot? dot?}])
|
||||
|
||||
(defn home
|
||||
[]
|
||||
(let [selected-tab (reagent/atom :recent)]
|
||||
(fn []
|
||||
(let [pending-contact-requests (rf/sub [:activity-center/pending-contact-requests])]
|
||||
[safe-area/consumer
|
||||
(fn [{:keys [top]}]
|
||||
[:<>
|
||||
(if (= @selected-tab :contacts)
|
||||
[contacts pending-contact-requests top]
|
||||
[chats @selected-tab top])
|
||||
[rn/view
|
||||
{:style (style/blur-container top)}
|
||||
[blur/view
|
||||
{:blur-amount (if platform/ios? 20 10)
|
||||
:blur-type (if (colors/dark?) :dark (if platform/ios? :light :xlight))
|
||||
:style style/blur}]
|
||||
[common.home/top-nav]
|
||||
[common.home/title-column
|
||||
{:label (i18n/label :t/messages)
|
||||
:handler #(rf/dispatch [:show-bottom-sheet
|
||||
{:content home.sheet/new-chat-bottom-sheet}])
|
||||
:accessibility-label :new-chat-button}]
|
||||
[quo/discover-card
|
||||
{:title (i18n/label :t/invite-friends-to-status)
|
||||
:description (i18n/label :t/share-invite-link)}]
|
||||
[quo/tabs
|
||||
{:style style/tabs
|
||||
:size 32
|
||||
:on-change #(reset! selected-tab %)
|
||||
:default-active @selected-tab
|
||||
:data (get-tabs-data (pos? (count pending-contact-requests)))}]]])]))))
|
||||
(fn []
|
||||
(let [pending-contact-requests (rf/sub [:activity-center/pending-contact-requests])
|
||||
selected-tab (or (rf/sub [:messages-home/selected-tab]) :tab/recent)]
|
||||
[safe-area/consumer
|
||||
(fn [{:keys [top]}]
|
||||
[:<>
|
||||
(if (= selected-tab :tab/contacts)
|
||||
[contacts pending-contact-requests top]
|
||||
[chats selected-tab top])
|
||||
[rn/view
|
||||
{:style (style/blur-container top)}
|
||||
[blur/view
|
||||
{:blur-amount (if platform/ios? 20 10)
|
||||
:blur-type (if (colors/dark?) :dark (if platform/ios? :light :xlight))
|
||||
:style style/blur}]
|
||||
[common.home/top-nav]
|
||||
[common.home/title-column
|
||||
{:label (i18n/label :t/messages)
|
||||
:handler #(rf/dispatch [:show-bottom-sheet
|
||||
{:content home.sheet/new-chat-bottom-sheet}])
|
||||
:accessibility-label :new-chat-button}]
|
||||
[quo/discover-card
|
||||
{:title (i18n/label :t/invite-friends-to-status)
|
||||
:description (i18n/label :t/share-invite-link)}]
|
||||
[quo/tabs
|
||||
{:style style/tabs
|
||||
:size 32
|
||||
:on-change (fn [tab]
|
||||
(rf/dispatch [:messages-home/select-tab tab]))
|
||||
:default-active selected-tab
|
||||
:data (get-tabs-data (pos? (count pending-contact-requests)))}]]])])))
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
:elevation 5
|
||||
:max-height (/ max-y 2)})}
|
||||
[rn/flat-list
|
||||
{:keyboardShouldPersistTaps :always
|
||||
:data (vals suggestions)
|
||||
:key-fn :key
|
||||
:render-fn mention-item
|
||||
:render-data (:text-input-ref refs)
|
||||
:accessibility-label :mentions-list}]]))])
|
||||
{:keyboard-should-persist-taps :always
|
||||
:data (vals suggestions)
|
||||
:key-fn :key
|
||||
:render-fn mention-item
|
||||
:render-data (:text-input-ref refs)
|
||||
:accessibility-label :mentions-list}]]))])
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
(i18n/label :t/deleted-this-message)]])
|
||||
|
||||
(defn- compute-on-long-press-fn
|
||||
[{:keys [deleted? pinned] :as message}
|
||||
[{:keys [deleted? pinned deleted-for-me?] :as message}
|
||||
{:keys [message-pin-enabled] :as context}]
|
||||
;; only show drawer for user who has the permission to unpin messages
|
||||
(when (and pinned deleted? message-pin-enabled)
|
||||
(when (and pinned (or deleted? deleted-for-me?) message-pin-enabled)
|
||||
(fn []
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
|
||||
@@ -89,45 +89,54 @@
|
||||
|
||||
(defn link-preview-loader
|
||||
[link _]
|
||||
(reagent/create-class
|
||||
{:component-did-mount
|
||||
(fn []
|
||||
(rf/dispatch [:chat.ui/load-link-preview-data link]))
|
||||
:component-did-update
|
||||
(fn [this [_ previous-props]]
|
||||
(let [[_ props] (.-argv (.-props ^js this))
|
||||
refresh-photo? (not= previous-props props)]
|
||||
(when refresh-photo?
|
||||
(rf/dispatch [:chat.ui/load-link-preview-data props]))))
|
||||
:reagent-render
|
||||
(fn [link {:keys [on-long-press]}]
|
||||
(let [cached-preview-data (rf/sub [:link-preview/cache link])]
|
||||
(when-let [{:keys [site title thumbnail-url error] :as preview-data} cached-preview-data]
|
||||
(when (and (not error) site title)
|
||||
[rn/touchable-opacity
|
||||
{:style (when-not (is-gif? thumbnail-url)
|
||||
{:align-self :stretch})
|
||||
:on-press #(when (security/safe-link? link)
|
||||
(rf/dispatch [:browser.ui/message-link-pressed link]))
|
||||
:on-long-press on-long-press}
|
||||
[rn/view (style/wrapper)
|
||||
(when-not (is-gif? thumbnail-url)
|
||||
[:<>
|
||||
[rn/view (style/title-wrapper)
|
||||
[rn/image {:style (style/title-site-image)}]
|
||||
[rn/text {:style (style/title-text)}
|
||||
site]]
|
||||
[rn/text {:style (style/main-text)}
|
||||
title]
|
||||
[rn/text {:style (style/extra-text)}
|
||||
link]])
|
||||
(when-not (string/blank? thumbnail-url)
|
||||
[:<>
|
||||
[rn/view (style/separator)]
|
||||
[fast-image/fast-image
|
||||
{:source {:uri thumbnail-url}
|
||||
:style (style/image (select-keys preview-data [:height :width]))
|
||||
:accessibility-label :member-photo}]])]]))))}))
|
||||
(let [measured-width (reagent/atom 0)
|
||||
measured-height (reagent/atom 0)]
|
||||
(reagent/create-class
|
||||
{:component-did-mount
|
||||
(fn []
|
||||
(rf/dispatch [:chat.ui/load-link-preview-data link]))
|
||||
:component-did-update
|
||||
(fn [this [_ previous-props]]
|
||||
(let [[_ props] (.-argv (.-props ^js this))
|
||||
refresh-photo? (not= previous-props props)]
|
||||
(when refresh-photo?
|
||||
(rf/dispatch [:chat.ui/load-link-preview-data props]))))
|
||||
:reagent-render
|
||||
(fn [link {:keys [on-long-press]}]
|
||||
(let [cached-preview-data (rf/sub [:link-preview/cache link])]
|
||||
(when-let [{:keys [site title thumbnail-url error]} cached-preview-data]
|
||||
(when (and (not error) site title)
|
||||
[rn/touchable-opacity
|
||||
{:style (when-not (is-gif? thumbnail-url)
|
||||
{:align-self :stretch})
|
||||
:on-press #(when (security/safe-link? link)
|
||||
(rf/dispatch [:browser.ui/message-link-pressed link]))
|
||||
:on-long-press on-long-press}
|
||||
[rn/view (style/wrapper)
|
||||
(when-not (is-gif? thumbnail-url)
|
||||
[:<>
|
||||
[rn/view (style/title-wrapper)
|
||||
[rn/image {:style (style/title-site-image)}]
|
||||
[rn/text {:style (style/title-text)}
|
||||
site]]
|
||||
[rn/text {:style (style/main-text)}
|
||||
title]
|
||||
[rn/text {:style (style/extra-text)}
|
||||
link]])
|
||||
(when-not (string/blank? thumbnail-url)
|
||||
[:<>
|
||||
[rn/view (style/separator)]
|
||||
[fast-image/fast-image
|
||||
{:source {:uri thumbnail-url}
|
||||
:on-load (fn [e]
|
||||
(let [{:keys [width height]} (js->clj (.-nativeEvent e)
|
||||
:keywordize-keys
|
||||
true)]
|
||||
(reset! measured-width width)
|
||||
(reset! measured-height height)))
|
||||
:style (style/image {:width @measured-width
|
||||
:height @measured-height})
|
||||
:accessibility-label :member-photo}]])]]))))})))
|
||||
|
||||
(defn link-preview-enable-request
|
||||
[]
|
||||
|
||||
@@ -186,8 +186,7 @@
|
||||
(when (and (not locked?) id)
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch [:chat/navigate-to-chat (str community-id id)])
|
||||
(rf/dispatch [:search/home-filter-changed nil]))}))))
|
||||
(rf/dispatch [:chat/navigate-to-chat (str community-id id)]))}))))
|
||||
|
||||
(defn add-on-press-handler-to-chats
|
||||
[community-id chats]
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
[dark-overlay?]
|
||||
[:f>
|
||||
(fn []
|
||||
(carousel.animation/initialize-animation)
|
||||
[rn/view
|
||||
{:style style/background-container}
|
||||
[carousel/view dark-overlay?]
|
||||
(when dark-overlay?
|
||||
[blur/view
|
||||
{:style style/background-blur-overlay
|
||||
:blur-amount 30
|
||||
:blur-radius 25
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent}])])])
|
||||
(let [animate? (not dark-overlay?)]
|
||||
(when animate? (carousel.animation/initialize-animation))
|
||||
[rn/view
|
||||
{:style style/background-container}
|
||||
[carousel/view animate?]
|
||||
(when dark-overlay?
|
||||
[blur/view
|
||||
{:style style/background-blur-overlay
|
||||
:blur-amount 30
|
||||
:blur-radius 25
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent}])]))])
|
||||
|
||||
@@ -35,16 +35,22 @@
|
||||
|
||||
(defn initialize-animation
|
||||
[]
|
||||
(when-not @progress
|
||||
(reset! progress (reanimated/use-shared-value 0))
|
||||
(reset! paused (reanimated/use-shared-value false))
|
||||
(animate-progress @progress @paused)))
|
||||
(reset! progress (reanimated/use-shared-value 0))
|
||||
(reset! paused (reanimated/use-shared-value false))
|
||||
(animate-progress @progress @paused))
|
||||
|
||||
;; Derived Values
|
||||
(defn carousel-left-position
|
||||
[window-width]
|
||||
(worklets.onboarding-carousel/carousel-left-position window-width @progress))
|
||||
[window-width animate?]
|
||||
(if animate?
|
||||
(worklets.onboarding-carousel/carousel-left-position window-width @progress)
|
||||
(-> (or (reanimated/get-shared-value @progress) 0)
|
||||
(quot -25)
|
||||
(* window-width))))
|
||||
|
||||
(defn dynamic-progress-bar-width
|
||||
[progress-bar-width]
|
||||
(worklets.onboarding-carousel/dynamic-progress-bar-width progress-bar-width @progress))
|
||||
[progress-bar-width animate?]
|
||||
(if animate?
|
||||
(worklets.onboarding-carousel/dynamic-progress-bar-width progress-bar-width @progress)
|
||||
(-> (or (reanimated/get-shared-value @progress) 0)
|
||||
(* progress-bar-width)
|
||||
(/ 100))))
|
||||
|
||||
@@ -48,12 +48,16 @@
|
||||
:flex-direction :row})
|
||||
|
||||
(defn dynamic-progress-bar
|
||||
[width]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:width width}
|
||||
{:height 2
|
||||
:border-radius 4
|
||||
:overflow :hidden}))
|
||||
[width animate?]
|
||||
(let [normal-style {:height 2
|
||||
:border-radius 4
|
||||
:overflow :hidden
|
||||
:width width}]
|
||||
(if animate?
|
||||
(reanimated/apply-animations-to-style
|
||||
{:width width}
|
||||
normal-style)
|
||||
normal-style)))
|
||||
|
||||
(defn progress-bar-container
|
||||
[progress-bar-width status-bar-height]
|
||||
@@ -63,11 +67,15 @@
|
||||
:top (+ 12 status-bar-height)})
|
||||
|
||||
(defn carousel-container
|
||||
[left]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:left left}
|
||||
{:position :absolute
|
||||
:right 0
|
||||
:top 0
|
||||
:bottom 0
|
||||
:flex-direction :row}))
|
||||
[left animate?]
|
||||
(let [normal-style {:position :absolute
|
||||
:right 0
|
||||
:top 0
|
||||
:bottom 0
|
||||
:flex-direction :row
|
||||
:left left}]
|
||||
(if animate?
|
||||
(reanimated/apply-animations-to-style
|
||||
{:left left}
|
||||
normal-style)
|
||||
normal-style)))
|
||||
|
||||
@@ -54,30 +54,33 @@
|
||||
[rn/view {:style (style/progress-bar-item static? true)}]])
|
||||
|
||||
(defn dynamic-progress-bar
|
||||
[progress-bar-width]
|
||||
[progress-bar-width animate?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [width (animation/dynamic-progress-bar-width progress-bar-width)]
|
||||
[reanimated/view {:style (style/dynamic-progress-bar width)}
|
||||
(let [width (animation/dynamic-progress-bar-width progress-bar-width animate?)
|
||||
container-view (if animate? reanimated/view rn/view)]
|
||||
[container-view {:style (style/dynamic-progress-bar width animate?)}
|
||||
[progress-bar
|
||||
{:static? false
|
||||
:progress-bar-width progress-bar-width}]]))])
|
||||
|
||||
(defn view
|
||||
[]
|
||||
[animate?]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [window-width (rf/sub [:dimensions/window-width])
|
||||
view-id (rf/sub [:view-id])
|
||||
status-bar-height (:status-bar-height @navigation/constants)
|
||||
progress-bar-width (- window-width 40)
|
||||
carousel-left (animation/carousel-left-position window-width)]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(reanimated/set-shared-value @animation/paused (not= view-id :intro)))
|
||||
[view-id])
|
||||
carousel-left (animation/carousel-left-position window-width animate?)
|
||||
container-view (if animate? reanimated/view rn/view)]
|
||||
(when animate?
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(reanimated/set-shared-value @animation/paused (not= view-id :intro)))
|
||||
[view-id]))
|
||||
[:<>
|
||||
[reanimated/view {:style (style/carousel-container carousel-left)}
|
||||
[container-view {:style (style/carousel-container carousel-left animate?)}
|
||||
(for [index (range 2)]
|
||||
^{:key index}
|
||||
[content-view
|
||||
@@ -91,5 +94,5 @@
|
||||
[progress-bar
|
||||
{:static? true
|
||||
:progress-bar-width progress-bar-width}]
|
||||
[dynamic-progress-bar progress-bar-width]]]))])
|
||||
[dynamic-progress-bar progress-bar-width animate?]]]))])
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn navigation-bar
|
||||
[{:keys [on-press-info]}]
|
||||
[rn/view {:style {:height 56}}
|
||||
[{:keys [top right-section-buttons disable-back-button?]}]
|
||||
[rn/view
|
||||
{:style {:height 56
|
||||
:margin-top top}}
|
||||
[quo/page-nav
|
||||
{:align-mid? true
|
||||
:mid-section {:type :text-only}
|
||||
:mid-section {:type :text-only :main-text ""}
|
||||
:left-section {:type :blur-bg
|
||||
:icon :i/arrow-left
|
||||
:icon-override-theme :dark
|
||||
:on-press #(rf/dispatch [:navigate-back])}
|
||||
:right-section-buttons [{:type :blur-bg
|
||||
:icon :i/info
|
||||
:icon-override-theme :dark
|
||||
:on-press on-press-info}]}]])
|
||||
:on-press (when-not disable-back-button?
|
||||
#(rf/dispatch [:navigate-back]))}
|
||||
:right-section-buttons right-section-buttons}]])
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
[quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.onboarding.common.background.view :as background]
|
||||
[status-im2.contexts.onboarding.common.navigation-bar.view :as navigation-bar]
|
||||
@@ -65,7 +66,7 @@
|
||||
:shown true}
|
||||
:placeholder (i18n/label :t/password-creation-placeholder-1)
|
||||
:on-change-text on-change-password
|
||||
:on-focus #(on-input-focus :password)}]
|
||||
:on-focus on-input-focus}]
|
||||
[rn/view {:style style/space-between-inputs}]
|
||||
[password-with-hint
|
||||
{:hint {:text hint-2-text
|
||||
@@ -75,7 +76,7 @@
|
||||
:error? error?
|
||||
:placeholder (i18n/label :t/password-creation-placeholder-2)
|
||||
:on-change-text on-change-repeat-password
|
||||
:on-focus #(on-input-focus :repeat-password)
|
||||
:on-focus on-input-focus
|
||||
:on-blur on-blur-repeat-password}]]))
|
||||
|
||||
(def strength-status
|
||||
@@ -146,9 +147,9 @@
|
||||
:passwords-match? same-passwords?
|
||||
:empty-password? empty-password?
|
||||
:show-password-validation? @show-password-validation?
|
||||
:on-input-focus (fn [input-id]
|
||||
:on-input-focus (fn []
|
||||
(scroll-to-end-fn)
|
||||
(reset! focused-input input-id))
|
||||
(reset! focused-input :password))
|
||||
:on-change-password (fn [new-value]
|
||||
(reset! password new-value)
|
||||
(when (same-password-length?)
|
||||
@@ -162,19 +163,18 @@
|
||||
(reset! show-password-validation? true))}]]
|
||||
|
||||
[rn/view {:style style/bottom-part}
|
||||
[rn/view {:style style/disclaimer-container}
|
||||
[quo/disclaimer
|
||||
{:blur? true
|
||||
:on-change #(reset! accepts-disclaimer? %)
|
||||
:checked? @accepts-disclaimer?}
|
||||
(i18n/label :t/password-creation-disclaimer)]]
|
||||
|
||||
(when (= @focused-input :password)
|
||||
[help
|
||||
{:validations validations
|
||||
:password-strength password-strength}])
|
||||
|
||||
(when (= @focused-input :repeat-password)
|
||||
[rn/view {:style style/disclaimer-container}
|
||||
[quo/disclaimer
|
||||
{:blur? true
|
||||
:checked? @accepts-disclaimer?
|
||||
:on-change #(reset! accepts-disclaimer? %)}
|
||||
(i18n/label :t/password-creation-disclaimer)]])
|
||||
|
||||
[rn/view {:style style/button-container}
|
||||
[quo/button
|
||||
{:disabled (not meet-requirements?)
|
||||
@@ -186,14 +186,21 @@
|
||||
|
||||
(defn create-password
|
||||
[]
|
||||
(let [scroll-view-ref (atom nil)
|
||||
scroll-to-end-fn #(js/setTimeout ^js/Function (.-scrollToEnd @scroll-view-ref) 250)]
|
||||
(fn []
|
||||
[:<>
|
||||
[background/view true]
|
||||
[rn/scroll-view
|
||||
{:ref #(reset! scroll-view-ref %)
|
||||
:style style/overlay
|
||||
:content-container-style style/content-style}
|
||||
[navigation-bar/navigation-bar {:on-press-info #(js/alert "Info pressed")}]
|
||||
[password-form {:scroll-to-end-fn scroll-to-end-fn}]]])))
|
||||
[:f>
|
||||
(let [scroll-view-ref (atom nil)
|
||||
scroll-to-end-fn #(js/setTimeout ^js/Function (.-scrollToEnd @scroll-view-ref) 250)]
|
||||
(fn []
|
||||
(let [{:keys [top]} (safe-area/use-safe-area)]
|
||||
[:<>
|
||||
[background/view true]
|
||||
[rn/scroll-view
|
||||
{:ref #(reset! scroll-view-ref %)
|
||||
:style style/overlay
|
||||
:content-container-style style/content-style}
|
||||
[navigation-bar/navigation-bar
|
||||
{:top top
|
||||
:right-section-buttons [{:type :blur-bg
|
||||
:icon :i/info
|
||||
:icon-override-theme :dark
|
||||
:on-press #(js/alert "Info pressed")}]}]
|
||||
[password-form {:scroll-to-end-fn scroll-to-end-fn}]]])))])
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
:left 0
|
||||
:right 0})
|
||||
|
||||
(def navigation-bar {:height 56})
|
||||
|
||||
(def info-message
|
||||
{:margin-top 8})
|
||||
|
||||
|
||||