Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d739e4413 | ||
|
|
0503f5cfc9 | ||
|
|
a441156ff9 | ||
|
|
006b11d508 | ||
|
|
8f0a990e71 | ||
|
|
f0ca6372cd | ||
|
|
0f8aec00f2 | ||
|
|
de86586208 | ||
|
|
ac2d10bc5d | ||
|
|
e825f930fa | ||
|
|
394dfde87b | ||
|
|
e4db23b0a9 | ||
|
|
94ddbbcd2e | ||
|
|
ac27314547 | ||
|
|
7a4b12acf4 | ||
|
|
4e6dea6b36 | ||
|
|
899f89c800 | ||
|
|
b121678281 | ||
|
|
7f87c007c1 | ||
|
|
11726df060 | ||
|
|
336c98aff9 | ||
|
|
2f84cfd354 | ||
|
|
7b60a5f867 | ||
|
|
7d4be37111 | ||
|
|
2f19badc6c | ||
|
|
3b034265c0 | ||
|
|
5fffc230c9 | ||
|
|
5c92b7eb1e | ||
|
|
45da51bea6 | ||
|
|
8d166a3a52 | ||
|
|
401f7d7383 | ||
|
|
c238ebe36e | ||
|
|
a502da6ea4 | ||
|
|
f9255100a1 | ||
|
|
daa78b4171 | ||
|
|
f2c8f21336 | ||
|
|
554476ede9 | ||
|
|
8c358d4ae4 | ||
|
|
937c128c08 | ||
|
|
03cf4cec0e | ||
|
|
554f8aff09 | ||
|
|
d71cfd12c1 | ||
|
|
a5d767515d | ||
|
|
c38fdec5b7 | ||
|
|
fd6c607115 | ||
|
|
82357057ed |
@@ -61,6 +61,57 @@ the source file. For a real example, see
|
||||
[rn/view (do-something)]])
|
||||
```
|
||||
|
||||
### Always add styles inside the `:style` key
|
||||
|
||||
Although when compiling ReactNative for mobile some components are able work with
|
||||
their styles in the top-level of the properties map, prefer to add them inside the
|
||||
`:style` key in order to separate styles from properties:
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
[rn/button {:flex 1
|
||||
:padding-vertical 10
|
||||
:padding-horizontal 20
|
||||
:on-press #(js/alert "Hi!")
|
||||
:title "Button"}]
|
||||
|
||||
;; good
|
||||
[rn/button {:style {:flex 1
|
||||
:padding-vertical 10
|
||||
:padding-horizontal 20}
|
||||
:on-press #(js/alert "Hi!")
|
||||
:title "Button"}]
|
||||
|
||||
;; better
|
||||
;; (define them in a style ns & place them inside `:style` key)
|
||||
[rn/button {:style (style/button)
|
||||
:on-press #(js/alert "Hi!")
|
||||
:title "Button"}
|
||||
]
|
||||
```
|
||||
|
||||
### Always apply animated styles in the style file
|
||||
|
||||
When implementing styles for reanimated views, we should always define
|
||||
them in the style file and apply animations with reanimated/apply-animations-to-style
|
||||
in the style definition.
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
(defn circle
|
||||
[]
|
||||
(let [opacity (reanimated/use-shared-value 1)]
|
||||
[reanimated/view {:style (reanimated/apply-animations-to-style
|
||||
{:opacity opacity}
|
||||
style/circle-container)}]))
|
||||
|
||||
;; good
|
||||
(defn circle
|
||||
[]
|
||||
(let [opacity (reanimated/use-shared-value 1)]
|
||||
[reanimated/view {:style (style/circle-container opacity)}]))
|
||||
```
|
||||
|
||||
### Don't use percents to define width/height
|
||||
|
||||
In ReactNative, all layouts use the [flexbox
|
||||
|
||||
@@ -35,6 +35,16 @@ abstract_target 'Status' do
|
||||
target 'StatusImPR' do
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
# some of libs wouldn't be build for x86_64 otherwise and that is
|
||||
# necessary for ios simulators
|
||||
installer.pods_project.targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['ONLY_ACTIVE_ARCH'] = 'NO'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
use_native_modules!
|
||||
end
|
||||
|
||||
|
||||
@@ -323,6 +323,30 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||
callback.invoke(finalConfig);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createAccountAndLogin(final String createAccountRequest) {
|
||||
Log.d(TAG, "createAccountAndLogin");
|
||||
String result = Statusgo.createAccountAndLogin(createAccountRequest);
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "createAccountAndLogin success: " + result);
|
||||
Log.d(TAG, "Geth node started");
|
||||
} else {
|
||||
Log.e(TAG, "createAccountAndLogin failed: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void restoreAccountAndLogin(final String restoreAccountRequest) {
|
||||
Log.d(TAG, "restoreAccountAndLogin");
|
||||
String result = Statusgo.restoreAccountAndLogin(restoreAccountRequest);
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "restoreAccountAndLogin success: " + result);
|
||||
Log.d(TAG, "Geth node started");
|
||||
} else {
|
||||
Log.e(TAG, "restoreAccountAndLogin failed: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void saveAccountAndLogin(final String multiaccountData, final String password, final String settings, final String config, final String accountsData) {
|
||||
try {
|
||||
@@ -520,6 +544,8 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "[Opening accounts" + rootDir);
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -785,9 +811,10 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||
@ReactMethod
|
||||
public void getConnectionStringForBootstrappingAnotherDevice(final String configJSON, final Callback callback) throws JSONException {
|
||||
final JSONObject jsonConfig = new JSONObject(configJSON);
|
||||
final String keyUID = jsonConfig.getString("keyUID");
|
||||
final JSONObject senderConfig = jsonConfig.getJSONObject("senderConfig");
|
||||
final String keyUID = senderConfig.getString("keyUID");
|
||||
final String keyStorePath = this.getKeyStorePath(keyUID);
|
||||
jsonConfig.put("keystorePath", keyStorePath);
|
||||
senderConfig.put("keystorePath", keyStorePath);
|
||||
|
||||
executeRunnableStatusGoMethod(() -> Statusgo.getConnectionStringForBootstrappingAnotherDevice(jsonConfig.toString()), callback);
|
||||
}
|
||||
@@ -795,9 +822,10 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||
@ReactMethod
|
||||
public void inputConnectionStringForBootstrapping(final String connectionString, final String configJSON, final Callback callback) throws JSONException {
|
||||
final JSONObject jsonConfig = new JSONObject(configJSON);
|
||||
final JSONObject receiverConfig = jsonConfig.getJSONObject("receiverConfig");
|
||||
final String keyStorePath = pathCombine(this.getNoBackupDirectory(), "/keystore");
|
||||
jsonConfig.put("keystorePath", keyStorePath);
|
||||
jsonConfig.put("rootDataDir", this.getNoBackupDirectory());
|
||||
receiverConfig.put("keystorePath", keyStorePath);
|
||||
receiverConfig.getJSONObject("nodeConfig").put("rootDataDir", this.getNoBackupDirectory());
|
||||
executeRunnableStatusGoMethod(() -> Statusgo.inputConnectionStringForBootstrapping(connectionString, jsonConfig.toString()), callback);
|
||||
}
|
||||
|
||||
@@ -1129,6 +1157,23 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||
executeRunnableStatusGoMethod(() -> Statusgo.deleteImportedKey(address, password, keyStoreDir), callback);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String keystoreDir() {
|
||||
final String absRootDirPath = this.getNoBackupDirectory();
|
||||
return pathCombine(absRootDirPath, "keystore");
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String backupDisabledDataDir() {
|
||||
return this.getNoBackupDirectory();
|
||||
}
|
||||
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String logFilePath() {
|
||||
return getLogsFile().getAbsolutePath();
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String generateAlias(final String seed) {
|
||||
return Statusgo.generateAlias(seed);
|
||||
|
||||
@@ -309,12 +309,14 @@ RCT_EXPORT_METHOD(getConnectionStringForBootstrappingAnotherDevice:(NSString *)c
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
|
||||
NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
|
||||
NSString *keyUID = [configDict objectForKey:@"keyUID"];
|
||||
NSError *error;
|
||||
NSMutableDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:&error];
|
||||
NSMutableDictionary *senderConfig = configDict[@"senderConfig"];
|
||||
NSString *keyUID = senderConfig[@"keyUID"];
|
||||
NSURL *multiaccountKeystoreDir = [self getKeyStoreDir:keyUID];
|
||||
NSString *keystoreDir = multiaccountKeystoreDir.path;
|
||||
|
||||
[configDict setValue:keystoreDir forKey:@"keystorePath"];
|
||||
[senderConfig setValue:keystoreDir forKey:@"keystorePath"];
|
||||
NSString *modifiedConfigJSON = [configDict bv_jsonStringWithPrettyPrint:NO];
|
||||
|
||||
NSString *result = StatusgoGetConnectionStringForBootstrappingAnotherDevice(modifiedConfigJSON);
|
||||
@@ -326,17 +328,20 @@ RCT_EXPORT_METHOD(inputConnectionStringForBootstrapping:(NSString *)cs
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
|
||||
NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
|
||||
NSError *error;
|
||||
NSMutableDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:&error];
|
||||
NSMutableDictionary *receiverConfig = configDict[@"receiverConfig"];
|
||||
NSMutableDictionary *nodeConfig = receiverConfig[@"nodeConfig"];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject];
|
||||
NSURL *rootDataDir = rootUrl.path;
|
||||
NSURL *multiaccountKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
NSString *keystoreDir = multiaccountKeystoreDir.path;
|
||||
NSString *rootDataDir = rootUrl.path;
|
||||
|
||||
[configDict setValue:keystoreDir forKey:@"keystorePath"];
|
||||
[configDict setValue:rootDataDir forKey:@"rootDataDir"];
|
||||
[receiverConfig setValue:keystoreDir forKey:@"keystorePath"];
|
||||
[nodeConfig setValue:rootDataDir forKey:@"rootDataDir"];
|
||||
NSString *modifiedConfigJSON = [configDict bv_jsonStringWithPrettyPrint:NO];
|
||||
NSString *result = StatusgoInputConnectionStringForBootstrapping(cs,modifiedConfigJSON);
|
||||
NSString *result = StatusgoInputConnectionStringForBootstrapping(cs, modifiedConfigJSON);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
@@ -857,6 +862,34 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(generateAlias:(NSString *)publicKey) {
|
||||
return StatusgoGenerateAlias(publicKey);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(keystoreDir) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
|
||||
NSURL *commonKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
|
||||
|
||||
return commonKeystoreDir.path;
|
||||
}
|
||||
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(backupDisabledDataDir) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
return rootUrl.path;
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(logFilePath) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *rootUrl =[[fileManager
|
||||
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
|
||||
lastObject];
|
||||
return rootUrl.path;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(generateAliasAsync:(NSString *)publicKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
@@ -934,6 +967,20 @@ RCT_EXPORT_METHOD(identiconAsync:(NSString *)publicKey
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(createAccountAndLogin:(NSString *)request) {
|
||||
#if DEBUG
|
||||
NSLog(@"createAccountAndLogin() method called");
|
||||
#endif
|
||||
StatusgoCreateAccountAndLogin(request);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(restoreAccountAndLogin:(NSString *)request) {
|
||||
#if DEBUG
|
||||
NSLog(@"restoreAccountAndLogin() method called");
|
||||
#endif
|
||||
StatusgoRestoreAccountAndLogin(request);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(generateAliasAndIdenticonAsync:(NSString *)publicKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
|
||||
@@ -11572,57 +11572,57 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/slf4j/jcl-over-slf4j/2.0.6",
|
||||
"path": "org/slf4j/jcl-over-slf4j/2.0.7",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"jcl-over-slf4j-2.0.6.pom": {
|
||||
"sha1": "c9d9caedcca2a1564e47b55614960958c5f78773",
|
||||
"sha256": "1swgy5hwv54b1i1117shn3wmwba3v78qvy6cnv8v243j7ac6vzqq"
|
||||
"jcl-over-slf4j-2.0.7.pom": {
|
||||
"sha1": "15536e4a74a7aa317322a3d7814db8251de60d6f",
|
||||
"sha256": "1rzjwbmzf2hb85j6c41mch803vqwqdq1rfrrirfhp1lfpfnzyd23"
|
||||
},
|
||||
"jcl-over-slf4j-2.0.6.jar": {
|
||||
"sha1": "839ff57e112f2e28ef372e96d135696a6896b9ad",
|
||||
"sha256": "0s9scdwkxwj3al87ihanj10rscrjh44kligr5asb7qpl28d1xvks"
|
||||
"jcl-over-slf4j-2.0.7.jar": {
|
||||
"sha1": "f127fe5ee53404a8b3697cdd032dd1dd6a29dd77",
|
||||
"sha256": "0hhpc2qdl4aa9mb8dk89wzbd5vkna557vjmjdmfswvfjw5bng021"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/slf4j/slf4j-api/2.0.6",
|
||||
"path": "org/slf4j/slf4j-api/2.0.7",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"slf4j-api-2.0.6.pom": {
|
||||
"sha1": "2b93d5f66ad2ba259bf4b2c94da39f0d6c544400",
|
||||
"sha256": "0dipzawn8rxikciij2z06c25rb2vdj83s8ga3a7n10r77p2qcklb"
|
||||
"slf4j-api-2.0.7.pom": {
|
||||
"sha1": "facf002401dbff2065d4257690651e3fa775e3f4",
|
||||
"sha256": "10br4q2w50fn3mkvk1xji81wdpy86cm0wyv6m30xa0ha1v7kqh1d"
|
||||
},
|
||||
"slf4j-api-2.0.6.jar": {
|
||||
"sha1": "88c40d8b4f33326f19a7d3c0aaf2c7e8721d4953",
|
||||
"sha256": "1nkv0z4dpkvp6pr9ph8087z5r691bv95xdv3gnfi6s5j23a94aig"
|
||||
"slf4j-api-2.0.7.jar": {
|
||||
"sha1": "41eb7184ea9d556f23e18b5cb99cad1f8581fc00",
|
||||
"sha256": "1x26v62ypzpp84yfad8mx53llbacq6580y34v8nc618r7awrhqjx"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/slf4j/slf4j-jdk14/2.0.6",
|
||||
"path": "org/slf4j/slf4j-jdk14/2.0.7",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"slf4j-jdk14-2.0.6.pom": {
|
||||
"sha1": "96ac9d6ab608e787d54892b35205bc6a560aa007",
|
||||
"sha256": "0gnsyy1n5v8xqdy6a6p1752m5c4afihpw0n36n6aqw01y9l86q1h"
|
||||
"slf4j-jdk14-2.0.7.pom": {
|
||||
"sha1": "31039eac9f263c48bda14efd90aef0fb7a5a0e6a",
|
||||
"sha256": "1vx1ziyx36zf2lrpkvvm4c75hp8pw8vjpiyr5120ss8nwpd33qal"
|
||||
},
|
||||
"slf4j-jdk14-2.0.6.jar": {
|
||||
"sha1": "13056cb341f2d8795120f8027766a058da874f85",
|
||||
"sha256": "0jncm8a2ppliqpzkbqm26sn98mwif8c1nligc1zh4jbzr8mxyzhy"
|
||||
"slf4j-jdk14-2.0.7.jar": {
|
||||
"sha1": "d91cd16b55ffbd5f46c60d0173fd4eeccacfee2d",
|
||||
"sha256": "1xvphj12jpnvr1sw8zx42p49in4xi61mjvpxrkk3q3qpyad7017r"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/slf4j/slf4j-parent/2.0.6",
|
||||
"path": "org/slf4j/slf4j-parent/2.0.7",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"slf4j-parent-2.0.6.pom": {
|
||||
"sha1": "0f99f8426c64fc5e0c4b6749245e50484a16c372",
|
||||
"sha256": "091il49sidk0lcmzdy0vl3a4l16kw6x1q0l9vk0hir1ipq66b0hl"
|
||||
"slf4j-parent-2.0.7.pom": {
|
||||
"sha1": "3f97e066227cc2353d212b8c43440b0bf4c033f3",
|
||||
"sha256": "1rs55v9gqda5lks89dd81frps5c9g3zgxk832qyckw9srlvbp0n1"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -802,9 +802,9 @@ https://repo.maven.apache.org/maven2/org/ow2/asm/asm/6.0/asm-6.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/ow2/asm/asm/9.4/asm-9.4.pom
|
||||
https://repo.maven.apache.org/maven2/org/ow2/ow2/1.3/ow2-1.3.pom
|
||||
https://repo.maven.apache.org/maven2/org/ow2/ow2/1.5.1/ow2-1.5.1.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/jcl-over-slf4j/2.0.6/jcl-over-slf4j-2.0.6.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-api/2.0.6/slf4j-api-2.0.6.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-jdk14/2.0.6/slf4j-jdk14-2.0.6.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-parent/2.0.6/slf4j-parent-2.0.6.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/jcl-over-slf4j/2.0.7/jcl-over-slf4j-2.0.7.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-api/2.0.7/slf4j-api-2.0.7.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-jdk14/2.0.7/slf4j-jdk14-2.0.7.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-parent/2.0.7/slf4j-parent-2.0.7.pom
|
||||
https://repo.maven.apache.org/maven2/org/sonatype/oss/oss-parent/7/oss-parent-7.pom
|
||||
https://repo.maven.apache.org/maven2/org/sonatype/oss/oss-parent/9/oss-parent-9.pom
|
||||
|
||||
@@ -29,17 +29,17 @@ function findPackage(line, regex) {
|
||||
if (line ~ "com.facebook.react:react-native") { continue }
|
||||
|
||||
# Example: +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.50
|
||||
if (findPackage(line, "--- ([^:]+):([^:]+):([^ ]+)$")) {
|
||||
if (findPackage(line, "--- ([^ :]+):([^ :]+):([^ :]+)$")) {
|
||||
continue
|
||||
}
|
||||
|
||||
# Example: +--- androidx.lifecycle:lifecycle-common:{strictly 2.0.0} -> 2.0.0 (c)
|
||||
if (findPackage(line, "--- ([^:]+):([^:]+):[^ ]+ -> ([^: ]+) ?(\\([*c]\\))?$")) {
|
||||
if (findPackage(line, "--- ([^ :]+):([^ :]+):[^:]+ -> ([^ :]+) ?(\\([*c]\\))?$")) {
|
||||
continue
|
||||
}
|
||||
|
||||
# Example: +--- com.android.support:appcompat-v7:28.0.0 -> androidx.appcompat:appcompat:1.0.2
|
||||
if (findPackage(line, "--- [^:]+:[^:]+:[^ ]+ -> ([^:]+):([^:]+):([^ ]+)$")) {
|
||||
if (findPackage(line, "--- [^ :]+:[^ :]+:[^ ]+ -> ([^ :]+):([^ :]+):([^ :]+)$")) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ in {
|
||||
|
||||
# Package version adjustments
|
||||
gradle = super.gradle_5;
|
||||
nodejs = super.nodejs-16_x;
|
||||
yarn = super.yarn.override { nodejs = super.nodejs-16_x; };
|
||||
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";
|
||||
|
||||
@@ -42,12 +42,19 @@ let
|
||||
# for running gradle by hand
|
||||
gradle = mkShell {
|
||||
buildInputs = with pkgs; [ gradle maven goMavenResolver ];
|
||||
inputsFrom = [ nodejs-sh ];
|
||||
shellHook = ''
|
||||
export STATUS_GO_ANDROID_LIBDIR="DUMMY"
|
||||
export STATUS_NIX_MAVEN_REPO="${pkgs.deps.gradle}"
|
||||
export ANDROID_SDK_ROOT="${pkgs.androidPkgs.sdk}"
|
||||
export ANDROID_NDK_ROOT="${pkgs.androidPkgs.ndk}"
|
||||
|
||||
export STATUS_MOBILE_HOME=$(git rev-parse --show-toplevel)
|
||||
# WARNING: Unpatched Node.js deps allow Gradle to use remote repos.
|
||||
"$STATUS_MOBILE_HOME/nix/scripts/node_modules.sh" ${pkgs.deps.nodejs}
|
||||
function restore_patched_modules() {
|
||||
"$STATUS_MOBILE_HOME/nix/scripts/node_modules.sh" ${pkgs.deps.nodejs-patched}
|
||||
}
|
||||
trap restore_patched_modules EXIT
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@react-native-async-storage/async-storage": "^1.17.9",
|
||||
"@react-native-community/audio-toolkit": "git+https://github.com/tbenr/react-native-audio-toolkit.git#refs/tags/v2.0.3-status-v6",
|
||||
"@react-native-community/blur": "git+https://github.com/status-im/react-native-blur#refs/tags/v4.3.1-status",
|
||||
"@react-native-community/blur": "git+https://github.com/status-im/react-native-blur#refs/tags/v4.3.2-status",
|
||||
"@react-native-community/cameraroll": "git+https://github.com/status-im/react-native-cameraroll.git#refs/tags/v4.0.4-status.0",
|
||||
"@react-native-community/clipboard": "^1.2.2",
|
||||
"@react-native-community/hooks": "^2.5.1",
|
||||
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 788 KiB |
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 2.6 MiB |
|
After Width: | Height: | Size: 4.7 MiB |
|
After Width: | Height: | Size: 8.9 MiB |
@@ -45,7 +45,13 @@
|
||||
:devtools {:autobuild #shadow/env ["SHADOW_AUTOBUILD_ENABLED" :default true :as :bool]}
|
||||
:dev {:devtools {:after-load status-im2.setup.hot-reload/reload
|
||||
:build-notify status-im2.setup.hot-reload/build-notify
|
||||
:preloads [re-frisk-remote.preload]}
|
||||
:preloads [re-frisk-remote.preload
|
||||
;; In order to use component test helpers in
|
||||
;; the REPL we need to preload namespaces
|
||||
;; that are not normally required by
|
||||
;; production code, such as
|
||||
;; @testing-library/react-native.
|
||||
test-helpers.component]}
|
||||
:closure-defines
|
||||
{status-im2.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
|
||||
status-im2.config/OPENSEA_API_KEY #shadow/env "OPENSEA_API_KEY"}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useDerivedValue, withTiming, Easing } from 'react-native-reanimated';
|
||||
|
||||
const slideAnimationDuration = 300;
|
||||
|
||||
const easeOut = {
|
||||
duration: slideAnimationDuration,
|
||||
easing: Easing.bezier(0, 0, 0.58, 1),
|
||||
}
|
||||
|
||||
// Derived Values
|
||||
export function dynamicProgressBarWidth(staticProgressBarWidth, progress) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return staticProgressBarWidth * (progress.value || 0) / 100;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function carouselLeftPosition(windowWidth, progress) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
const progressValue = progress.value;
|
||||
switch (true) {
|
||||
case (progressValue < 25):
|
||||
return 0;
|
||||
case (progressValue === 25):
|
||||
return withTiming(-windowWidth, easeOut);
|
||||
case (progressValue < 50):
|
||||
return -windowWidth;
|
||||
case (progressValue === 50):
|
||||
return withTiming(-2 * windowWidth, easeOut);
|
||||
case (progressValue < 75):
|
||||
return -2 * windowWidth;
|
||||
case (progressValue === 75):
|
||||
return withTiming(-3 * windowWidth, easeOut);
|
||||
case (progressValue < 100):
|
||||
return -3 * windowWidth;
|
||||
case (progressValue === 100):
|
||||
return withTiming(-4 * windowWidth, easeOut);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -293,7 +293,10 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
||||
:createNativeWrapper identity
|
||||
:default #js {}})
|
||||
|
||||
(def react-native-redash #js {:clamp nil})
|
||||
(def react-native-redash
|
||||
#js
|
||||
{:clamp nil
|
||||
:withPause (fn [])})
|
||||
|
||||
(def react-native-languages
|
||||
(clj->js {:default {:language "en"
|
||||
@@ -370,6 +373,7 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
||||
"react-native-screens" (clj->js {})
|
||||
"react-native-reanimated" react-native-reanimated
|
||||
"react-native-redash/lib/module/v1" react-native-redash
|
||||
"react-native-redash" react-native-redash
|
||||
"react-native-fetch-polyfill" fetch
|
||||
"react-native-status-keycard" status-keycard
|
||||
"react-native-keychain" keychain
|
||||
@@ -407,6 +411,7 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
||||
"../src/js/worklets/bottom_sheet.js" #js {}
|
||||
"../src/js/worklets/record_audio.js" #js {}
|
||||
"../src/js/worklets/scroll_view.js" #js {}
|
||||
"../src/js/worklets/onboarding_carousel.js" #js {}
|
||||
"../src/js/worklets/lightbox.js" #js {}
|
||||
"./fleets.js" default-fleets
|
||||
"@walletconnect/client" wallet-connect-client
|
||||
|
||||
@@ -58,6 +58,18 @@
|
||||
|
||||
(h/test "Size :small"
|
||||
(h/render (user-avatar-component :small))
|
||||
(h/is-truthy (h/get-by-text "NU")))
|
||||
|
||||
(h/test "Two letters with excess whitespace"
|
||||
(h/render [user-avatar/user-avatar
|
||||
{:full-name "New User"
|
||||
:size :big}])
|
||||
(h/is-truthy (h/get-by-text "NU")))
|
||||
|
||||
(h/test "Two letters with leading whitespace"
|
||||
(h/render [user-avatar/user-avatar
|
||||
{:full-name " New User"
|
||||
:size :big}])
|
||||
(h/is-truthy (h/get-by-text "NU"))))
|
||||
|
||||
(h/describe "One letter"
|
||||
|
||||
@@ -5,14 +5,18 @@
|
||||
[react-native.core :as rn]
|
||||
[react-native.fast-image :as fast-image]))
|
||||
|
||||
(defn trim-whitespace [s] (string/join " " (string/split (string/trim s) #"\s+")))
|
||||
|
||||
(defn- extract-initials
|
||||
[full-name amount-initials]
|
||||
(let [upper-case-first-letter (comp string/upper-case first)
|
||||
names-list (string/split full-name " ")]
|
||||
(->> names-list
|
||||
(map upper-case-first-letter)
|
||||
(take amount-initials)
|
||||
(string/join))))
|
||||
names-list (string/split (trim-whitespace full-name) " ")]
|
||||
(if (= (first names-list) "")
|
||||
""
|
||||
(->> names-list
|
||||
(map upper-case-first-letter)
|
||||
(take amount-initials)
|
||||
(string/join)))))
|
||||
|
||||
(defn initials-avatar
|
||||
[{:keys [full-name size draw-ring? customization-color]}]
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def themes
|
||||
(defn themes
|
||||
[customization-color]
|
||||
{:light {:primary {:icon-color colors/white
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/primary-50
|
||||
:pressed colors/primary-60
|
||||
:disabled colors/primary-50}}
|
||||
:background-color {:default (colors/custom-color customization-color 50)
|
||||
:pressed (colors/custom-color customization-color 60)
|
||||
:disabled (colors/custom-color customization-color 50)}}
|
||||
:secondary {:icon-color colors/primary-50
|
||||
:label-color colors/primary-50
|
||||
:background-color {:default colors/primary-50-opa-20
|
||||
@@ -74,9 +75,9 @@
|
||||
:disabled colors/neutral-95}}}
|
||||
:dark {:primary {:icon-color colors/white
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/primary-60
|
||||
:pressed colors/primary-50
|
||||
:disabled colors/primary-60}}
|
||||
:background-color {:default (colors/custom-color customization-color 60)
|
||||
:pressed (colors/custom-color customization-color 50)
|
||||
:disabled (colors/custom-color customization-color 60)}}
|
||||
:secondary {:icon-color colors/primary-50
|
||||
:label-color colors/primary-50
|
||||
:background-color {:default colors/primary-50-opa-20
|
||||
@@ -218,14 +219,15 @@
|
||||
(let [pressed (reagent/atom false)]
|
||||
(fn
|
||||
[{:keys [on-press disabled type size community-color community-text-color before after above
|
||||
width
|
||||
width customization-color
|
||||
override-theme override-background-color
|
||||
on-long-press accessibility-label icon icon-no-color style inner-style test-ID]
|
||||
:or {type :primary
|
||||
size 40}}
|
||||
:or {type :primary
|
||||
size 40
|
||||
customization-color :primary}}
|
||||
children]
|
||||
(let [{:keys [icon-color icon-secondary-color background-color label-color border-color]}
|
||||
(get-in themes
|
||||
(get-in (themes customization-color)
|
||||
[(or
|
||||
override-theme
|
||||
(theme/get-theme)) type])
|
||||
@@ -252,11 +254,7 @@
|
||||
[rn/view
|
||||
{:style (merge
|
||||
(shape-style-container type icon size)
|
||||
{:background-color
|
||||
(if (= state :pressed)
|
||||
(colors/theme-colors colors/neutral-100 colors/white)
|
||||
:transparent)
|
||||
:width width}
|
||||
{:width width}
|
||||
style)}
|
||||
[rn/view
|
||||
{:style (merge
|
||||
|
||||
@@ -22,18 +22,18 @@
|
||||
opts
|
||||
{:type :default/:success/:error
|
||||
:size :default/:tiny
|
||||
:icon :main-icons/info ;; info message icon
|
||||
:icon :i/info ;; info message icon
|
||||
:text-color colors/white ;; text color override
|
||||
:icon-color colors/white ;; icon color override
|
||||
:no-icon-color? false ;; disable tint color for icon"
|
||||
[{:keys [type size icon text-color icon-color no-icon-color?]} message]
|
||||
[{:keys [type size icon text-color icon-color no-icon-color? style]} message]
|
||||
(let [weight (if (= size :default) :regular :medium)
|
||||
size (if (= size :default) :paragraph-2 :label)
|
||||
text-color (or text-color (get-color type))
|
||||
icon-color (or icon-color text-color)]
|
||||
[rn/view
|
||||
{:style {:flex-direction :row
|
||||
:flex 1}}
|
||||
{:style (merge {:flex-direction :row}
|
||||
style)}
|
||||
[quo2.icons/icon icon
|
||||
{:color icon-color
|
||||
:no-color no-icon-color?
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
(ns quo2.components.inputs.input.component-spec
|
||||
(:require [quo2.components.inputs.input.view :as input]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "Input"
|
||||
(h/test "default render"
|
||||
(h/render [input/input])
|
||||
(h/is-truthy (h/query-by-label-text :input))
|
||||
(h/is-null (h/query-by-label-text :password-input)))
|
||||
|
||||
(h/test "Password type"
|
||||
(h/render [input/input {:type :password}])
|
||||
(h/is-truthy (h/query-by-label-text :password-input))
|
||||
(h/is-null (h/query-by-label-text :input)))
|
||||
|
||||
(h/describe "Icon"
|
||||
(h/test "Doesn't exist in base input"
|
||||
(h/render [input/input])
|
||||
(h/is-null (h/query-by-label-text :input-icon)))
|
||||
|
||||
(h/test "Renders"
|
||||
(h/render [input/input {:icon-name :i/placeholder}])
|
||||
(h/is-truthy (h/get-by-label-text :input-icon))))
|
||||
|
||||
(h/describe "Right accessory"
|
||||
(h/test "Doesn't exist in base input"
|
||||
(h/render [input/input])
|
||||
(h/is-null (h/query-by-label-text :input-right-icon)))
|
||||
|
||||
(h/test "Clear icon"
|
||||
(h/render [input/input {:clearable? true}])
|
||||
(h/is-truthy (h/query-by-label-text :input-right-icon)))
|
||||
|
||||
(h/test "Password icon"
|
||||
(h/render [input/input {:type :password}])
|
||||
(h/is-truthy (h/query-by-label-text :input-right-icon))))
|
||||
|
||||
(h/describe "Button"
|
||||
(h/test "Doesn't exist in base input"
|
||||
(h/render [input/input])
|
||||
(h/is-null (h/query-by-label-text :input-button)))
|
||||
|
||||
(h/test "Renders with given text and it's pressable"
|
||||
(let [button-text "This is a button"
|
||||
button-callback (h/mock-fn)]
|
||||
(h/render [input/input
|
||||
{:button {:on-press button-callback
|
||||
:text button-text}}])
|
||||
(h/is-truthy (h/query-by-label-text :input-button))
|
||||
(h/is-truthy (h/get-by-text button-text))
|
||||
(h/fire-event :press (h/query-by-label-text :input-button))
|
||||
(h/was-called button-callback))))
|
||||
|
||||
(h/describe "Label"
|
||||
(h/test "Doesn't exist in base input"
|
||||
(h/render [input/input])
|
||||
(h/is-null (h/query-by-label-text :input-labels)))
|
||||
|
||||
(h/test "Renders with specified text"
|
||||
(let [input-label "My label"]
|
||||
(h/render [input/input {:label input-label}])
|
||||
(h/is-truthy (h/query-by-label-text :input-labels))
|
||||
(h/is-truthy (h/get-by-text input-label)))))
|
||||
|
||||
(h/test "Char limit counter"
|
||||
(let [char-limit 100
|
||||
char-limit-str (str "0/" char-limit)]
|
||||
(h/render [input/input {:char-limit char-limit}])
|
||||
(h/is-truthy (h/query-by-label-text :input-labels))
|
||||
(h/is-truthy (h/get-by-text char-limit-str)))))
|
||||
@@ -66,7 +66,7 @@
|
||||
:padding-horizontal 8
|
||||
:border-width 1
|
||||
:border-color (:border-color colors-by-status)
|
||||
:border-radius (if small? 10 14)
|
||||
:border-radius (if small? 10 12)
|
||||
:opacity (if disabled? 0.3 1)})
|
||||
|
||||
(defn left-icon-container
|
||||
@@ -83,15 +83,15 @@
|
||||
|
||||
(defn input
|
||||
[colors-by-status small? multiple-lines?]
|
||||
(merge (text/text-style {:size :paragraph-1 :weight :regular})
|
||||
{:flex 1
|
||||
:text-align-vertical :top
|
||||
:padding-right 0
|
||||
:padding-left (if small? 4 8)
|
||||
:padding-vertical (if small? 4 8)
|
||||
:color (:text colors-by-status)}
|
||||
(when-not multiple-lines?
|
||||
{:height (if small? 30 38)})))
|
||||
(let [base-props (assoc (text/text-style {:size :paragraph-1 :weight :regular})
|
||||
:flex 1
|
||||
:padding-right 0
|
||||
:padding-left (if small? 4 8)
|
||||
:padding-vertical (if small? 4 8)
|
||||
:color (:text colors-by-status))]
|
||||
(if multiple-lines?
|
||||
(assoc base-props :text-align-vertical :top)
|
||||
(assoc base-props :height (if small? 30 38) :line-height nil))))
|
||||
|
||||
(defn right-icon-touchable-area
|
||||
[small?]
|
||||
@@ -110,8 +110,7 @@
|
||||
:color (:clear-icon variant-colors)})
|
||||
|
||||
(def texts-container
|
||||
{:flex 1
|
||||
:flex-direction :row
|
||||
{:flex-direction :row
|
||||
:height 18
|
||||
:margin-bottom 8})
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
(defn- label-&-counter
|
||||
[{:keys [label current-chars char-limit variant-colors]}]
|
||||
(let [count-text (when char-limit (str current-chars "/" char-limit))]
|
||||
[rn/view {:style style/texts-container}
|
||||
[rn/view
|
||||
{:accessibility-label :input-labels
|
||||
:style style/texts-container}
|
||||
[rn/view {:style style/label-container}
|
||||
[text/text
|
||||
{:style (style/label-color variant-colors)
|
||||
@@ -25,36 +27,40 @@
|
||||
|
||||
(defn- left-accessory
|
||||
[{:keys [variant-colors small? icon-name]}]
|
||||
[rn/view {:style (style/left-icon-container small?)}
|
||||
[rn/view
|
||||
{:accessibility-label :input-icon
|
||||
:style (style/left-icon-container small?)}
|
||||
[icon/icon icon-name (style/icon variant-colors)]])
|
||||
|
||||
(defn- right-accessory
|
||||
[{:keys [variant-colors small? disabled? on-press icon-style-fn icon-name]}]
|
||||
[rn/touchable-opacity
|
||||
{:style (style/right-icon-touchable-area small?)
|
||||
:disabled disabled?
|
||||
:on-press on-press}
|
||||
{:accessibility-label :input-right-icon
|
||||
:style (style/right-icon-touchable-area small?)
|
||||
:disabled disabled?
|
||||
:on-press on-press}
|
||||
[icon/icon icon-name (icon-style-fn variant-colors)]])
|
||||
|
||||
(defn- right-button
|
||||
[{:keys [variant-colors colors-by-status small? disabled? on-press text]}]
|
||||
[rn/touchable-opacity
|
||||
{:style (style/button variant-colors small?)
|
||||
:disabled disabled?
|
||||
:on-press on-press}
|
||||
{:accessibility-label :input-button
|
||||
:style (style/button variant-colors small?)
|
||||
:disabled disabled?
|
||||
:on-press on-press}
|
||||
[rn/text {:style (style/button-text colors-by-status)}
|
||||
text]])
|
||||
|
||||
(def ^:private custom-props
|
||||
"Custom properties that must be removed from properties map passed to InputText."
|
||||
[:type :blur? :override-theme :error? :right-icon :left-icon :disabled? :small? :button
|
||||
:label :char-limit :on-char-limit-reach :icon-name :multiline?])
|
||||
:label :char-limit :on-char-limit-reach :icon-name :multiline? :on-focus :on-blur])
|
||||
|
||||
(defn- base-input
|
||||
[{:keys [on-change-text on-char-limit-reach]}]
|
||||
(let [status (reagent/atom :default)
|
||||
on-focus #(reset! status :focus)
|
||||
on-blur #(reset! status :default)
|
||||
internal-on-focus #(reset! status :focus)
|
||||
internal-on-blur #(reset! status :default)
|
||||
multiple-lines? (reagent/atom false)
|
||||
set-multiple-lines! #(let [height (oops/oget % "nativeEvent.contentSize.height")]
|
||||
(if (> height 57)
|
||||
@@ -68,7 +74,7 @@
|
||||
(when (>= amount-chars char-limit)
|
||||
(on-char-limit-reach amount-chars))))]
|
||||
(fn [{:keys [blur? override-theme error? right-icon left-icon disabled? small? button
|
||||
label char-limit multiline? clearable?]
|
||||
label char-limit multiline? clearable? on-focus on-blur]
|
||||
:as props}]
|
||||
(let [status-kw (cond
|
||||
disabled? :disabled
|
||||
@@ -77,7 +83,7 @@
|
||||
colors-by-status (style/status-colors status-kw blur? override-theme)
|
||||
variant-colors (style/variants-colors blur? override-theme)
|
||||
clean-props (apply dissoc props custom-props)]
|
||||
[rn/view
|
||||
[:<>
|
||||
(when (or label char-limit)
|
||||
[label-&-counter
|
||||
{:variant-colors variant-colors
|
||||
@@ -92,11 +98,16 @@
|
||||
:icon-name icon-name}])
|
||||
[rn/text-input
|
||||
(cond-> {:style (style/input colors-by-status small? @multiple-lines?)
|
||||
:accessibility-label :input
|
||||
:placeholder-text-color (:placeholder colors-by-status)
|
||||
:cursor-color (:cursor variant-colors)
|
||||
:editable (not disabled?)
|
||||
:on-focus on-focus
|
||||
:on-blur on-blur}
|
||||
:on-focus (fn []
|
||||
(when on-focus (on-focus))
|
||||
(internal-on-focus))
|
||||
:on-blur (fn []
|
||||
(when on-blur (on-blur))
|
||||
(internal-on-blur))}
|
||||
:always (merge clean-props)
|
||||
multiline? (assoc :multiline true
|
||||
:on-content-size-change set-multiple-lines!)
|
||||
@@ -126,12 +137,13 @@
|
||||
(fn [props]
|
||||
[base-input
|
||||
(assoc props
|
||||
:auto-capitalize :none
|
||||
:auto-complete :new-password
|
||||
:secure-text-entry (not @password-shown?)
|
||||
:right-icon {:style-fn style/password-icon
|
||||
:icon-name (if @password-shown? :i/hide :i/reveal)
|
||||
:on-press #(swap! password-shown? not)})])))
|
||||
:accessibility-label :password-input
|
||||
:auto-capitalize :none
|
||||
:auto-complete :new-password
|
||||
:secure-text-entry (not @password-shown?)
|
||||
:right-icon {:style-fn style/password-icon
|
||||
:icon-name (if @password-shown? :i/hide :i/reveal)
|
||||
:on-press #(swap! password-shown? not)})])))
|
||||
|
||||
(defn input
|
||||
"This input supports the following properties:
|
||||
@@ -146,7 +158,7 @@
|
||||
- :clearable? - Booolean to specify if this input has a clear button at the end.
|
||||
- :on-clear - Function executed when the clear button is pressed.
|
||||
- :button - Map containing `:on-press` & `:text` keys, if provided renders a button
|
||||
- :label - A label for this input.
|
||||
- :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.
|
||||
and supports the usual React Native's TextInput properties to control its behaviour:
|
||||
|
||||
@@ -25,13 +25,13 @@
|
||||
:height 24
|
||||
:borderRadius 12}]}
|
||||
[user-avatar/user-avatar
|
||||
(merge image-picker-props
|
||||
{:customization-color customization-color
|
||||
:full-name (if (seq full-name)
|
||||
full-name
|
||||
placeholder)
|
||||
:status-indicator? false
|
||||
:size :medium})]]
|
||||
(assoc image-picker-props
|
||||
:customization-color customization-color
|
||||
:full-name (if (seq full-name)
|
||||
full-name
|
||||
placeholder)
|
||||
:status-indicator? false
|
||||
:size :medium)]]
|
||||
[buttons/button
|
||||
{:accessibility-label :select-profile-picture-button
|
||||
:type :grey
|
||||
@@ -47,5 +47,6 @@
|
||||
[rn/view {:style style/input-container}
|
||||
[title-input/title-input
|
||||
(merge title-input-props
|
||||
{:placeholder placeholder
|
||||
{:override-theme :dark
|
||||
:placeholder placeholder
|
||||
:customization-color customization-color})]]]))
|
||||
|
||||
@@ -34,9 +34,11 @@
|
||||
(def text-input-container {:flex 1})
|
||||
|
||||
(defn title-text
|
||||
[disabled? blur?]
|
||||
[disabled? blur? override-theme]
|
||||
{:text-align-vertical :bottom
|
||||
:color (when disabled? (get-disabled-color blur?))})
|
||||
:color (if disabled?
|
||||
(get-disabled-color blur?)
|
||||
(colors/theme-colors colors/neutral-100 colors/white override-theme))})
|
||||
|
||||
(defn char-count
|
||||
[blur?]
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
on-change-text
|
||||
placeholder
|
||||
max-length
|
||||
default-value]
|
||||
default-value
|
||||
override-theme]
|
||||
:or {max-length 0
|
||||
default-value ""}}]
|
||||
(let [focused? (reagent/atom false)
|
||||
@@ -34,7 +35,7 @@
|
||||
(text/text-style
|
||||
{:size :heading-2
|
||||
:weight :semi-bold
|
||||
:style (style/title-text disabled? blur?)})
|
||||
:style (style/title-text disabled? blur? override-theme)})
|
||||
:default-value default-value
|
||||
:accessibility-label :profile-title-input
|
||||
:on-focus #(swap! focused? (fn [] true))
|
||||
|
||||
@@ -30,16 +30,16 @@
|
||||
{:no-color true})))
|
||||
|
||||
(defn left-section-view
|
||||
[{:keys [on-press icon accessibility-label type icon-override-theme] :or {type :grey}}
|
||||
[{:keys [on-press icon accessibility-label type icon-background-color] :or {type :grey}}
|
||||
put-middle-section-on-left?]
|
||||
[rn/view {:style (when put-middle-section-on-left? {:margin-right 5})}
|
||||
[button/button
|
||||
{:on-press on-press
|
||||
:icon true
|
||||
:type type
|
||||
:size 32
|
||||
:accessibility-label accessibility-label
|
||||
:override-theme icon-override-theme}
|
||||
{:on-press on-press
|
||||
:icon true
|
||||
:type type
|
||||
:size 32
|
||||
:accessibility-label accessibility-label
|
||||
:override-background-color icon-background-color}
|
||||
icon]])
|
||||
|
||||
(defn- mid-section-comp
|
||||
@@ -150,13 +150,15 @@
|
||||
: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]
|
||||
{:keys [icon on-press type style icon-override-theme accessibility-label]
|
||||
:or {type :grey}}]
|
||||
^{:key index}
|
||||
[rn/view
|
||||
{:style (assoc style
|
||||
:margin-right
|
||||
(if (= index last-icon-index) 0 8))}
|
||||
(cond-> {:style (assoc style
|
||||
:margin-right
|
||||
(if (= index last-icon-index) 0 8))}
|
||||
accessibility-label (assoc :accessibility-label accessibility-label
|
||||
:accessible true))
|
||||
[button/button
|
||||
{:on-press on-press
|
||||
:icon true
|
||||
@@ -235,15 +237,18 @@
|
||||
:align-items :center}}
|
||||
(when left-section
|
||||
[left-section-view left-section put-middle-section-on-left?])
|
||||
(when put-middle-section-on-left?
|
||||
[mid-section-view
|
||||
(assoc mid-section-props
|
||||
:left-align? true
|
||||
:description (:description mid-section)
|
||||
:description-color (:description-color mid-section)
|
||||
:description-icon (:description-icon mid-section)
|
||||
:align-mid? align-mid?
|
||||
:description-user-icon (:description-user-icon mid-section))])]
|
||||
(when-not put-middle-section-on-left?
|
||||
[mid-section-view mid-section-props])
|
||||
(when mid-section
|
||||
(cond
|
||||
put-middle-section-on-left?
|
||||
[mid-section-view
|
||||
(assoc mid-section-props
|
||||
:left-align? true
|
||||
:description (:description mid-section)
|
||||
:description-color (:description-color mid-section)
|
||||
:description-icon (:description-icon mid-section)
|
||||
:align-mid? align-mid?
|
||||
:description-user-icon (:description-user-icon mid-section))]
|
||||
|
||||
(not put-middle-section-on-left?)
|
||||
[mid-section-view mid-section-props]))]
|
||||
[right-section-view right-section-buttons]]))
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
[text/text
|
||||
{:style (style/tip-text completed?)
|
||||
:weight :regular
|
||||
:size :paragraph-2} text]
|
||||
:size :paragraph-2}
|
||||
text]
|
||||
(when completed?
|
||||
[rn/view
|
||||
{:style style/strike-through
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn card-container
|
||||
[customization-color padding-bottom]
|
||||
{:flex-direction :column
|
||||
:padding-horizontal 12
|
||||
:padding-top 12
|
||||
:padding-bottom padding-bottom
|
||||
:flex 1
|
||||
:border-radius 16
|
||||
:background-color (colors/custom-color customization-color 50 40)})
|
||||
[{:keys [customization-color padding-bottom border-bottom-radius]}]
|
||||
{:padding-horizontal 12
|
||||
:padding-top 12
|
||||
:padding-bottom padding-bottom
|
||||
:flex 1
|
||||
:border-top-left-radius 16
|
||||
:border-top-right-radius 16
|
||||
:border-bottom-left-radius border-bottom-radius
|
||||
:border-bottom-right-radius border-bottom-radius
|
||||
:background-color (colors/custom-color customization-color 50 40)})
|
||||
|
||||
(def card-header
|
||||
{:flex-direction :row
|
||||
|
||||
@@ -4,77 +4,104 @@
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.components.tags.tag :as tag]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.hole-view :as hole-view]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.avatars.user-avatar.view :as user-avatar]
|
||||
[quo2.components.profile.profile-card.style :as style]))
|
||||
[quo2.components.profile.profile-card.style :as style]
|
||||
[quo2.components.avatars.user-avatar.view :as user-avatar]))
|
||||
|
||||
(defn profile-card
|
||||
[{:keys [key-card? profile-picture name hash customization-color
|
||||
emoji-hash on-options-press show-emoji-hash? padding-bottom
|
||||
show-options-button? show-user-hash? show-logged-in? on-card-press]
|
||||
(defn- profile-card-component
|
||||
[{:keys [keycard-account? profile-picture name hash
|
||||
customization-color emoji-hash on-options-press
|
||||
show-emoji-hash? show-options-button? show-user-hash?
|
||||
show-logged-in? on-card-press login-card? last-item? card-style]
|
||||
:or {show-emoji-hash? false
|
||||
show-user-hash? false
|
||||
customization-color :turquoise
|
||||
show-options-button? false
|
||||
show-logged-in? false
|
||||
key-card? false}}]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press on-card-press
|
||||
:flex 1
|
||||
:accessibility-label :profile-card}
|
||||
[rn/view
|
||||
(style/card-container
|
||||
customization-color
|
||||
(or padding-bottom (if show-emoji-hash? 12 10)))
|
||||
[rn/view
|
||||
{:style style/card-header}
|
||||
[user-avatar/user-avatar
|
||||
{:full-name name
|
||||
:profile-picture profile-picture
|
||||
:override-theme :dark
|
||||
:size :medium
|
||||
:status-indicator? false}]
|
||||
[rn/view {:flex-direction :row}
|
||||
(when show-logged-in?
|
||||
[tag/tag
|
||||
{:type :icon
|
||||
:size 32
|
||||
:blurred? true
|
||||
:labelled? true
|
||||
:resource :main-icons2/check
|
||||
:accessibility-label :logged-in-tag
|
||||
:icon-color colors/success-50
|
||||
keycard-account? false
|
||||
login-card? false
|
||||
last-item? false
|
||||
card-style {:padding-horizontal 20
|
||||
:flex 1}}}]
|
||||
(let [{:keys [width]} (rn/use-window-dimensions)
|
||||
padding-bottom (cond
|
||||
login-card? 38
|
||||
show-emoji-hash? 12
|
||||
:else 10)
|
||||
border-bottom-radius (if (or (not login-card?) last-item?) 16 0)]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press on-card-press
|
||||
:accessibility-label :profile-card}
|
||||
[hole-view/hole-view
|
||||
{:key (str name last-item?) ;; Key is required to force removal of holes
|
||||
:style (merge {:flex-direction :row} card-style)
|
||||
:holes (if (or (not login-card?) last-item?)
|
||||
[]
|
||||
[{:x 20
|
||||
:y 108
|
||||
:width (- width 40)
|
||||
:height 50
|
||||
:borderRadius 16}])}
|
||||
[rn/view
|
||||
{:style (style/card-container
|
||||
{:customization-color customization-color
|
||||
:padding-bottom padding-bottom
|
||||
:border-bottom-radius border-bottom-radius})}
|
||||
[rn/view
|
||||
{:style style/card-header}
|
||||
[user-avatar/user-avatar
|
||||
{:full-name name
|
||||
:profile-picture profile-picture
|
||||
:override-theme :dark
|
||||
:label (i18n/label :t/logged-in)}])
|
||||
(when show-options-button?
|
||||
[button/button
|
||||
{:size 32
|
||||
:type :blur-bg
|
||||
:icon true
|
||||
:override-theme :dark
|
||||
:style style/option-button
|
||||
:on-press on-options-press
|
||||
:accessibility-label :profile-card-options}
|
||||
:i/options])]]
|
||||
[rn/view
|
||||
{:style style/name-container}
|
||||
[text/text
|
||||
{:size :heading-2
|
||||
:weight :semi-bold
|
||||
:number-of-lines 1
|
||||
:style style/user-name} name]
|
||||
(when key-card?
|
||||
(icon/icon
|
||||
:i/keycard
|
||||
style/keycard-icon))]
|
||||
(when show-user-hash?
|
||||
[text/text
|
||||
{:weight :monospace
|
||||
:number-of-lines 1
|
||||
:style style/user-hash} hash])
|
||||
(when (and show-emoji-hash? emoji-hash)
|
||||
[text/text
|
||||
{:weight :monospace
|
||||
:number-of-lines 1
|
||||
:style style/emoji-hash} emoji-hash])]])
|
||||
:size :medium
|
||||
:status-indicator? false
|
||||
:customization-color customization-color}]
|
||||
[rn/view {:flex-direction :row}
|
||||
(when show-logged-in?
|
||||
[tag/tag
|
||||
{:type :icon
|
||||
:size 32
|
||||
:blurred? true
|
||||
:labelled? true
|
||||
:resource :main-icons2/check
|
||||
:accessibility-label :logged-in-tag
|
||||
:icon-color colors/success-50
|
||||
:override-theme :dark
|
||||
:label (i18n/label :t/logged-in)}])
|
||||
(when show-options-button?
|
||||
[button/button
|
||||
{:size 32
|
||||
:type :blur-bg
|
||||
:icon true
|
||||
:override-theme :dark
|
||||
:style style/option-button
|
||||
:on-press on-options-press
|
||||
:accessibility-label :profile-card-options}
|
||||
:i/options])]]
|
||||
[rn/view
|
||||
{:style style/name-container}
|
||||
[text/text
|
||||
{:size :heading-2
|
||||
:weight :semi-bold
|
||||
:number-of-lines 1
|
||||
:style style/user-name} name]
|
||||
(when keycard-account?
|
||||
(icon/icon
|
||||
:i/keycard
|
||||
style/keycard-icon))]
|
||||
(when show-user-hash?
|
||||
[text/text
|
||||
{:weight :monospace
|
||||
:number-of-lines 1
|
||||
:style style/user-hash} hash])
|
||||
(when (and show-emoji-hash? emoji-hash)
|
||||
[text/text
|
||||
{:weight :monospace
|
||||
:number-of-lines 1
|
||||
:style style/emoji-hash} emoji-hash])]]]))
|
||||
|
||||
(defn profile-card
|
||||
[props]
|
||||
[:f> profile-card-component props])
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
(ns quo2.components.selectors.disclaimer.component-spec
|
||||
(:require [quo2.components.selectors.disclaimer.view :as disclaimer]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "Disclaimer tests"
|
||||
(h/test "Default render of toggle component"
|
||||
(h/render [disclaimer/view {:on-change (h/mock-fn)} "test"])
|
||||
(h/is-truthy (h/get-by-label-text :checkbox-off)))
|
||||
|
||||
(h/test "Renders its text"
|
||||
(let [text "I accept this disclaimer"]
|
||||
(h/render [disclaimer/view {} text])
|
||||
(h/is-truthy (h/get-by-text text))))
|
||||
|
||||
(h/test "On change event gets fire after press"
|
||||
(let [mock-fn (h/mock-fn)]
|
||||
(h/render [disclaimer/view {:on-change mock-fn} "test"])
|
||||
(h/fire-event :press (h/get-by-label-text :checkbox-off))
|
||||
(h/was-called mock-fn)))
|
||||
|
||||
(h/describe "It's rendered according to its `checked?` property"
|
||||
(h/test "checked? true"
|
||||
(h/render [disclaimer/view {:checked? true} "test"])
|
||||
(h/is-null (h/query-by-label-text :checkbox-off))
|
||||
(h/is-truthy (h/query-by-label-text :checkbox-on)))
|
||||
(h/test "checked? false"
|
||||
(h/render [disclaimer/view {:checked? false} "test"])
|
||||
(h/is-null (h/query-by-label-text :checkbox-on))
|
||||
(h/is-truthy (h/query-by-label-text :checkbox-off)))))
|
||||
@@ -2,14 +2,16 @@
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn container
|
||||
[]
|
||||
{:flex-direction :row
|
||||
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80-opa-40)
|
||||
:padding 11
|
||||
:align-self :stretch
|
||||
:border-radius 12
|
||||
:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-70)})
|
||||
[blur?]
|
||||
(let [dark-background (if blur? colors/white-opa-5 colors/neutral-80-opa-40)
|
||||
dark-border (if blur? colors/white-opa-10 colors/neutral-70)]
|
||||
{:flex-direction :row
|
||||
:background-color (colors/theme-colors colors/neutral-5 dark-background)
|
||||
:padding 11
|
||||
:align-self :stretch
|
||||
:border-radius 12
|
||||
:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 dark-border)}))
|
||||
|
||||
(def text
|
||||
{:margin-left 8})
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn view
|
||||
[{:keys [on-change accessibility-label container-style]} label]
|
||||
[{:keys [checked? blur? on-change accessibility-label container-style]} label]
|
||||
[rn/view
|
||||
{:style (merge container-style (style/container))}
|
||||
{:style (merge container-style (style/container blur?))}
|
||||
[selectors/checkbox
|
||||
{:accessibility-label accessibility-label
|
||||
:on-change on-change}]
|
||||
:on-change on-change
|
||||
:checked? checked?}]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:style style/text}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def themes-for-blur
|
||||
{:light {:background-color colors/neutral-80-opa-5}
|
||||
:dark {:background-color colors/white-opa-5}})
|
||||
|
||||
(def themes
|
||||
{:light {:background-color colors/neutral-20}
|
||||
:dark {:background-color colors/neutral-80}})
|
||||
@@ -12,11 +16,12 @@
|
||||
(defn segmented-control
|
||||
[{:keys [default-active on-change]}]
|
||||
(let [active-tab-id (reagent/atom default-active)]
|
||||
(fn [{:keys [data size]}]
|
||||
(fn [{:keys [data size override-theme blur?]}]
|
||||
(let [active-id @active-tab-id]
|
||||
[rn/view
|
||||
{:flex-direction :row
|
||||
:background-color (get-in themes [(theme/get-theme) :background-color])
|
||||
:background-color (get-in (if blur? themes-for-blur themes)
|
||||
[(or override-theme (theme/get-theme)) :background-color])
|
||||
:border-radius (case size
|
||||
32 10
|
||||
28 8
|
||||
@@ -29,12 +34,14 @@
|
||||
{:margin-left (if (= 0 indx) 0 2)
|
||||
:flex 1}
|
||||
[tab/view
|
||||
{:id id
|
||||
:segmented? true
|
||||
:size size
|
||||
:active (= id active-id)
|
||||
:on-press (fn [tab-id]
|
||||
(reset! active-tab-id tab-id)
|
||||
(when on-change
|
||||
(on-change tab-id)))}
|
||||
{:id id
|
||||
:segmented? true
|
||||
:size size
|
||||
:override-theme override-theme
|
||||
:blur? blur?
|
||||
:active (= id active-id)
|
||||
:on-press (fn [tab-id]
|
||||
(reset! active-tab-id tab-id)
|
||||
(when on-change
|
||||
(on-change tab-id)))}
|
||||
label]])]))))
|
||||
|
||||
@@ -79,11 +79,12 @@
|
||||
[notification-dot/notification-dot
|
||||
{:style style/notification-dot}])
|
||||
[rn/view
|
||||
{:style (style/tab {:size size
|
||||
:disabled disabled
|
||||
:segmented? segmented?
|
||||
:background-color background-color
|
||||
:show-notification-dot? show-notification-dot?})}
|
||||
{:style (style/tab
|
||||
{:size size
|
||||
:disabled disabled
|
||||
:segmented? segmented?
|
||||
:background-color (if (and segmented? (not active)) :transparent background-color)
|
||||
:show-notification-dot? show-notification-dot?})}
|
||||
(when before
|
||||
[rn/view
|
||||
[icons/icon before {:color icon-color}]])
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
quo2.components.settings.accounts.view
|
||||
quo2.components.settings.privacy-option
|
||||
quo2.components.onboarding.small-option-card.view
|
||||
quo2.components.tabs.segmented-tab
|
||||
quo2.components.tabs.account-selector
|
||||
quo2.components.tabs.tabs
|
||||
quo2.components.tags.context-tags
|
||||
@@ -91,6 +92,7 @@
|
||||
(def audio-tag quo2.components.tags.context-tags/audio-tag)
|
||||
(def community-tag quo2.components.tags.context-tags/community-tag)
|
||||
(def tabs quo2.components.tabs.tabs/tabs)
|
||||
(def segmented-control quo2.components.tabs.segmented-tab/segmented-control)
|
||||
(def account-selector quo2.components.tabs.account-selector/account-selector)
|
||||
(def floating-shell-button quo2.components.navigation.floating-shell-button/floating-shell-button)
|
||||
(def page-nav quo2.components.navigation.page-nav/page-nav)
|
||||
|
||||
@@ -2,21 +2,23 @@
|
||||
(:require [quo2.components.avatars.user-avatar.component-spec]
|
||||
[quo2.components.banners.banner.component-spec]
|
||||
[quo2.components.buttons.--tests--.buttons-component-spec]
|
||||
[quo2.components.colors.color-picker.component-spec]
|
||||
[quo2.components.counter.--tests--.counter-component-spec]
|
||||
[quo2.components.dividers.--tests--.divider-label-component-spec]
|
||||
[quo2.components.dividers.strength-divider.component-spec]
|
||||
[quo2.components.drawers.action-drawers.component-spec]
|
||||
[quo2.components.drawers.drawer-buttons.component-spec]
|
||||
[quo2.components.drawers.permission-context.component-spec]
|
||||
[quo2.components.colors.color-picker.component-spec]
|
||||
[quo2.components.inputs.input.component-spec]
|
||||
[quo2.components.inputs.profile-input.component-spec]
|
||||
[quo2.components.inputs.title-input.component-spec]
|
||||
[quo2.components.markdown.--tests--.text-component-spec]
|
||||
[quo2.components.onboarding.small-option-card.component-spec]
|
||||
[quo2.components.password.tips.component-spec]
|
||||
[quo2.components.profile.select-profile.component-spec]
|
||||
[quo2.components.record-audio.record-audio.--tests--.record-audio-component-spec]
|
||||
[quo2.components.record-audio.soundtrack.--tests--.soundtrack-component-spec]
|
||||
[quo2.components.profile.select-profile.component-spec]
|
||||
[quo2.components.selectors.--tests--.selectors-component-spec]
|
||||
[quo2.components.selectors.disclaimer.component-spec]
|
||||
[quo2.components.selectors.filter.component-spec]
|
||||
[quo2.components.tags.--tests--.status-tags-component-spec]))
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
;;100 with transparency
|
||||
(def neutral-100-opa-0 (alpha neutral-100 0))
|
||||
(def neutral-100-opa-10 (alpha neutral-100 0.1))
|
||||
(def neutral-100-opa-30 (alpha neutral-100 0.3))
|
||||
(def neutral-100-opa-60 (alpha neutral-100 0.6))
|
||||
(def neutral-100-opa-70 (alpha neutral-100 0.7))
|
||||
(def neutral-100-opa-80 (alpha neutral-100 0.8))
|
||||
@@ -126,6 +127,7 @@
|
||||
|
||||
;;Solid
|
||||
(def black "#000000")
|
||||
(def onboarding-header-black "#000716")
|
||||
|
||||
;;;;Primary
|
||||
|
||||
@@ -236,7 +238,11 @@
|
||||
([color suffix]
|
||||
(custom-color color suffix nil))
|
||||
([color suffix opacity]
|
||||
(let [base-color (get-in colors-map [(keyword color) suffix])]
|
||||
(let [color-keyword (keyword color)
|
||||
base-color (get-in colors-map
|
||||
[(if (= color-keyword :yinyang)
|
||||
(if (theme/dark?) :yang :yin)
|
||||
(keyword color)) suffix])]
|
||||
(if opacity (alpha base-color (/ opacity 100)) base-color))))))
|
||||
|
||||
(defn custom-color-by-theme
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
(ns react-native.camera-kit
|
||||
(:require ["react-native-camera-kit" :refer (CameraKitCamera)]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def camera (reagent/adapt-react-class CameraKitCamera))
|
||||
@@ -6,3 +6,7 @@
|
||||
(let [kb (.useKeyboard hooks)]
|
||||
{:keyboard-shown (.-keyboardShown ^js kb)
|
||||
:keyboard-height (.-keyboardHeight ^js kb)}))
|
||||
|
||||
(defn use-back-handler
|
||||
[handler]
|
||||
(.useBackHandler hooks handler))
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
withDelay
|
||||
withSpring
|
||||
withRepeat
|
||||
withSequence
|
||||
withDecay
|
||||
Easing
|
||||
Keyframe
|
||||
@@ -17,10 +18,12 @@
|
||||
SlideOutUp
|
||||
LinearTransition)]
|
||||
[reagent.core :as reagent]
|
||||
["react-native-redash" :refer (withPause)]
|
||||
[react-native.flat-list :as rn-flat-list]
|
||||
[utils.collection]
|
||||
[utils.worklets.core :as worklets.core]))
|
||||
|
||||
(def ^:const default-duration 300)
|
||||
|
||||
;; Animations
|
||||
(def slide-in-up-animation SlideInUp)
|
||||
(def slide-out-up-animation SlideOutUp)
|
||||
@@ -57,6 +60,8 @@
|
||||
(def with-decay withDecay)
|
||||
(def key-frame Keyframe)
|
||||
(def with-repeat withRepeat)
|
||||
(def with-sequence withSequence)
|
||||
(def with-pause withPause)
|
||||
(def cancel-animation cancelAnimation)
|
||||
|
||||
;; Easings
|
||||
@@ -65,6 +70,7 @@
|
||||
(def in-out
|
||||
(.-inOut ^js Easing))
|
||||
|
||||
;; trying to put default-easing inside easings map causes test to fail
|
||||
(defn default-easing [] (in-out (.-quad ^js Easing)))
|
||||
|
||||
(def easings
|
||||
@@ -115,13 +121,15 @@
|
||||
(js-obj "duration" duration
|
||||
"easing" (get easings easing))))))
|
||||
|
||||
(defn animate-shared-value-with-delay-default-easing
|
||||
[anim val duration delay]
|
||||
(set-shared-value anim
|
||||
(with-delay delay
|
||||
(with-timing val
|
||||
(js-obj "duration" duration
|
||||
"easing" (in-out (.-quad ^js Easing)))))))
|
||||
(defn animate-delay
|
||||
([animation val delay]
|
||||
(animate-delay animation val delay default-duration))
|
||||
([animation val delay duration]
|
||||
(set-shared-value animation
|
||||
(with-delay delay
|
||||
(with-timing val
|
||||
(clj->js {:duration duration
|
||||
:easing (default-easing)}))))))
|
||||
|
||||
(defn animate-shared-value-with-repeat
|
||||
[anim val duration easing number-of-repetitions reverse?]
|
||||
|
||||
@@ -147,8 +147,9 @@
|
||||
|
||||
(defn build-image-messages
|
||||
[{db :db} chat-id input-text]
|
||||
(let [images (get-in db [:chat/inputs chat-id :metadata :sending-image])
|
||||
album-id (str (random-uuid))]
|
||||
(let [images (get-in db [:chat/inputs chat-id :metadata :sending-image])
|
||||
{:keys [message-id]} (get-in db [:chat/inputs chat-id :metadata :responding-to-message])
|
||||
album-id (str (random-uuid))]
|
||||
(mapv (fn [[_ {:keys [resized-uri width height]}]]
|
||||
{:chat-id chat-id
|
||||
:album-id album-id
|
||||
@@ -159,7 +160,8 @@
|
||||
;; TODO: message not received if text field is
|
||||
;; nil or empty, issue:
|
||||
;; https://github.com/status-im/status-mobile/issues/14754
|
||||
:text (or input-text "placeholder")})
|
||||
:text (or input-text "placeholder")
|
||||
:response-to message-id})
|
||||
images)))
|
||||
|
||||
(rf/defn clean-input
|
||||
|
||||
@@ -276,7 +276,7 @@
|
||||
(vals (get-in db [:communities community-id :chats])))]
|
||||
(when (and id
|
||||
(not= (:current-chat-id db) (str community-id id)))
|
||||
(chat.events/navigate-to-chat cofx (str community-id id) nil))))
|
||||
(chat.events/navigate-to-chat cofx (str community-id id)))))
|
||||
|
||||
(rf/defn fetch
|
||||
[_]
|
||||
@@ -817,7 +817,7 @@
|
||||
[cofx community-id]
|
||||
(rf/merge cofx
|
||||
(navigation/pop-to-root :shell-stack)
|
||||
(navigation/navigate-to-nav2 :community community-id true)))
|
||||
(navigation/navigate-to-cofx :community-overview community-id)))
|
||||
|
||||
(rf/defn member-role-updated
|
||||
{:events [:community.member/role-updated]}
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
(defn words-count
|
||||
[s]
|
||||
(if (empty? s)
|
||||
nil
|
||||
0
|
||||
(-> s
|
||||
passphrase->words
|
||||
count)))
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
status-im2.contexts.activity-center.events
|
||||
status-im2.contexts.activity-center.notification.contact-requests.events
|
||||
status-im2.contexts.shell.events
|
||||
status-im2.contexts.onboarding.events
|
||||
status-im.chat.models.gaps
|
||||
[status-im2.navigation.events :as navigation]))
|
||||
|
||||
@@ -116,7 +117,7 @@
|
||||
(let [current-theme-type (get-in cofx [:db :multiaccount :appearance])]
|
||||
(when (and (multiaccounts.model/logged-in? cofx)
|
||||
(= current-theme-type status-im2.constants/theme-type-system))
|
||||
{:multiaccounts.ui/switch-theme
|
||||
{:multiaccounts.ui/switch-theme-fx
|
||||
[(get-in db [:multiaccount :appearance])
|
||||
(:view-id db) true]})))
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
{:events [:navigate-chat-updated]}
|
||||
[cofx chat-id]
|
||||
(when (get-in cofx [:db :chats chat-id])
|
||||
(chat.events/navigate-to-chat cofx chat-id nil)))
|
||||
(chat.events/navigate-to-chat cofx chat-id)))
|
||||
|
||||
(rf/defn handle-chat-removed
|
||||
{:events [:chat-removed]}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
[{:keys [db] :as cofx}]
|
||||
(rf/merge cofx
|
||||
{:db db}
|
||||
(navigation/pop-to-root :multiaccounts-stack)))
|
||||
(navigation/pop-to-root :profiles)))
|
||||
|
||||
(rf/defn login-pin-more-icon-pressed
|
||||
{:events [:keycard.login.pin.ui/more-icon-pressed]}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns status-im.mobile-sync-settings.core
|
||||
(:require [status-im2.common.bottom-sheet.events :as bottom-sheet]
|
||||
[status-im2.contexts.add-new-contact.events :as add-new-contact]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
@@ -42,7 +43,8 @@
|
||||
(and logged-in? initialized?)
|
||||
[(mailserver/process-next-messages-request)
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(wallet/restart-wallet-service nil)]
|
||||
(wallet/restart-wallet-service nil)
|
||||
(add-new-contact/set-new-identity-reconnected)]
|
||||
|
||||
logged-in?
|
||||
[(mailserver/process-next-messages-request)
|
||||
|
||||
@@ -5,14 +5,13 @@
|
||||
[status-im2.common.bottom-sheet.events :as bottom-sheet]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.native-module.core :as native-module]
|
||||
[status-im.theme.core :as theme]
|
||||
[utils.re-frame :as rf]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im2.setup.hot-reload :as hot-reload]
|
||||
[status-im2.common.theme.core :as utils.theme]
|
||||
[status-im2.common.theme.core :as theme]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im2.contexts.shell.animation :as shell.animation]
|
||||
[status-im.contact.db :as contact.db]))
|
||||
@@ -137,16 +136,16 @@
|
||||
{::blank-preview-flag-changed private?}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:multiaccounts.ui/switch-theme
|
||||
:multiaccounts.ui/switch-theme-fx
|
||||
(fn [[theme-type view-id reload-ui?]]
|
||||
(let [[theme status-bar-theme nav-bar-color]
|
||||
;; Status bar theme represents status bar icons colors, so opposite to app theme
|
||||
(if (or (= theme-type constants/theme-type-dark)
|
||||
(and (= theme-type constants/theme-type-system)
|
||||
(utils.theme/dark-mode?)))
|
||||
(theme/device-theme-dark?)))
|
||||
[:dark :light colors/neutral-100]
|
||||
[:light :dark colors/white])]
|
||||
(theme/change-theme theme)
|
||||
(theme/set-theme theme)
|
||||
(re-frame/dispatch [:change-root-status-bar-style
|
||||
(if (shell.animation/home-stack-open?) status-bar-theme :light)])
|
||||
(when reload-ui?
|
||||
@@ -158,9 +157,17 @@
|
||||
{:events [:multiaccounts.ui/appearance-switched]}
|
||||
[cofx theme]
|
||||
(rf/merge cofx
|
||||
{:multiaccounts.ui/switch-theme [theme :appearance true]}
|
||||
{:multiaccounts.ui/switch-theme-fx [theme :appearance true]}
|
||||
(multiaccounts.update/multiaccount-update :appearance theme {})))
|
||||
|
||||
(rf/defn switch-theme
|
||||
{:events [:multiaccounts.ui/switch-theme]}
|
||||
[cofx theme view-id]
|
||||
(let [theme (or theme
|
||||
(get-in cofx [:db :multiaccount :appearance])
|
||||
constants/theme-type-dark)]
|
||||
{:multiaccounts.ui/switch-theme-fx [theme view-id false]}))
|
||||
|
||||
(rf/defn switch-profile-picture-show-to
|
||||
{:events [:multiaccounts.ui/profile-picture-show-to-switched]}
|
||||
[cofx id]
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[status-im2.common.log :as logging]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im2.contexts.shell.animation :as shell.animation]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
@@ -338,10 +339,6 @@
|
||||
{:method "permissions_getDappPermissions"
|
||||
:on-success #(re-frame/dispatch [::initialize-dapp-permissions %])}]})
|
||||
|
||||
(rf/defn initialize-appearance
|
||||
[cofx]
|
||||
{:multiaccounts.ui/switch-theme [(get-in cofx [:db :multiaccount :appearance]) nil false]})
|
||||
|
||||
(rf/defn get-group-chat-invitations
|
||||
[_]
|
||||
{:json-rpc/call
|
||||
@@ -393,7 +390,6 @@
|
||||
#(do (re-frame/dispatch [:chats-list/load-success %])
|
||||
(rf/dispatch [:communities/get-user-requests-to-join])
|
||||
(re-frame/dispatch [::get-chats-callback]))})
|
||||
(initialize-appearance)
|
||||
(initialize-wallet-connect)
|
||||
(get-node-config)
|
||||
(communities/fetch)
|
||||
@@ -479,8 +475,17 @@
|
||||
(defn redirect-to-root
|
||||
"Decides which root should be initialised depending on user and app state"
|
||||
[db]
|
||||
(if (get db :tos/accepted?)
|
||||
(cond
|
||||
(get db :local-pairing/completed-pairing?)
|
||||
(re-frame/dispatch [:syncing/pairing-completed])
|
||||
|
||||
(get db :onboarding-2/new-account?)
|
||||
(re-frame/dispatch [:navigate-to :enable-notifications])
|
||||
|
||||
(get db :tos/accepted?)
|
||||
(re-frame/dispatch [:init-root :shell-stack])
|
||||
|
||||
:else
|
||||
(re-frame/dispatch [:init-root :tos])))
|
||||
|
||||
(rf/defn login-only-events
|
||||
@@ -514,10 +519,11 @@
|
||||
tos-accepted? (get db :tos/accepted?)
|
||||
{:networks/keys [current-network networks]} db
|
||||
network-id (str (get-in networks [current-network :config :NetworkId]))]
|
||||
(shell.animation/change-selected-stack-id :communities-stack true)
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(dissoc :multiaccounts/login)
|
||||
(assoc :tos/next-root :onboarding-notification :chats/loading? false)
|
||||
(assoc :tos/next-root :enable-notifications :chats/loading? false)
|
||||
(assoc-in [:multiaccount :multiaccounts/first-account] first-account?))
|
||||
::get-tokens [network-id accounts recovered-account?]}
|
||||
(finish-keycard-setup)
|
||||
@@ -528,7 +534,7 @@
|
||||
(multiaccounts/switch-preview-privacy-mode-flag)
|
||||
(link-preview/request-link-preview-whitelist)
|
||||
(logging/set-log-level (:log-level multiaccount))
|
||||
(navigation/init-root :shell-stack))))
|
||||
(navigation/init-root :enable-notifications))))
|
||||
|
||||
(defn- keycard-setup?
|
||||
[cofx]
|
||||
@@ -631,7 +637,7 @@
|
||||
(assoc-in [:keycard :pin :login] []))})
|
||||
#(if keycard-account?
|
||||
{:init-root-fx :multiaccounts-keycard}
|
||||
{:init-root-fx :multiaccounts})
|
||||
{:init-root-fx :profiles})
|
||||
#(when goto-key-storage?
|
||||
(navigation/navigate-to-cofx % :actions-not-logged-in nil))))))
|
||||
|
||||
@@ -730,8 +736,9 @@
|
||||
keycard-multiaccount? (boolean (:keycard-pairing multiaccount))]
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (update db :keycard dissoc :application-info)
|
||||
:navigate-to-fx (if keycard-multiaccount? :keycard-login-pin :login)}
|
||||
(merge
|
||||
{:db (update db :keycard dissoc :application-info)}
|
||||
(when keycard-multiaccount? {:navigate-to-fx :keycard-login-pin}))
|
||||
(open-login (select-keys multiaccount [:key-uid :name :public-key :identicon :images])))))
|
||||
|
||||
(rf/defn hide-keycard-banner
|
||||
|
||||
@@ -91,6 +91,14 @@
|
||||
key-uid
|
||||
#(.loginWithConfig ^js (status) account-data hashed-password config))))
|
||||
|
||||
(defn create-account-and-login
|
||||
[request]
|
||||
(.createAccountAndLogin ^js (status) (types/clj->json request)))
|
||||
|
||||
(defn restore-account-and-login
|
||||
[request]
|
||||
(.restoreAccountAndLogin ^js (status) (types/clj->json request)))
|
||||
|
||||
(defn export-db
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
[key-uid account-data hashed-password callback]
|
||||
@@ -611,3 +619,15 @@
|
||||
current-password#
|
||||
new-password
|
||||
callback))
|
||||
|
||||
(defn backup-disabled-data-dir
|
||||
[]
|
||||
(.backupDisabledDataDir ^js (status)))
|
||||
|
||||
(defn keystore-dir
|
||||
[]
|
||||
(.keystoreDir ^js (status)))
|
||||
|
||||
(defn log-file-path
|
||||
[]
|
||||
(.logFilePath ^js (status)))
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
[status-im.visibility-status-updates.core :as visibility-status-updates]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im2.contexts.chat.messages.link-preview.events :as link-preview]
|
||||
[taoensso.timbre :as log]))
|
||||
[taoensso.timbre :as log]
|
||||
[status-im2.constants :as constants]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]))
|
||||
|
||||
(rf/defn status-node-started
|
||||
[{db :db :as cofx} {:keys [error]}]
|
||||
@@ -53,10 +56,40 @@
|
||||
:peer-stats peer-stats
|
||||
:peers-count (count (:peers peer-stats)))}))
|
||||
|
||||
(defn handle-local-pairing-signals
|
||||
[event]
|
||||
(log/debug "local pairing signal received"
|
||||
{:event event}))
|
||||
(rf/defn handle-local-pairing-signals
|
||||
[{:keys [db] :as cofx} event]
|
||||
(log/info "local pairing signal received"
|
||||
{:event event})
|
||||
(let [connection-success? (= (:type event)
|
||||
constants/local-pairing-event-connection-success)
|
||||
error-on-pairing? (contains? constants/local-pairing-event-errors (:type event))
|
||||
completed-pairing? (and (= (:type event)
|
||||
constants/local-pairing-event-process-success)
|
||||
(= (:action event)
|
||||
constants/local-pairing-action-pairing-account))
|
||||
logged-in? (multiaccounts.model/logged-in? cofx)
|
||||
;; since `connection-success` event is received on both sender and receiver devices
|
||||
;; we check the `logged-in?` status to identify the receiver and take the user to next screen
|
||||
navigate-to-syncing-devices? (and connection-success? (not logged-in?))
|
||||
user-in-syncing-devices-screen? (= (:view-id db) :syncing-devices)]
|
||||
(merge {:db (cond-> db
|
||||
connection-success?
|
||||
(assoc :local-pairing/completed-pairing? false)
|
||||
|
||||
error-on-pairing?
|
||||
(dissoc :local-pairing/completed-pairing?)
|
||||
|
||||
completed-pairing?
|
||||
(assoc :local-pairing/completed-pairing? true))}
|
||||
(when navigate-to-syncing-devices?
|
||||
{:dispatch [:navigate-to :syncing-devices]})
|
||||
(when (and error-on-pairing? user-in-syncing-devices-screen?)
|
||||
{:dispatch-n [[:toasts/upsert
|
||||
{:icon :i/info
|
||||
:icon-color colors/danger-50
|
||||
:override-theme :light
|
||||
:text (i18n/label :t/error-syncing-connection-failed)}]
|
||||
[:navigate-back]]}))))
|
||||
|
||||
(rf/defn process
|
||||
{:events [:signals/signal-received]}
|
||||
@@ -110,5 +143,6 @@
|
||||
"status.updates.timedout" (visibility-status-updates/handle-visibility-status-updates
|
||||
cofx
|
||||
(js->clj event-js :keywordize-keys true))
|
||||
"localPairing" (handle-local-pairing-signals (js->clj event-js :keywordize-keys true))
|
||||
"localPairing" (handle-local-pairing-signals cofx
|
||||
(js->clj event-js :keywordize-keys true))
|
||||
(log/debug "Event " type " not handled"))))
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
(ns status-im.theme.core
|
||||
(:require [quo.theme :as quo.theme]
|
||||
[quo2.theme :as quo2.theme]))
|
||||
|
||||
(defn change-theme
|
||||
[theme]
|
||||
(quo.theme/set-theme theme)
|
||||
(quo2.theme/set-theme theme))
|
||||
@@ -48,7 +48,7 @@
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:communities/load-category-states id])
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch [:navigate-to-nav2 :community {:community-id id}]))
|
||||
(rf/dispatch [:navigate-to :community-overview id]))
|
||||
:on-long-press #(rf/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[community/community-actions community])}])}
|
||||
@@ -111,7 +111,7 @@
|
||||
(i18n/label :t/open-membership))]]
|
||||
:on-press #(do
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch [:navigate-to-nav2 :community {:community-id id}]))}]))
|
||||
(rf/dispatch [:navigate-to :community-overview id]))}]))
|
||||
|
||||
(defn communities-actions
|
||||
[]
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
[]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:on-press #(hide-sheet-and-dispatch [:generate-and-derive-addresses])
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :intro])
|
||||
:icon :main-icons/add
|
||||
:accessibility-label :generate-a-new-key
|
||||
:title (i18n/label :t/generate-a-new-key)}])
|
||||
|
||||
@@ -88,14 +88,14 @@
|
||||
|
||||
(defn valid-nickname?
|
||||
[nickname]
|
||||
(not (string/blank? nickname)))
|
||||
(not (string/blank? (string/trim (or nickname "")))))
|
||||
|
||||
(defn- nickname-input
|
||||
[nickname entered-nickname public-key]
|
||||
[quo/text-input
|
||||
{:on-change-text #(reset! entered-nickname %)
|
||||
:on-submit-editing #(when (valid-nickname? @entered-nickname)
|
||||
(save-nickname public-key @entered-nickname))
|
||||
(save-nickname public-key (string/trim (or @entered-nickname ""))))
|
||||
:auto-capitalize :none
|
||||
:auto-focus false
|
||||
:max-length 32
|
||||
@@ -131,7 +131,9 @@
|
||||
:center
|
||||
[quo/button
|
||||
{:type :secondary
|
||||
:on-press #(save-nickname public-key @entered-nickname)}
|
||||
:disabled (not (valid-nickname? @entered-nickname))
|
||||
:on-press #(when (valid-nickname? @entered-nickname)
|
||||
(save-nickname public-key (string/trim (or @entered-nickname ""))))}
|
||||
(i18n/label :t/done)]}]])))
|
||||
|
||||
(defn button-item
|
||||
|
||||
@@ -377,14 +377,14 @@
|
||||
:height 40}}]
|
||||
[communities.icon/community-icon community])]
|
||||
[rn/view {:padding-right 14 :flex 1}
|
||||
[rn/text {:style {:font-weight "700" :font-size 17}}
|
||||
[rn/text {:style {:font-weight "700" :font-size 17 :color quo.colors/black}}
|
||||
name]
|
||||
[rn/text description]]]
|
||||
[rn/text {:style {:color quo.colors/black}} description]]]
|
||||
[rn/view (style/community-view-button)
|
||||
[rn/touchable-opacity
|
||||
{:on-press #(re-frame/dispatch
|
||||
[:communities/navigate-to-community
|
||||
{:community-id (:id community)}])}
|
||||
(:id community)])}
|
||||
[rn/text
|
||||
{:style {:text-align :center
|
||||
:color quo.colors/blue}} (i18n/label :t/view)]]]])))
|
||||
|
||||
@@ -126,6 +126,11 @@
|
||||
(when (string? s)
|
||||
(string/replace s m r)))
|
||||
|
||||
(defn safe-nth
|
||||
[coll index]
|
||||
(when (number? index)
|
||||
(nth coll index)))
|
||||
|
||||
(defn svg?
|
||||
[some-string]
|
||||
(string/ends-with? some-string ".svg"))
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[status-im.ui.screens.multiaccounts.key-storage.views :as key-storage]
|
||||
[status-im.ui.screens.multiaccounts.recover.views :as recover.views]
|
||||
[status-im2.common.bottom-sheet.view :as bottom-sheet]
|
||||
[status-im2.contexts.chat.messages.pin.list.view :as pin.list]
|
||||
[status-im2.contexts.chat.menus.pinned-messages.view :as pinned-messages-menu]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn bottom-sheet
|
||||
@@ -50,7 +50,8 @@
|
||||
(merge key-storage/migrate-account-password)
|
||||
|
||||
(= view :pinned-messages-list)
|
||||
(merge {:content pin.list/pinned-messages-list}))]
|
||||
(merge {:content pinned-messages-menu/pinned-messages
|
||||
:bottom-safe-area-spacing? false}))]
|
||||
[:f>
|
||||
(fn []
|
||||
(rn/use-effect (fn []
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
(ns status-im2.common.bottom-sheet-screen.style
|
||||
(:require
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(defn background
|
||||
[opacity]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:opacity opacity}
|
||||
{:background-color colors/neutral-100-opa-70
|
||||
:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0}))
|
||||
|
||||
(defn main-view
|
||||
[translate-y]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:transform [{:translate-y translate-y}]}
|
||||
{:background-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20
|
||||
:flex 1
|
||||
:overflow :hidden}))
|
||||
|
||||
(def handle-container
|
||||
{:left 0
|
||||
:right 0
|
||||
:top 0
|
||||
:height 20
|
||||
:z-index 1
|
||||
:position :absolute
|
||||
:justify-content :center
|
||||
:align-items :center})
|
||||
|
||||
(defn handle
|
||||
[]
|
||||
{:width 32
|
||||
:height 4
|
||||
:border-radius 100
|
||||
:background-color (colors/theme-colors colors/neutral-100-opa-30 colors/white-opa-30)})
|
||||
@@ -0,0 +1,81 @@
|
||||
(ns status-im2.common.bottom-sheet-screen.view
|
||||
(:require
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.hooks :as hooks]
|
||||
[react-native.navigation :as navigation]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[oops.core :as oops]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im2.common.bottom-sheet-screen.style :as style]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def ^:const drag-threshold 100)
|
||||
|
||||
(defn drag-gesture
|
||||
[translate-y opacity scroll-enabled curr-scroll]
|
||||
(->
|
||||
(gesture/gesture-pan)
|
||||
(gesture/on-start (fn [e]
|
||||
(when (< (oops/oget e "velocityY") 0)
|
||||
(reset! scroll-enabled true))))
|
||||
(gesture/on-update (fn [e]
|
||||
(let [translation (oops/oget e "translationY")
|
||||
progress (Math/abs (/ translation drag-threshold))]
|
||||
(when (pos? translation)
|
||||
(reanimated/set-shared-value translate-y translation)
|
||||
(reanimated/set-shared-value opacity (- 1 (/ progress 5)))))))
|
||||
(gesture/on-end (fn [e]
|
||||
(if (> (oops/oget e "translationY") drag-threshold)
|
||||
(do
|
||||
(reanimated/set-shared-value opacity (reanimated/with-timing-duration 0 100))
|
||||
(rf/dispatch [:navigate-back]))
|
||||
(do
|
||||
(reanimated/set-shared-value opacity (reanimated/with-timing 1))
|
||||
(reanimated/set-shared-value translate-y (reanimated/with-timing 0))
|
||||
(reset! scroll-enabled true)))))
|
||||
(gesture/on-finalize (fn [e]
|
||||
(when (and (>= (oops/oget e "velocityY") 0)
|
||||
(<= @curr-scroll (if platform/ios? -1 0)))
|
||||
(reset! scroll-enabled false))))))
|
||||
|
||||
(defn on-scroll
|
||||
[e curr-scroll]
|
||||
(let [y (oops/oget e "nativeEvent.contentOffset.y")]
|
||||
(reset! curr-scroll y)))
|
||||
|
||||
(defn view
|
||||
[content skip-background?]
|
||||
[:f>
|
||||
(let [scroll-enabled (reagent/atom true)
|
||||
curr-scroll (atom 0)]
|
||||
(fn []
|
||||
(let [sb-height (navigation/status-bar-height)
|
||||
insets (safe-area/use-safe-area)
|
||||
padding-top (Math/max sb-height (:top insets))
|
||||
padding-top (if platform/ios? padding-top (+ padding-top 10))
|
||||
opacity (reanimated/use-shared-value 0)
|
||||
translate-y (reanimated/use-shared-value 0)
|
||||
close (fn []
|
||||
(reanimated/set-shared-value opacity (reanimated/with-timing-duration 0 100))
|
||||
(rf/dispatch [:navigate-back]))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(reanimated/animate-delay opacity 1 (if platform/ios? 300 100))))
|
||||
(hooks/use-back-handler close)
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:padding-top padding-top}}
|
||||
(when-not skip-background?
|
||||
[reanimated/view {:style (style/background opacity)}])
|
||||
[gesture/gesture-detector
|
||||
{:gesture (drag-gesture translate-y opacity scroll-enabled curr-scroll)}
|
||||
[reanimated/view {:style (style/main-view translate-y)}
|
||||
[rn/view {:style style/handle-container}
|
||||
[rn/view {:style (style/handle)}]]
|
||||
[content
|
||||
{:close close
|
||||
:scroll-enabled @scroll-enabled
|
||||
:on-scroll #(on-scroll % curr-scroll)}]]]])))])
|
||||
@@ -26,18 +26,23 @@
|
||||
[quo/text {:style {:margin-left 10}} extra-text]]))
|
||||
|
||||
(defn confirmation-drawer
|
||||
[{:keys [title description context button-text on-press extra-action extra-text accessibility-label]}]
|
||||
[{:keys [title description context button-text on-press extra-action extra-text accessibility-label
|
||||
close-button-text]}]
|
||||
(let [extra-action-selected? (reagent/atom false)]
|
||||
(fn []
|
||||
(let [{:keys [group-chat chat-id public-key color name]} context
|
||||
id (or chat-id public-key)
|
||||
display-name
|
||||
(if-not group-chat (first (rf/sub [:contacts/contact-two-names-by-identity id])) name)
|
||||
contact (when-not group-chat
|
||||
(rf/sub [:contacts/contact-by-address
|
||||
id]))
|
||||
photo-path (when-not (empty? (:images contact))
|
||||
(rf/sub [:chats/photo-path id]))]
|
||||
(let [{:keys [group-chat chat-id public-key color profile-picture
|
||||
name]} context
|
||||
id (or chat-id public-key)
|
||||
display-name (or
|
||||
name
|
||||
(when-not group-chat
|
||||
(rf/sub [:contacts/contact-name-by-identity id])))
|
||||
contact (when-not group-chat
|
||||
(rf/sub [:contacts/contact-by-address
|
||||
id]))
|
||||
photo-path (or profile-picture
|
||||
(when-not (empty? (:images contact))
|
||||
(rf/sub [:chats/photo-path id])))]
|
||||
[rn/view
|
||||
{:style {:margin-horizontal 20}
|
||||
:accessibility-label accessibility-label}
|
||||
@@ -57,7 +62,7 @@
|
||||
{:type :grey
|
||||
:style {:flex 0.48} ;;WUT? 0.48 , whats that ?
|
||||
:on-press #(rf/dispatch [:bottom-sheet/hide])}
|
||||
(i18n/label :t/close)]
|
||||
(or close-button-text (i18n/label :t/close))]
|
||||
[quo/button
|
||||
{:type :danger
|
||||
:style {:flex 0.48}
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
(ns status-im2.common.resources)
|
||||
|
||||
(def ui
|
||||
{:add-new-contact (js/require "../resources/images/ui2/add-contact.png")
|
||||
:intro-1 (js/require "../resources/images/ui2/intro-1.png")
|
||||
:intro-2 (js/require "../resources/images/ui2/intro-2.png")
|
||||
:intro-3 (js/require "../resources/images/ui2/intro-3.png")
|
||||
:intro-4 (js/require "../resources/images/ui2/intro-4.png")
|
||||
: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")
|
||||
:onboarding-bg-1 (js/require "../resources/images/ui2/onboarding-bg-1.png")
|
||||
:onboarding-blur-bg (js/require "../resources/images/ui2/onboarding_blur_bg.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")})
|
||||
{:add-new-contact (js/require "../resources/images/ui2/add-contact.png")
|
||||
: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")
|
||||
:use-keycard (js/require "../resources/images/ui2/keycard.png")
|
||||
:onboarding-illustration (js/require "../resources/images/ui2/onboarding_illustration.png")})
|
||||
|
||||
(def mock-images
|
||||
{:coinbase (js/require "../resources/images/mock2/coinbase.png")
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
(ns status-im2.common.theme.core
|
||||
(:require [react-native.core :as rn]))
|
||||
(:require [quo.theme :as quo]
|
||||
[quo2.theme :as quo2]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(def initial-mode (atom (rn/get-color-scheme)))
|
||||
(def device-theme (atom (rn/get-color-scheme)))
|
||||
|
||||
;; Note - don't use value returned by change listener
|
||||
;; https://github.com/facebook/react-native/issues/28525
|
||||
(defn add-mode-change-listener
|
||||
(defn add-device-theme-change-listener
|
||||
[callback]
|
||||
(rn/appearance-add-change-listener #(let [mode (rn/get-color-scheme)]
|
||||
(when-not (= mode @initial-mode)
|
||||
(reset! initial-mode mode)
|
||||
(callback (keyword mode))))))
|
||||
(rn/appearance-add-change-listener #(let [theme (rn/get-color-scheme)]
|
||||
(when-not (= theme @device-theme)
|
||||
(reset! device-theme theme)
|
||||
(callback (keyword theme))))))
|
||||
|
||||
(defn dark-mode?
|
||||
(defn device-theme-dark?
|
||||
[]
|
||||
(= @initial-mode "dark"))
|
||||
(= @device-theme "dark"))
|
||||
|
||||
(defn set-theme
|
||||
[value]
|
||||
(quo/set-theme value)
|
||||
(quo2/set-theme value))
|
||||
|
||||
@@ -90,6 +90,9 @@
|
||||
(def ^:const command-state-transaction-pending 6)
|
||||
(def ^:const command-state-transaction-sent 7)
|
||||
|
||||
(def ^:const profile-default-color :blue)
|
||||
(def ^:const profile-name-max-length 24)
|
||||
|
||||
(def ^:const profile-pictures-show-to-contacts-only 1)
|
||||
(def ^:const profile-pictures-show-to-everyone 2)
|
||||
(def ^:const profile-pictures-show-to-none 3)
|
||||
@@ -260,6 +263,27 @@
|
||||
An example of a connection string is -> cs2:5vd6J6:Jfc:27xMmHKEYwzRGXcvTtuiLZFfXscMx4Mz8d9wEHUxDj4p7:EG7Z13QScfWBJNJ5cprszzDQ5fBVsYMirXo8MaQFJvpF:3 "
|
||||
"cs")
|
||||
|
||||
;; sender and receiver events
|
||||
(def ^:const local-pairing-event-connection-success "connection-success")
|
||||
(def ^:const local-pairing-event-connection-error "connection-error")
|
||||
(def ^:const local-pairing-event-transfer-success "transfer-success")
|
||||
(def ^:const local-pairing-event-transfer-error "transfer-error")
|
||||
|
||||
;; receiver events
|
||||
(def ^:const local-pairing-event-received-amount "received-account")
|
||||
(def ^:const local-pairing-event-process-success "process-success")
|
||||
(def ^:const local-pairing-event-process-error "process-error")
|
||||
|
||||
(def ^:const local-pairing-event-errors
|
||||
#{local-pairing-event-connection-error
|
||||
local-pairing-event-transfer-error
|
||||
local-pairing-event-process-error})
|
||||
|
||||
(def ^:const local-pairing-action-connect 1)
|
||||
(def ^:const local-pairing-action-pairing-account 2)
|
||||
(def ^:const local-pairing-action-sync-device 3)
|
||||
(def ^:const local-pairing-action-pairing-installation 4)
|
||||
|
||||
(def ^:const serialization-key
|
||||
"We pass this serialization key as a parameter to MultiformatSerializePublicKey
|
||||
function at status-go, This key determines the output base of the serialization.
|
||||
@@ -292,3 +316,10 @@
|
||||
(def ^:const everyone-mention-id "0x00001")
|
||||
|
||||
(def ^:const empty-category-id :communities/not-categorized)
|
||||
|
||||
(def ^:const seed-phrase-valid-length #{12 18 24})
|
||||
|
||||
(def ^:const auth-method-password "password")
|
||||
(def ^:const auth-method-biometric "biometric")
|
||||
(def ^:const auth-method-biometric-prepare "biometric-prepare")
|
||||
(def ^:const auth-method-none "none")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
(ns status-im2.contexts.activity-center.notification.contact-requests.events
|
||||
(:require [status-im2.contexts.activity-center.events :as ac-events]
|
||||
[taoensso.timbre :as log]
|
||||
(:require [taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/defn accept-contact-request
|
||||
@@ -19,8 +18,7 @@
|
||||
(log/error "Failed to accept contact-request"
|
||||
{:error error
|
||||
:event :activity-center.contact-requests/accept
|
||||
:contact-id contact-id})
|
||||
nil)
|
||||
:contact-id contact-id}))
|
||||
|
||||
(rf/defn decline-contact-request
|
||||
{:events [:activity-center.contact-requests/decline]}
|
||||
@@ -38,32 +36,4 @@
|
||||
(log/error "Failed to decline contact-request"
|
||||
{:error error
|
||||
:event :activity-center.contact-requests/decline
|
||||
:contact-id contact-id})
|
||||
nil)
|
||||
|
||||
(rf/defn cancel-outgoing-contact-request
|
||||
{:events [:activity-center.contact-requests/cancel-outgoing]}
|
||||
[{:keys [db]} {:keys [contact-id notification-id]}]
|
||||
(when-let [notification (ac-events/get-notification db notification-id)]
|
||||
{:json-rpc/call
|
||||
[{:method "wakuext_cancelOutgoingContactRequest"
|
||||
:params [{:id contact-id}]
|
||||
:on-success #(rf/dispatch [:activity-center.contact-requests/cancel-outgoing-success
|
||||
notification])
|
||||
:on-error #(rf/dispatch [:activity-center.contact-requests/cancel-outgoing-error contact-id
|
||||
%])}]}))
|
||||
|
||||
(rf/defn cancel-outgoing-contact-request-success
|
||||
{:events [:activity-center.contact-requests/cancel-outgoing-success]}
|
||||
[_ notification]
|
||||
{:dispatch [:activity-center.notifications/reconcile
|
||||
[(assoc notification :deleted true)]]})
|
||||
|
||||
(rf/defn cancel-outgoing-contact-request-error
|
||||
{:events [:activity-center.contact-requests/cancel-outgoing-error]}
|
||||
[_ contact-id error]
|
||||
(log/error "Failed to cancel outgoing contact-request"
|
||||
{:error error
|
||||
:event :activity-center.contact-requests/cancel-outgoing
|
||||
:contact-id contact-id})
|
||||
nil)
|
||||
:contact-id contact-id}))
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
(ns status-im2.contexts.activity-center.notification.contact-requests.view
|
||||
(:require [quo2.core :as quo]
|
||||
[react-native.gesture :as gesture]
|
||||
[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]))
|
||||
(:require
|
||||
[quo2.core :as quo]
|
||||
[react-native.gesture :as gesture]
|
||||
[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- swipe-button-accept
|
||||
[{:keys [style]} _]
|
||||
@@ -22,24 +23,17 @@
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/decline)}])
|
||||
|
||||
(defn- swipe-button-cancel-pending
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-danger-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/cancel)}])
|
||||
|
||||
(defn- swipeable
|
||||
[{:keys [active-swipeable extra-fn notification]} child]
|
||||
(let [{:keys [id author message last-message]} notification
|
||||
{:keys [contact-request-state]} (or (:message notification)
|
||||
(:last-message notification))
|
||||
{:keys [public-key]} (rf/sub [:multiaccount/contact])
|
||||
message (or message last-message)]
|
||||
(let [{:keys [id author message]} notification
|
||||
{:keys [contact-request-state]} message
|
||||
{:keys [public-key]} (rf/sub [:multiaccount/contact])
|
||||
outgoing? (= public-key author)]
|
||||
(cond
|
||||
(#{constants/contact-request-message-state-accepted
|
||||
constants/contact-request-message-state-declined}
|
||||
contact-request-state)
|
||||
(or (#{constants/contact-request-message-state-accepted
|
||||
constants/contact-request-message-state-declined}
|
||||
contact-request-state)
|
||||
(and outgoing? (= contact-request-state constants/contact-request-message-state-pending)))
|
||||
[common/swipeable
|
||||
{:left-button common/swipe-button-read-or-unread
|
||||
:left-on-press common/swipe-on-press-toggle-read
|
||||
@@ -49,33 +43,23 @@
|
||||
:extra-fn extra-fn}
|
||||
child]
|
||||
|
||||
(= contact-request-state constants/contact-request-message-state-pending)
|
||||
(if (= public-key author)
|
||||
[common/swipeable
|
||||
{:right-button swipe-button-cancel-pending
|
||||
:right-on-press (fn []
|
||||
(rf/dispatch
|
||||
[:activity-center.contact-requests/cancel-outgoing
|
||||
{:contact-id (:from message)
|
||||
:notification-id id}]))
|
||||
:active-swipeable active-swipeable
|
||||
:extra-fn extra-fn}
|
||||
child]
|
||||
[common/swipeable
|
||||
{:left-button swipe-button-accept
|
||||
:left-on-press #(rf/dispatch [:activity-center.contact-requests/accept id])
|
||||
:right-button swipe-button-decline
|
||||
:right-on-press #(rf/dispatch [:activity-center.contact-requests/decline id])
|
||||
:active-swipeable active-swipeable
|
||||
:extra-fn extra-fn}
|
||||
child])
|
||||
(and (= contact-request-state constants/contact-request-message-state-pending)
|
||||
(not outgoing?))
|
||||
[common/swipeable
|
||||
{:left-button swipe-button-accept
|
||||
:left-on-press #(rf/dispatch [:activity-center.contact-requests/accept id])
|
||||
:right-button swipe-button-decline
|
||||
:right-on-press #(rf/dispatch [:activity-center.contact-requests/decline id])
|
||||
:active-swipeable active-swipeable
|
||||
:extra-fn extra-fn}
|
||||
child]
|
||||
|
||||
:else
|
||||
child)))
|
||||
|
||||
(defn- outgoing-contact-request-view
|
||||
[{:keys [notification set-swipeable-height]}]
|
||||
(let [{:keys [id chat-id message last-message]} notification
|
||||
(let [{:keys [chat-id message last-message]} notification
|
||||
{:keys [contact-request-state] :as message} (or message last-message)]
|
||||
(if (= contact-request-state constants/contact-request-message-state-accepted)
|
||||
[quo/activity-log
|
||||
@@ -99,17 +83,7 @@
|
||||
:message {:body (get-in message [:content :text])}
|
||||
:items (case contact-request-state
|
||||
constants/contact-request-message-state-pending
|
||||
[{:type :button
|
||||
:subtype :danger
|
||||
:key :button-cancel
|
||||
:label (i18n/label :t/cancel)
|
||||
:accessibility-label :cancel-contact-request
|
||||
:on-press (fn []
|
||||
(rf/dispatch
|
||||
[:activity-center.contact-requests/cancel-outgoing
|
||||
{:contact-id (:from message)
|
||||
:notification-id id}]))}
|
||||
{:type :status
|
||||
[{:type :status
|
||||
:subtype :pending
|
||||
:key :status-pending
|
||||
:blur? true
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns status-im2.contexts.add-new-contact.events
|
||||
(:require [utils.re-frame :as rf]
|
||||
(:require [clojure.string :as string]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im.utils.types :as types]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
@@ -11,11 +12,120 @@
|
||||
[status-im2.contexts.contacts.events :as data-store.contacts]
|
||||
[status-im.utils.utils :as utils]))
|
||||
|
||||
(defn init-contact
|
||||
"Create a new contact (persisted to app-db as [:contacts/new-identity]).
|
||||
The following options are available:
|
||||
|
||||
| key | description |
|
||||
| -------------------|-------------|
|
||||
| `:user-public-key` | user's public key (not the contact)
|
||||
| `:input` | raw user input (untrimmed)
|
||||
| `:scanned` | scanned user input (untrimmed)
|
||||
| `:id` | public-key|compressed-key|ens
|
||||
| `:type` | :empty|:public-key|:compressed-key|:ens
|
||||
| `:ens` | id.eth|id.ens-stateofus
|
||||
| `:public-key` | public-key (from decompression or ens resolution)
|
||||
| `:state` | :empty|:invalid|:decompress-key|:resolve-ens|:valid
|
||||
| `:msg` | keyword i18n msg"
|
||||
([]
|
||||
(-> [:user-public-key :input :scanned :id :type :ens :public-key :state :msg]
|
||||
(zipmap (repeat nil))))
|
||||
([kv] (-> (init-contact) (merge kv))))
|
||||
|
||||
(def url-regex #"^https?://join.status.im/u/(.+)")
|
||||
|
||||
(defn ->id
|
||||
[{:keys [input] :as contact}]
|
||||
(let [trimmed-input (utils/safe-trim input)]
|
||||
(->> {:id (if (empty? trimmed-input)
|
||||
nil
|
||||
(if-some [[_ id] (re-matches url-regex trimmed-input)]
|
||||
id
|
||||
trimmed-input))}
|
||||
(merge contact))))
|
||||
|
||||
(defn ->type
|
||||
[{:keys [id] :as contact}]
|
||||
(->> (cond
|
||||
(empty? id)
|
||||
{:type :empty}
|
||||
|
||||
(validators/valid-public-key? id)
|
||||
{:type :public-key
|
||||
:public-key id}
|
||||
|
||||
(validators/valid-compressed-key? id)
|
||||
{:type :compressed-key}
|
||||
|
||||
:else
|
||||
{:type :ens
|
||||
:ens (stateofus/ens-name-parse id)})
|
||||
(merge contact)))
|
||||
|
||||
(defn ->state
|
||||
[{:keys [id type public-key user-public-key] :as contact}]
|
||||
(->> (cond
|
||||
(empty? id)
|
||||
{:state :empty}
|
||||
|
||||
(= type :public-key)
|
||||
{:state :invalid
|
||||
:msg :t/not-a-chatkey}
|
||||
|
||||
(= public-key user-public-key)
|
||||
{:state :invalid
|
||||
:msg :t/can-not-add-yourself}
|
||||
|
||||
(and (= type :compressed-key) (empty? public-key))
|
||||
{:state :decompress-key}
|
||||
|
||||
(and (= type :ens) (empty? public-key))
|
||||
{:state :resolve-ens}
|
||||
|
||||
(and (or (= type :compressed-key) (= type :ens))
|
||||
(validators/valid-public-key? public-key))
|
||||
{:state :valid})
|
||||
(merge contact)))
|
||||
|
||||
(def validate-contact (comp ->state ->type ->id))
|
||||
|
||||
(defn dispatcher [event input] (fn [arg] (rf/dispatch [event input arg])))
|
||||
|
||||
(rf/defn set-new-identity
|
||||
{:events [:contacts/set-new-identity]}
|
||||
[{:keys [db]} input scanned]
|
||||
(let [user-public-key (get-in db [:multiaccount :public-key])
|
||||
{:keys [input id ens state]
|
||||
:as contact} (-> {:user-public-key user-public-key
|
||||
:input input
|
||||
:scanned scanned}
|
||||
init-contact
|
||||
validate-contact)]
|
||||
(case state
|
||||
|
||||
:empty {:db (dissoc db :contacts/new-identity)}
|
||||
(:valid :invalid) {:db (assoc db :contacts/new-identity contact)}
|
||||
:decompress-key {:db (assoc db :contacts/new-identity contact)
|
||||
:contacts/decompress-public-key
|
||||
{:compressed-key id
|
||||
:on-success
|
||||
(dispatcher :contacts/set-new-identity-success input)
|
||||
:on-error
|
||||
(dispatcher :contacts/set-new-identity-error input)}}
|
||||
:resolve-ens {:db (assoc db :contacts/new-identity contact)
|
||||
:contacts/resolve-public-key-from-ens
|
||||
{:chain-id (ethereum/chain-id db)
|
||||
:ens ens
|
||||
:on-success
|
||||
(dispatcher :contacts/set-new-identity-success input)
|
||||
:on-error
|
||||
(dispatcher :contacts/set-new-identity-error input)}})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:contacts/decompress-public-key
|
||||
(fn [{:keys [public-key on-success on-error]}]
|
||||
(fn [{:keys [compressed-key on-success on-error]}]
|
||||
(status/compressed-key->public-key
|
||||
public-key
|
||||
compressed-key
|
||||
(fn [resp]
|
||||
(let [{:keys [error]} (types/json->clj resp)]
|
||||
(if error
|
||||
@@ -23,74 +133,16 @@
|
||||
(on-success (str "0x" (subs resp 5)))))))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:contacts/resolve-public-key-from-ens-name
|
||||
(fn [{:keys [chain-id ens-name on-success on-error]}]
|
||||
(ens/pubkey chain-id ens-name on-success on-error)))
|
||||
|
||||
(defn fx-callbacks
|
||||
[input ens-name]
|
||||
{:on-success (fn [pubkey]
|
||||
(rf/dispatch [:contacts/set-new-identity-success input ens-name pubkey]))
|
||||
:on-error (fn [err]
|
||||
(rf/dispatch [:contacts/set-new-identity-error err input]))})
|
||||
|
||||
(defn identify-type
|
||||
[input]
|
||||
(let [regex #"^https?://join.status.im/u/(.+)"
|
||||
id (as-> (utils/safe-trim input) $
|
||||
(if-some [[_ match] (re-matches regex $)]
|
||||
match
|
||||
$)
|
||||
(if (empty? $) nil $))
|
||||
public-key? (validators/valid-public-key? id)
|
||||
compressed-key? (validators/valid-compressed-key? id)
|
||||
type (cond (empty? id) :empty
|
||||
public-key? :public-key
|
||||
compressed-key? :compressed-key
|
||||
:else :ens-name)
|
||||
ens-name (when (= type :ens-name)
|
||||
(stateofus/ens-name-parse id))]
|
||||
{:input input
|
||||
:id id
|
||||
:type type
|
||||
:ens-name ens-name}))
|
||||
|
||||
(rf/defn set-new-identity
|
||||
{:events [:contacts/set-new-identity]}
|
||||
[{:keys [db]} input]
|
||||
(let [{:keys [input id type ens-name]} (identify-type input)]
|
||||
(case type
|
||||
:empty {:db (dissoc db :contacts/new-identity)}
|
||||
:public-key {:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:public-key id
|
||||
:state :error
|
||||
:error :uncompressed-key})}
|
||||
:compressed-key {:db
|
||||
(assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:state :searching})
|
||||
:contacts/decompress-public-key
|
||||
(merge {:public-key id}
|
||||
(fx-callbacks id ens-name))}
|
||||
:ens-name {:db
|
||||
(assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:state :searching})
|
||||
:contacts/resolve-public-key-from-ens-name
|
||||
(merge {:chain-id (ethereum/chain-id db)
|
||||
:ens-name ens-name}
|
||||
(fx-callbacks id ens-name))})))
|
||||
:contacts/resolve-public-key-from-ens
|
||||
(fn [{:keys [chain-id ens on-success on-error]}]
|
||||
(ens/pubkey chain-id ens on-success on-error)))
|
||||
|
||||
(rf/defn build-contact
|
||||
{:events [:contacts/build-contact]}
|
||||
[_ pubkey ens-name open-profile-modal?]
|
||||
[_ pubkey ens open-profile-modal?]
|
||||
{:json-rpc/call [{:method "wakuext_buildContact"
|
||||
:params [{:publicKey pubkey
|
||||
:ENSName ens-name}]
|
||||
:ENSName ens}]
|
||||
:js-response true
|
||||
:on-success #(rf/dispatch [:contacts/contact-built
|
||||
pubkey
|
||||
@@ -106,24 +158,25 @@
|
||||
|
||||
(rf/defn set-new-identity-success
|
||||
{:events [:contacts/set-new-identity-success]}
|
||||
[{:keys [db] :as cofx} input ens-name pubkey]
|
||||
(rf/merge cofx
|
||||
{:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:public-key pubkey
|
||||
:ens-name ens-name
|
||||
:state :valid})}
|
||||
(build-contact pubkey ens-name false)))
|
||||
[{:keys [db]} input pubkey]
|
||||
(let [contact (get-in db [:contacts/new-identity])]
|
||||
(when (= (:input contact) input)
|
||||
(rf/merge {:db (assoc db
|
||||
:contacts/new-identity
|
||||
(->state (assoc contact :public-key pubkey)))}
|
||||
(build-contact pubkey (:ens contact) false)))))
|
||||
|
||||
(rf/defn set-new-identity-error
|
||||
{:events [:contacts/set-new-identity-error]}
|
||||
[{:keys [db]} error input]
|
||||
{:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:state :error
|
||||
:error :invalid})})
|
||||
[{:keys [db]} input err]
|
||||
(let [contact (get-in db [:contacts/new-identity])]
|
||||
(when (= (:input contact) input)
|
||||
(let [state (cond
|
||||
(or (string/includes? (:message err) "fallback failed")
|
||||
(string/includes? (:message err) "no such host"))
|
||||
{:state :invalid :msg :t/lost-connection}
|
||||
:else {:state :invalid})]
|
||||
{:db (assoc db :contacts/new-identity (merge contact state))}))))
|
||||
|
||||
(rf/defn clear-new-identity
|
||||
{:events [:contacts/clear-new-identity :contacts/new-chat-focus]}
|
||||
@@ -132,7 +185,13 @@
|
||||
|
||||
(rf/defn qr-code-scanned
|
||||
{:events [:contacts/qr-code-scanned]}
|
||||
[{:keys [db] :as cofx} input]
|
||||
[{:keys [db] :as cofx} scanned]
|
||||
(rf/merge cofx
|
||||
(set-new-identity input)
|
||||
(set-new-identity scanned scanned)
|
||||
(navigation/navigate-back)))
|
||||
|
||||
(rf/defn set-new-identity-reconnected
|
||||
[{:keys [db]}]
|
||||
(let [input (get-in db [:contacts/new-identity :input])
|
||||
resubmit? (and input (= :new-contact (get-in db [:view-id])))]
|
||||
(rf/dispatch [:contacts/set-new-identity input])))
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
(ns status-im2.contexts.add-new-contact.events-test
|
||||
(:require [cljs.test :refer-macros [deftest is are]]
|
||||
[status-im2.contexts.add-new-contact.events :as core]))
|
||||
(:require [cljs.test :refer-macros [deftest are]]
|
||||
[status-im2.contexts.add-new-contact.events :as events]))
|
||||
|
||||
(def user-ukey
|
||||
"0x04ca27ed9c7c4099d230c6d8853ad0cfaf084a019c543e9e433d3c04fac6de9147cf572b10e247cfe52f396b5aa10456b56dd1cf1d8a681e2b93993d44594b2e85")
|
||||
(def user-ckey "zQ3shtFEo4PxpQiYGcNZZ8xhJmhD6WBXwnHPBueu5SRnvPXjk")
|
||||
(def ukey
|
||||
"0x045596a7ff87da36860a84b0908191ce60a504afc94aac93c1abd774f182967ce694f1bf2d8773cd59f4dd0863e951f9b7f7351c5516291a0fceb73f8c392a0e88")
|
||||
(def ckey "zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gA")
|
||||
@@ -10,47 +13,118 @@
|
||||
(def link-ckey (str "https://join.status.im/u/" ckey))
|
||||
(def link-ens (str "https://join.status.im/u/" ens))
|
||||
|
||||
(deftest identify-type-test
|
||||
(are [input expected] (= (core/identify-type input) expected)
|
||||
"" {:input ""
|
||||
:id nil
|
||||
:type :empty
|
||||
:ens-name nil}
|
||||
;;; unit tests (no app-db involved)
|
||||
|
||||
ukey {:input ukey
|
||||
:id ukey
|
||||
:type :public-key
|
||||
:ens-name nil}
|
||||
(deftest validate-contact-test
|
||||
(are [i e] (= (events/validate-contact (events/init-contact
|
||||
{:user-public-key user-ukey
|
||||
:input i}))
|
||||
(events/init-contact e))
|
||||
|
||||
ens {:input ens
|
||||
:id ens
|
||||
:type :ens-name
|
||||
:ens-name ens-stateofus-eth}
|
||||
"" {:user-public-key user-ukey
|
||||
:input ""
|
||||
:type :empty
|
||||
:state :empty}
|
||||
|
||||
ckey {:input ckey
|
||||
:id ckey
|
||||
:type :compressed-key
|
||||
:ens-name nil}
|
||||
" " {:user-public-key user-ukey
|
||||
:input " "
|
||||
:type :empty
|
||||
:state :empty}
|
||||
|
||||
link-ckey {:input link-ckey
|
||||
:id ckey
|
||||
:type :compressed-key
|
||||
:ens-name nil}
|
||||
ukey {:user-public-key user-ukey
|
||||
:input ukey
|
||||
:id ukey
|
||||
:type :public-key
|
||||
:public-key ukey
|
||||
:state :invalid
|
||||
:msg :t/not-a-chatkey}
|
||||
|
||||
link-ens {:input link-ens
|
||||
:id ens
|
||||
:type :ens-name
|
||||
:ens-name ens-stateofus-eth}))
|
||||
ens {:user-public-key user-ukey
|
||||
:input ens
|
||||
:id ens
|
||||
:type :ens
|
||||
:ens ens-stateofus-eth
|
||||
:state :resolve-ens}
|
||||
|
||||
(deftest search-empty-string-test
|
||||
(is (= (core/set-new-identity {:db {:contacts/new-identity :foo}} "")
|
||||
{:db {}})))
|
||||
(str " " ens) {:user-public-key user-ukey
|
||||
:input (str " " ens)
|
||||
:id ens
|
||||
:type :ens
|
||||
:ens ens-stateofus-eth
|
||||
:state :resolve-ens}
|
||||
|
||||
(deftest search-uncompressed-key-test
|
||||
(is (= (core/set-new-identity {:db {}} ukey)
|
||||
{:db {:contacts/new-identity
|
||||
{:input ukey
|
||||
:public-key ukey
|
||||
:state :error
|
||||
:error :uncompressed-key}}})))
|
||||
ckey {:user-public-key user-ukey
|
||||
:input ckey
|
||||
:id ckey
|
||||
:type :compressed-key
|
||||
:state :decompress-key}
|
||||
|
||||
link-ckey {:user-public-key user-ukey
|
||||
:input link-ckey
|
||||
:id ckey
|
||||
:type :compressed-key
|
||||
:state :decompress-key}
|
||||
|
||||
link-ens {:user-public-key user-ukey
|
||||
:input link-ens
|
||||
:id ens
|
||||
:type :ens
|
||||
:ens ens-stateofus-eth
|
||||
:state :resolve-ens}))
|
||||
|
||||
;;; event handler tests (no callbacks)
|
||||
|
||||
(def db
|
||||
{:multiaccount {:public-key user-ukey}
|
||||
:networks/current-network "mainnet_rpc"
|
||||
:networks/networks {"mainnet_rpc"
|
||||
{:id "mainnet_rpc"
|
||||
:config {:NetworkId 1}}}})
|
||||
|
||||
(deftest set-new-identity-test
|
||||
(with-redefs [events/dispatcher (fn [& args] args)]
|
||||
(are [i edb] (= (events/set-new-identity {:db db} i nil) edb)
|
||||
|
||||
"" {:db db}
|
||||
|
||||
ukey {:db (assoc db
|
||||
:contacts/new-identity
|
||||
(events/init-contact
|
||||
{:user-public-key user-ukey
|
||||
:input ukey
|
||||
:id ukey
|
||||
:type :public-key
|
||||
:public-key ukey
|
||||
:state :invalid
|
||||
:msg :t/not-a-chatkey}))}
|
||||
|
||||
ens {:db (assoc db
|
||||
:contacts/new-identity
|
||||
(events/init-contact
|
||||
{:user-public-key user-ukey
|
||||
:input ens
|
||||
:id ens
|
||||
:type :ens
|
||||
:ens ens-stateofus-eth
|
||||
:public-key nil ; not yet...
|
||||
:state :resolve-ens}))
|
||||
:contacts/resolve-public-key-from-ens
|
||||
{:chain-id 1
|
||||
:ens ens-stateofus-eth
|
||||
:on-success [:contacts/set-new-identity-success ens]
|
||||
:on-error [:contacts/set-new-identity-error ens]}}
|
||||
|
||||
;; compressed-key & add-self-as-contact
|
||||
user-ckey {:db (assoc db
|
||||
:contacts/new-identity
|
||||
(events/init-contact
|
||||
{:user-public-key user-ukey
|
||||
:input user-ckey
|
||||
:id user-ckey
|
||||
:type :compressed-key
|
||||
:public-key nil ; not yet...
|
||||
:state :decompress-key}))
|
||||
:contacts/decompress-public-key
|
||||
{:compressed-key user-ckey
|
||||
:on-success [:contacts/set-new-identity-success user-ckey]
|
||||
:on-error [:contacts/set-new-identity-error user-ckey]}})))
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
{:style {:flex-direction :row
|
||||
:justify-content :space-between}})
|
||||
|
||||
(def container-error
|
||||
(def container-invalid
|
||||
{:style {:flex-direction :row
|
||||
:align-items :center
|
||||
:margin-top 8}})
|
||||
@@ -64,18 +64,18 @@
|
||||
colors/neutral-50
|
||||
colors/neutral-40)}})
|
||||
|
||||
(def icon-error
|
||||
(def icon-invalid
|
||||
{:size 16
|
||||
:color colors/danger-50})
|
||||
|
||||
(def text-error
|
||||
(def text-invalid
|
||||
{:size :paragraph-2
|
||||
:align :left
|
||||
:style {:margin-left 4
|
||||
:color colors/danger-50}})
|
||||
|
||||
(defn text-input-container
|
||||
[error?]
|
||||
[invalid?]
|
||||
{:style {:padding-top 1
|
||||
:padding-left 12
|
||||
:padding-right 7
|
||||
@@ -88,7 +88,7 @@
|
||||
colors/neutral-95)
|
||||
:border-width 1
|
||||
:border-radius 12
|
||||
:border-color (if error?
|
||||
:border-color (if invalid?
|
||||
colors/danger-50-opa-40
|
||||
(colors/theme-colors
|
||||
colors/neutral-20
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[quo2.core :as quo]
|
||||
[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]
|
||||
@@ -44,60 +45,78 @@
|
||||
|
||||
(defn new-contact
|
||||
[]
|
||||
(let [{:keys [input public-key state error ens-name]} (rf/sub [:contacts/new-identity])
|
||||
error? (and (= state :error)
|
||||
(= error :uncompressed-key))]
|
||||
[rn/keyboard-avoiding-view (style/container-kbd)
|
||||
[rn/view style/container-image
|
||||
[rn/image
|
||||
{:source (resources/get-image :add-new-contact)
|
||||
:style style/image}]
|
||||
[quo/button
|
||||
(merge (style/button-close)
|
||||
{:on-press
|
||||
(fn []
|
||||
(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 error?)
|
||||
[rn/text-input
|
||||
(merge (style/text-input)
|
||||
{:default-value input
|
||||
:placeholder (i18n/label :t/type-some-chat-key)
|
||||
:on-change-text #(debounce/debounce-and-dispatch
|
||||
[:contacts/set-new-identity %]
|
||||
600)})]
|
||||
(when (string/blank? input)
|
||||
(let [clipboard (reagent/atom nil)
|
||||
default-value (reagent/atom nil)]
|
||||
(fn []
|
||||
(clipboard/get-string #(reset! clipboard %))
|
||||
(let [{:keys [input scanned public-key ens state msg]}
|
||||
(rf/sub [:contacts/new-identity])
|
||||
invalid? (= state :invalid)
|
||||
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}]
|
||||
[quo/button
|
||||
(merge (style/button-close)
|
||||
{:on-press
|
||||
(fn []
|
||||
(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
|
||||
[quo/button
|
||||
(merge style/button-paste
|
||||
{:on-press (fn []
|
||||
(clipboard/get-string #(rf/dispatch [:contacts/set-new-identity %])))})
|
||||
(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 error?
|
||||
[rn/view style/container-error
|
||||
[quo/icon :i/alert style/icon-error]
|
||||
[quo/text style/text-error (i18n/label :t/not-a-chatkey)]])
|
||||
(when (= state :valid)
|
||||
[found-contact public-key])]
|
||||
[rn/view
|
||||
[quo/button
|
||||
(merge (style/button-view-profile state)
|
||||
{:on-press
|
||||
(fn []
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:chat.ui/show-profile public-key ens-name]))})
|
||||
(i18n/label :t/view-profile)]]]]))
|
||||
(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)]]]]))))
|
||||
|
||||
@@ -202,9 +202,9 @@
|
||||
(rf/defn navigate-to-chat
|
||||
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
|
||||
{:events [:chat/navigate-to-chat]}
|
||||
[{db :db :as cofx} chat-id from-shell?]
|
||||
[{db :db :as cofx} chat-id]
|
||||
(rf/merge cofx
|
||||
{:dispatch [:navigate-to-nav2 :chat chat-id from-shell?]}
|
||||
{:dispatch [:navigate-to :chat chat-id]}
|
||||
(when-not (or (= (:view-id db) :community) (= (:view-id db) :community-overview))
|
||||
(navigation/pop-to-root :shell-stack))
|
||||
(close-chat false)
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
(let [chat-id "test_chat"
|
||||
db {:pagination-info {chat-id {:all-loaded? true}}}]
|
||||
(testing "Pagination info should be reset on navigation"
|
||||
(let [res (chat/navigate-to-chat {:db db} chat-id false)]
|
||||
(let [res (chat/navigate-to-chat {:db db} chat-id)]
|
||||
(is (nil? (get-in res [:db :pagination-info chat-id :all-loaded?])))))))
|
||||
|
||||
(deftest camera-roll-loading-more-test
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
(anim/animate background-color :transparent)
|
||||
(anim/animate opacity 0)
|
||||
(rf/dispatch (if platform/ios?
|
||||
[:navigate-back]
|
||||
[:chat.ui/exit-lightbox-signal @index]
|
||||
[:navigate-back])))
|
||||
:style style/close-container}
|
||||
[quo/icon :close {:size 20 :color colors/white}]]
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
(defn toggle-opacity
|
||||
[index {:keys [opacity-value border-value transparent? atoms]} portrait?]
|
||||
(let [{:keys [small-list-ref]} atoms
|
||||
opacity (reanimated/get-shared-value opacity-value)]
|
||||
opacity (reanimated/get-shared-value opacity-value)]
|
||||
(if (= opacity 1)
|
||||
(do
|
||||
(when platform/ios?
|
||||
@@ -63,8 +63,8 @@
|
||||
(orientation/lock-to-portrait "lightbox"))
|
||||
(js/setTimeout #(when @flat-list-ref
|
||||
(.scrollToOffset
|
||||
^js @flat-list-ref
|
||||
#js {:animated false :offset (* (+ item-width seperator-width) @index)}))
|
||||
^js @flat-list-ref
|
||||
#js {:animated false :offset (* (+ item-width seperator-width) @index)}))
|
||||
timeout)
|
||||
(when platform/ios?
|
||||
(top-view/animate-rotation result screen-width screen-height insets animations))))
|
||||
@@ -124,58 +124,57 @@
|
||||
;; we get `insets` from `screen-params` because trying to consume it from
|
||||
;; lightbox screen causes lots of problems
|
||||
(let [{:keys [messages index insets]} (rf/sub [:get-screen-params])
|
||||
render-list (reagent/atom false)
|
||||
atoms {:flat-list-ref (atom nil)
|
||||
:small-list-ref (atom nil)
|
||||
:scroll-index-lock? (atom true)}
|
||||
atoms {:flat-list-ref (atom nil)
|
||||
:small-list-ref (atom nil)
|
||||
:scroll-index-lock? (atom true)}
|
||||
;; The initial value of data is the image that was pressed (and not the whole album) in order
|
||||
;; for the transition animation to execute properly, otherwise it would animate towards
|
||||
;; outside the screen (even if we have `initialScrollIndex` set).
|
||||
data (reagent/atom [(nth messages index)])
|
||||
scroll-index (reagent/atom index)
|
||||
transparent? (reagent/atom false)
|
||||
set-full-height? (reagent/atom false)
|
||||
window (rf/sub [:dimensions/window])
|
||||
window-width (:width window)
|
||||
window-height (:height window)
|
||||
window-height (if platform/android?
|
||||
(+ window-height (:top insets))
|
||||
window-height)
|
||||
animations {:background-color (anim/use-val "rgba(0,0,0,0)")
|
||||
:border (anim/use-val (if platform/ios? 0 12))
|
||||
:opacity (anim/use-val 0)
|
||||
:rotate (anim/use-val "0deg")
|
||||
:layout (anim/use-val -10)
|
||||
:top-view-y (anim/use-val 0)
|
||||
:top-view-x (anim/use-val 0)
|
||||
:top-view-width (anim/use-val window-width)
|
||||
:top-view-bg (anim/use-val colors/neutral-100-opa-0)
|
||||
:pan-y (anim/use-val 0)
|
||||
:pan-x (anim/use-val 0)}
|
||||
derived {:top-layout (worklet/info-layout (:layout animations)
|
||||
true)
|
||||
:bottom-layout (worklet/info-layout (:layout animations)
|
||||
false)}
|
||||
callback (fn [e]
|
||||
(on-viewable-items-changed e scroll-index atoms))]
|
||||
data (reagent/atom (if (number? index) [(nth messages index)] []))
|
||||
scroll-index (reagent/atom index)
|
||||
transparent? (reagent/atom false)
|
||||
set-full-height? (reagent/atom false)
|
||||
window (rf/sub [:dimensions/window])
|
||||
window-width (:width window)
|
||||
window-height (:height window)
|
||||
window-height (if platform/android?
|
||||
(+ window-height (:top insets))
|
||||
window-height)
|
||||
animations {:background-color (anim/use-val "rgba(0,0,0,0)")
|
||||
:border (anim/use-val (if platform/ios? 0 12))
|
||||
:opacity (anim/use-val 0)
|
||||
:rotate (anim/use-val "0deg")
|
||||
:layout (anim/use-val -10)
|
||||
:top-view-y (anim/use-val 0)
|
||||
:top-view-x (anim/use-val 0)
|
||||
:top-view-width (anim/use-val window-width)
|
||||
:top-view-bg (anim/use-val colors/neutral-100-opa-0)
|
||||
:pan-y (anim/use-val 0)
|
||||
:pan-x (anim/use-val 0)}
|
||||
derived {:top-layout (worklet/info-layout (:layout animations)
|
||||
true)
|
||||
:bottom-layout (worklet/info-layout (:layout animations)
|
||||
false)}
|
||||
callback (fn [e]
|
||||
(on-viewable-items-changed e scroll-index atoms))]
|
||||
(anim/animate (:background-color animations) "rgba(0,0,0,1)")
|
||||
(reset! data messages)
|
||||
(orientation/use-device-orientation-change
|
||||
(fn [result]
|
||||
(if platform/ios?
|
||||
(handle-orientation result scroll-index window-width window-height animations insets atoms)
|
||||
;; `use-device-orientation-change` will always be called on Android, so need to check
|
||||
(orientation/get-auto-rotate-state
|
||||
(fn [enabled?]
|
||||
;; RNN does not support landscape-right
|
||||
(when (and enabled? (not= result orientation/landscape-right))
|
||||
(handle-orientation result
|
||||
scroll-index
|
||||
window-width
|
||||
window-height
|
||||
animations
|
||||
insets
|
||||
atoms)))))))
|
||||
(fn [result]
|
||||
(if platform/ios?
|
||||
(handle-orientation result scroll-index window-width window-height animations insets atoms)
|
||||
;; `use-device-orientation-change` will always be called on Android, so need to check
|
||||
(orientation/get-auto-rotate-state
|
||||
(fn [enabled?]
|
||||
;; RNN does not support landscape-right
|
||||
(when (and enabled? (not= result orientation/landscape-right))
|
||||
(handle-orientation result
|
||||
scroll-index
|
||||
window-width
|
||||
window-height
|
||||
animations
|
||||
insets
|
||||
atoms)))))))
|
||||
(rn/use-effect (fn []
|
||||
(when @(:flat-list-ref atoms)
|
||||
(.scrollToIndex ^js @(:flat-list-ref atoms)
|
||||
@@ -186,7 +185,6 @@
|
||||
(anim/animate (:border animations) 12))
|
||||
(if platform/ios? 250 100))
|
||||
(js/setTimeout #(reset! (:scroll-index-lock? atoms) false) 300)
|
||||
(js/setTimeout #(reset! render-list true) 500)
|
||||
(fn []
|
||||
(rf/dispatch [:chat.ui/zoom-out-signal nil])
|
||||
(when platform/android?
|
||||
@@ -204,73 +202,44 @@
|
||||
window-height
|
||||
window-width)
|
||||
item-width (if (and landscape? platform/ios?) screen-height screen-width)]
|
||||
;[rn/view {:style (merge (style/image (+ screen-width seperator-width) screen-height) {:background-color :black})}
|
||||
;[reanimated/fast-image
|
||||
; {:source {:uri (:image (:content (nth messages index)))}
|
||||
; :native-ID :shared-element
|
||||
; :style {:width window-width
|
||||
; :height (* (:image-height (nth messages index)) (/ window-width (:image-width (nth messages index))))}
|
||||
; ;:style (style/image dimensions animations (:border-value args))
|
||||
; }]]
|
||||
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style {:background-color (:background-color
|
||||
animations)}
|
||||
animations)}
|
||||
{:height screen-height})}
|
||||
;(when-not @transparent?
|
||||
; [top-view/top-view (first messages) insets scroll-index animations derived landscape?
|
||||
; screen-width])
|
||||
(when-not @transparent?
|
||||
[top-view/top-view (first messages) insets scroll-index animations derived landscape?
|
||||
screen-width])
|
||||
[gesture/gesture-detector
|
||||
{:gesture (drag-gesture animations (and landscape? platform/ios?) set-full-height?)}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:transform [{:translateY (:pan-y animations)}
|
||||
{:translateX (:pan-x animations)}]}
|
||||
{})}
|
||||
(if @render-list
|
||||
[gesture/flat-list
|
||||
{:ref #(reset! (:flat-list-ref atoms) %)
|
||||
:key-fn :message-id
|
||||
:style {:width (+ screen-width seperator-width)}
|
||||
:data @data
|
||||
:render-fn image
|
||||
:render-data {:opacity-value (:opacity animations)
|
||||
:border-value (:border animations)
|
||||
:transparent? transparent?
|
||||
:set-full-height? set-full-height?
|
||||
:screen-height screen-height
|
||||
:screen-width screen-width
|
||||
:window-height window-height
|
||||
:window-width window-width
|
||||
:atoms atoms}
|
||||
:initial-scroll-index index
|
||||
:horizontal horizontal?
|
||||
:inverted inverted?
|
||||
:paging-enabled true
|
||||
:get-item-layout (fn [_ index] (get-item-layout _ index item-width))
|
||||
:viewability-config {:view-area-coverage-percent-threshold 50
|
||||
:wait-for-interaction true}
|
||||
:shows-vertical-scroll-indicator false
|
||||
:shows-horizontal-scroll-indicator false
|
||||
:on-viewable-items-changed callback}]
|
||||
[rn/view
|
||||
{:style (style/image (+ screen-width seperator-width) screen-height)}
|
||||
[rn/view {:style {:width window-width
|
||||
:height (* (:image-height (nth messages index)) (/ window-width (:image-width (nth messages index))))}}
|
||||
[reanimated/fast-image
|
||||
{:source {:uri (:image (:content (nth messages index)))}
|
||||
:native-ID :shared-element
|
||||
:style {:border-radius 12
|
||||
:width window-width
|
||||
:height (* (:image-height (nth messages index)) (/ window-width (:image-width (nth messages index))))}
|
||||
;:style (style/image dimensions animations (:border-value args))
|
||||
}]
|
||||
]
|
||||
[rn/view {:style {:width seperator-width}}]]
|
||||
)
|
||||
]]
|
||||
;(when (and (not @transparent?) (not landscape?))
|
||||
; [bottom-view/bottom-view messages index scroll-index insets animations derived
|
||||
; item-width atoms])
|
||||
]
|
||||
))]))])
|
||||
{:transform [{:translateY (:pan-y animations)}
|
||||
{:translateX (:pan-x animations)}]}
|
||||
{})}
|
||||
[gesture/flat-list
|
||||
{:ref #(reset! (:flat-list-ref atoms) %)
|
||||
:key-fn :message-id
|
||||
:style {:width (+ screen-width seperator-width)}
|
||||
:data @data
|
||||
:render-fn image
|
||||
:render-data {:opacity-value (:opacity animations)
|
||||
:border-value (:border animations)
|
||||
:transparent? transparent?
|
||||
:set-full-height? set-full-height?
|
||||
:screen-height screen-height
|
||||
:screen-width screen-width
|
||||
:window-height window-height
|
||||
:window-width window-width
|
||||
:atoms atoms}
|
||||
:horizontal horizontal?
|
||||
:inverted inverted?
|
||||
:paging-enabled true
|
||||
:get-item-layout (fn [_ index] (get-item-layout _ index item-width))
|
||||
:viewability-config {:view-area-coverage-percent-threshold 50
|
||||
:wait-for-interaction true}
|
||||
:shows-vertical-scroll-indicator false
|
||||
:shows-horizontal-scroll-indicator false
|
||||
:on-viewable-items-changed callback}]]]
|
||||
(when (and (not @transparent?) (not landscape?))
|
||||
[bottom-view/bottom-view messages index scroll-index insets animations derived
|
||||
item-width atoms])]))]))])
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
(ns status-im2.contexts.chat.menus.pinned-messages.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def heading
|
||||
{:margin-horizontal 20})
|
||||
|
||||
(def heading-container
|
||||
{:flex-direction :row
|
||||
:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:border-radius 20
|
||||
:align-items :center
|
||||
:align-self :flex-start
|
||||
:margin-horizontal 20
|
||||
:padding 4
|
||||
:margin-top 8})
|
||||
|
||||
(def heading-text
|
||||
{:margin-left 6
|
||||
:margin-right 4})
|
||||
|
||||
(def chat-name-text
|
||||
{:margin-left 4
|
||||
:margin-right 8})
|
||||
|
||||
(defn list-footer
|
||||
[bottom-inset]
|
||||
{:height bottom-inset})
|
||||
|
||||
(defn no-pinned-messages-container
|
||||
[bottom-inset]
|
||||
{:justify-content :center
|
||||
:align-items :center
|
||||
:margin-top 20
|
||||
:margin-bottom bottom-inset})
|
||||
|
||||
(def no-pinned-messages-icon
|
||||
{:width 120
|
||||
:height 120
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:border-width 1})
|
||||
|
||||
(def no-pinned-messages-text
|
||||
{:margin-top 20})
|
||||
@@ -0,0 +1,73 @@
|
||||
(ns status-im2.contexts.chat.menus.pinned-messages.view
|
||||
(:require [quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im2.contexts.chat.messages.content.deleted.view :as content.deleted]
|
||||
[status-im2.contexts.chat.messages.content.view :as message]
|
||||
[status-im2.contexts.chat.menus.pinned-messages.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def list-key-fn #(or (:message-id %) (:value %)))
|
||||
|
||||
(defn message-render-fn
|
||||
[{:keys [deleted? deleted-for-me?] :as message} _ _ context]
|
||||
;; TODO (flexsurfer) probably we don't want reactions here
|
||||
(if (or deleted? deleted-for-me?)
|
||||
[content.deleted/deleted-message message context]
|
||||
[message/message-with-reactions message context]))
|
||||
|
||||
(defn pinned-messages
|
||||
[chat-id]
|
||||
(let [pinned-messages (rf/sub [:chats/pinned-sorted-list chat-id])
|
||||
render-data (rf/sub [:chats/current-chat-message-list-view-context :in-pinned-view])
|
||||
current-chat (rf/sub [:chat-by-id chat-id])
|
||||
{:keys [community-id]} current-chat
|
||||
community (rf/sub [:communities/community community-id])]
|
||||
[safe-area/consumer
|
||||
(fn [insets]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [{window-height :height} (rn/use-window-dimensions)
|
||||
bottom-inset (:bottom insets)]
|
||||
[rn/scroll-view
|
||||
{:style {:max-height (- window-height (:top insets))}
|
||||
:accessibility-label :pinned-messages-menu}
|
||||
[:<>
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/heading}
|
||||
(i18n/label :t/pinned-messages)]
|
||||
(when community
|
||||
[rn/view
|
||||
{:style style/heading-container}
|
||||
[rn/text {:style style/heading-text} (:name community)]
|
||||
[quo/icon
|
||||
:i/chevron-right
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
|
||||
:size 12}]
|
||||
[rn/text
|
||||
{:style style/chat-name-text}
|
||||
(str "# " (:chat-name current-chat))]])]
|
||||
(if (pos? (count pinned-messages))
|
||||
[rn/flat-list
|
||||
{:data pinned-messages
|
||||
:render-data render-data
|
||||
:render-fn message-render-fn
|
||||
:footer [rn/view {:style (style/list-footer bottom-inset)}]
|
||||
:key-fn list-key-fn
|
||||
:separator quo/separator}]
|
||||
[rn/view {:style (style/no-pinned-messages-container bottom-inset)}
|
||||
[rn/view {:style style/no-pinned-messages-icon}
|
||||
[quo/icon :i/placeholder]]
|
||||
[quo/text
|
||||
{:weight :semi-bold
|
||||
:style style/no-pinned-messages-text}
|
||||
(i18n/label :t/no-pinned-messages)]
|
||||
[quo/text {:size :paragraph-2}
|
||||
(i18n/label
|
||||
(if community
|
||||
:t/no-pinned-messages-community-desc
|
||||
:t/no-pinned-messages-desc))]])]))])]))
|
||||
@@ -41,13 +41,13 @@
|
||||
:size 32} :i/reaction]])
|
||||
|
||||
(defn image-button
|
||||
[chat-id]
|
||||
[insets]
|
||||
[quo/button
|
||||
{:on-press (fn []
|
||||
(permissions/request-permissions
|
||||
{:permissions [:read-external-storage :write-external-storage]
|
||||
:on-allowed #(rf/dispatch
|
||||
[:open-modal :photo-selector {:chat-id chat-id}])
|
||||
[:open-modal :photo-selector {:insets insets}])
|
||||
:on-denied (fn []
|
||||
(background-timer/set-timeout
|
||||
#(utils-old/show-popup (i18n/label :t/error)
|
||||
@@ -122,7 +122,7 @@
|
||||
(when (and (not @input/recording-audio?)
|
||||
(nil? (get @input/reviewing-audio-filepath chat-id)))
|
||||
[:<>
|
||||
[image-button chat-id]
|
||||
[image-button insets]
|
||||
[rn/view {:width 12}]
|
||||
[reactions-button]
|
||||
[rn/view {:flex 1}]
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
:sending-image (seq images)
|
||||
:refs refs}]]]]
|
||||
(if suggestions?
|
||||
[mentions/mentions params insets]
|
||||
[mentions/mentions (select-keys params [:refs :suggestions :max-y]) insets]
|
||||
[controls/view send-ref record-ref params insets chat-id images
|
||||
edit #(clean-and-minimize params)])
|
||||
;;;;black background
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
(concat
|
||||
(when (and outgoing
|
||||
(not (or deleted? deleted-for-me?))
|
||||
;; temporarily disable edit image message until
|
||||
;; https://github.com/status-im/status-mobile/issues/15298
|
||||
;; is implemented
|
||||
(not= content-type constants/content-type-image)
|
||||
(not= content-type constants/content-type-audio))
|
||||
[{:type :main
|
||||
:on-press #(rf/dispatch [:chat.ui/edit-message message-data])
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
(ns status-im2.contexts.chat.messages.pin.list.view
|
||||
(:require [quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[status-im2.contexts.chat.messages.content.deleted.view :as content.deleted]
|
||||
[status-im2.contexts.chat.messages.content.view :as message]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def list-key-fn #(or (:message-id %) (:value %)))
|
||||
|
||||
(defn message-render-fn
|
||||
[{:keys [deleted? deleted-for-me?] :as message} _ _ context]
|
||||
;; TODO (flexsurfer) probably we don't want reactions here
|
||||
(if (or deleted? deleted-for-me?)
|
||||
[content.deleted/deleted-message message context]
|
||||
[message/message-with-reactions message context]))
|
||||
|
||||
(defn pinned-messages-list
|
||||
[chat-id]
|
||||
(let [pinned-messages (rf/sub [:chats/pinned-sorted-list chat-id])
|
||||
render-data (rf/sub [:chats/current-chat-message-list-view-context :in-pinned-view])
|
||||
current-chat (rf/sub [:chat-by-id chat-id])
|
||||
{:keys [community-id]} current-chat
|
||||
community (rf/sub [:communities/community community-id])]
|
||||
[rn/view {:accessibility-label :pinned-messages-list}
|
||||
;; TODO (flexsurfer) this should be a component in quo2
|
||||
;; https://github.com/status-im/status-mobile/issues/14529
|
||||
[:<>
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:margin-horizontal 20}}
|
||||
(i18n/label :t/pinned-messages)]
|
||||
(when community
|
||||
[rn/view
|
||||
{:style {:flex-direction :row
|
||||
:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:border-radius 20
|
||||
:align-items :center
|
||||
:align-self :flex-start
|
||||
:margin-horizontal 20
|
||||
:padding 4
|
||||
:margin-top 8}}
|
||||
[rn/text {:style {:margin-left 6 :margin-right 4}} (:name community)]
|
||||
[quo/icon
|
||||
:i/chevron-right
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
|
||||
:size 12}]
|
||||
[rn/text
|
||||
{:style {:margin-left 4
|
||||
:margin-right 8}}
|
||||
(str "# " (:chat-name current-chat))]])]
|
||||
(if (pos? (count pinned-messages))
|
||||
[rn/flat-list
|
||||
{:data pinned-messages
|
||||
:render-data render-data
|
||||
:render-fn message-render-fn
|
||||
:key-fn list-key-fn
|
||||
:separator quo/separator}]
|
||||
[rn/view
|
||||
{:style {:justify-content :center
|
||||
:align-items :center
|
||||
:margin-top 20}}
|
||||
[rn/view
|
||||
{:style {:width 120
|
||||
:height 120
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:border-width 1}} [quo/icon :i/placeholder]]
|
||||
[quo/text
|
||||
{:weight :semi-bold
|
||||
:style {:margin-top 20}}
|
||||
(i18n/label :t/no-pinned-messages)]
|
||||
[quo/text {:size :paragraph-2}
|
||||
(i18n/label
|
||||
(if community :t/no-pinned-messages-community-desc :t/no-pinned-messages-desc))]])]))
|
||||
@@ -15,8 +15,7 @@
|
||||
:flex-direction :row
|
||||
:left 0
|
||||
:right 0
|
||||
:margin-top 20
|
||||
:margin-bottom 12
|
||||
:top 20
|
||||
:justify-content :center
|
||||
:z-index 1})
|
||||
|
||||
@@ -66,8 +65,8 @@
|
||||
:height (/ window-width 3)
|
||||
:margin-left (when (not= (mod index 3) 0) 1)
|
||||
:margin-bottom 1
|
||||
:border-top-left-radius (when (= index 0) 10)
|
||||
:border-top-right-radius (when (= index 2) 10)})
|
||||
:border-top-left-radius (when (= index 0) 20)
|
||||
:border-top-right-radius (when (= index 2) 20)})
|
||||
|
||||
(defn overlay
|
||||
[window-width]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(ns status-im2.contexts.chat.photo-selector.view
|
||||
(:require
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.platform :as platform]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.i18n :as i18n]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[quo2.components.notifications.info-count :as info-count]
|
||||
[quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
@@ -13,6 +13,7 @@
|
||||
[status-im2.contexts.chat.photo-selector.style :as style]
|
||||
[status-im.utils.core :as utils]
|
||||
[quo.react]
|
||||
[status-im2.common.bottom-sheet-screen.view :as bottom-sheet-screen]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn on-press-confirm-selection
|
||||
@@ -80,31 +81,38 @@
|
||||
(inc (utils/first-index #(= (:uri item) (:uri %)) @selected))])])
|
||||
|
||||
(defn album-title
|
||||
[photos? selected-album selected temporary-selected]
|
||||
[rn/touchable-opacity
|
||||
{:style (style/title-container)
|
||||
:active-opacity 1
|
||||
:accessibility-label :album-title
|
||||
:on-press (fn []
|
||||
(if photos?
|
||||
(do
|
||||
(reset! temporary-selected @selected)
|
||||
(rf/dispatch [:open-modal :album-selector]))
|
||||
(rf/dispatch [:navigate-back])))}
|
||||
[quo/text
|
||||
{:weight :medium
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:style {:max-width 150}}
|
||||
selected-album]
|
||||
[rn/view {:style (style/chevron-container)}
|
||||
[quo/icon (if photos? :i/chevron-down :i/chevron-up)
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white)}]]])
|
||||
[photos? selected-album]
|
||||
(fn []
|
||||
[rn/touchable-opacity
|
||||
{:style (style/title-container)
|
||||
:active-opacity 1
|
||||
:accessibility-label :album-title
|
||||
:on-press (fn []
|
||||
;; TODO: album-selector issue:
|
||||
;; https://github.com/status-im/status-mobile/issues/15398
|
||||
(js/alert "currently disabled")
|
||||
;(if photos?
|
||||
; (do
|
||||
; (reset! temporary-selected @selected)
|
||||
; (rf/dispatch [:open-modal :album-selector {:insets insets}]))
|
||||
; (rf/dispatch [:navigate-back]))
|
||||
)}
|
||||
[quo/text
|
||||
{:weight :medium
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:style {:max-width 150}}
|
||||
selected-album]
|
||||
[rn/view {:style (style/chevron-container)}
|
||||
[quo/icon (if photos? :i/chevron-down :i/chevron-up)
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white)}]]]))
|
||||
|
||||
|
||||
(defn photo-selector
|
||||
[]
|
||||
[:f>
|
||||
(let [temporary-selected (reagent/atom [])] ; used when switching albums
|
||||
(let [{:keys [insets]} (rf/sub [:get-screen-params])
|
||||
temporary-selected (reagent/atom [])] ; used when switching albums
|
||||
(fn []
|
||||
(let [selected (reagent/atom []) ; currently selected
|
||||
selected-images (rf/sub [:chats/sending-image]) ; already selected and dispatched
|
||||
@@ -116,26 +124,19 @@
|
||||
(reset! selected (vec (vals selected-images)))
|
||||
(reset! selected @temporary-selected)))
|
||||
[selected-album])
|
||||
[safe-area/consumer
|
||||
(fn [insets]
|
||||
[bottom-sheet-screen/view
|
||||
(fn [{:keys [scroll-enabled on-scroll]}]
|
||||
(let [window-width (:width (rn/get-window))
|
||||
camera-roll-photos (rf/sub [:camera-roll/photos])
|
||||
end-cursor (rf/sub [:camera-roll/end-cursor])
|
||||
loading? (rf/sub [:camera-roll/loading-more])
|
||||
has-next-page? (rf/sub [:camera-roll/has-next-page])]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[:<>
|
||||
[rn/view
|
||||
{:style style/buttons-container}
|
||||
(when platform/android?
|
||||
[rn/touchable-opacity
|
||||
{:active-opacity 1
|
||||
:on-press #(rf/dispatch [:navigate-back])
|
||||
:style (style/close-button-container)}
|
||||
[quo/icon :i/close
|
||||
{:size 20 :color (colors/theme-colors colors/black colors/white)}]])
|
||||
[album-title true selected-album selected temporary-selected]
|
||||
[album-title true selected-album selected temporary-selected insets]
|
||||
[clear-button selected]]
|
||||
[rn/flat-list
|
||||
[gesture/flat-list
|
||||
{:key-fn identity
|
||||
:render-fn image
|
||||
:render-data {:window-width window-width :selected selected}
|
||||
@@ -143,7 +144,9 @@
|
||||
:num-columns 3
|
||||
:content-container-style {:width "100%"
|
||||
:padding-bottom (+ (:bottom insets) 100)
|
||||
:padding-top 80}
|
||||
:padding-top 64}
|
||||
:on-scroll on-scroll
|
||||
:scroll-enabled scroll-enabled
|
||||
:on-end-reached #(rf/dispatch [:camera-roll/on-end-reached end-cursor
|
||||
selected-album loading?
|
||||
has-next-page?])}]
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:communities/load-category-states (:id item)])
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch [:navigate-to :community {:community-id (:id item)}]))
|
||||
(rf/dispatch [:navigate-to :community-overview (:id item)]))
|
||||
:on-long-press #(rf/dispatch
|
||||
[:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
@@ -144,7 +144,7 @@
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:communities/load-category-states (:id community)])
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch [:navigate-to :community (:id community)]))
|
||||
(rf/dispatch [:navigate-to :community-overview (:id community)]))
|
||||
:on-long-press #(rf/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
;; TODO implement with quo2
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
item (merge item unviewed-counts)]
|
||||
[quo/communities-membership-list-item
|
||||
{:style {:padding-horizontal 18}
|
||||
:on-press #(rf/dispatch [:navigate-to-nav2 :community-overview id])
|
||||
:on-press #(rf/dispatch [:navigate-to :community-overview id])
|
||||
:on-long-press #(rf/dispatch
|
||||
[:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
|
||||
@@ -295,14 +295,14 @@
|
||||
|
||||
(defn page-nav-right-section-buttons
|
||||
[id]
|
||||
[{:icon :i/options
|
||||
:background-color (scroll-page/icon-color)
|
||||
:on-press #(rf/dispatch
|
||||
[:bottom-sheet/show-sheet
|
||||
{:content
|
||||
(fn []
|
||||
[options/community-options-bottom-sheet
|
||||
id])}])}])
|
||||
[{:icon :i/options
|
||||
:background-color (scroll-page/icon-color)
|
||||
:accessibility-label :community-options-for-community
|
||||
:on-press #(rf/dispatch
|
||||
[:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[options/community-options-bottom-sheet
|
||||
id])}])}])
|
||||
|
||||
(defn pick-first-category-by-height
|
||||
[scroll-height first-channel-height categories-heights]
|
||||
|
||||
@@ -3,16 +3,13 @@
|
||||
|
||||
(def background-container
|
||||
{:background-color colors/neutral-95
|
||||
:flex-direction :row})
|
||||
|
||||
(defn background-gradient-overlay
|
||||
[dark-overlay?]
|
||||
{:position :absolute
|
||||
:height (if dark-overlay? 240 136)
|
||||
:top 0
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom 0})
|
||||
:flex-direction :row
|
||||
:position :absolute
|
||||
:overflow :hidden
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0})
|
||||
|
||||
(def background-blur-overlay
|
||||
{:position :absolute
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
(ns status-im2.contexts.onboarding.common.background.view
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im2.common.resources :as resources]
|
||||
[react-native.linear-gradient :as linear-gradient]
|
||||
[status-im2.contexts.onboarding.common.background.style :as style]))
|
||||
[react-native.blur :as blur]
|
||||
[status-im2.contexts.onboarding.common.carousel.view :as carousel]
|
||||
[status-im2.contexts.onboarding.common.background.style :as style]
|
||||
[status-im2.contexts.onboarding.common.carousel.animation :as carousel.animation]))
|
||||
|
||||
(defn view
|
||||
[dark-overlay?]
|
||||
[rn/view
|
||||
{:style style/background-container}
|
||||
[rn/image
|
||||
{:blur-radius (if dark-overlay? 13 0)
|
||||
:style {:flex 1}
|
||||
;; Todo - get background image from sub using carousel index on landing page
|
||||
:source (resources/get-image :onboarding-bg-1)}]
|
||||
[linear-gradient/linear-gradient
|
||||
{:colors [(if dark-overlay? (colors/custom-color :yin 50) "#000716")
|
||||
(if dark-overlay? (colors/custom-color :yin 50 0) "#000716")]
|
||||
:start {:x 0 :y 0}
|
||||
:end {:x 0 :y 1}
|
||||
:style (style/background-gradient-overlay dark-overlay?)}]
|
||||
(when dark-overlay?
|
||||
[:f>
|
||||
(fn []
|
||||
(carousel.animation/initialize-animation)
|
||||
[rn/view
|
||||
{:style style/background-blur-overlay}])])
|
||||
{: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}])])])
|
||||
|
||||