Merge branch 'develop' into 14620

This commit is contained in:
Ibrahem Khalil 2023-01-08 12:23:37 +02:00 committed by GitHub
commit 9d886a18e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
110 changed files with 3754 additions and 1356 deletions

View File

@ -1,16 +1,18 @@
{:lint-as {status-im.utils.views/defview clojure.core/defn {:lint-as {status-im.utils.views/defview clojure.core/defn
status-im.utils.views/letsubs clojure.core/let status-im.utils.views/letsubs clojure.core/let
reagent.core/with-let clojure.core/let reagent.core/with-let clojure.core/let
status-im.utils.fx/defn clj-kondo.lint-as/def-catch-all status-im.utils.fx/defn clj-kondo.lint-as/def-catch-all
utils.re-frame/defn clj-kondo.lint-as/def-catch-all utils.re-frame/defn clj-kondo.lint-as/def-catch-all
quo.react/with-deps-check clojure.core/fn quo.react/with-deps-check clojure.core/fn
quo.previews.preview/list-comp clojure.core/for quo.previews.preview/list-comp clojure.core/for
status-im.utils.styles/def clojure.core/def status-im.utils.styles/def clojure.core/def
status-im.utils.styles/defn clojure.core/defn status-im.utils.styles/defn clojure.core/defn
status-im.test-helpers/deftest-sub clojure.core/defn test-helpers.unit/deftest-sub clojure.core/defn
taoensso.tufte/defnp clojure.core/defn} taoensso.tufte/defnp clojure.core/defn}
:linters {:consistent-alias {:level :error :linters {:consistent-alias {:level :error
:aliases {clojure.string string :aliases {clojure.string string
clojure.set set
clojure.walk walk
taoensso.timbre log}} taoensso.timbre log}}
:invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]} :invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]}
;; TODO remove number when this is fixed ;; TODO remove number when this is fixed

View File

@ -364,7 +364,7 @@ actually subscribing to them, so reframe's signal graph gets validated too.
(is (= expected (recipes [current-user all-recipes location])))))) (is (= expected (recipes [current-user all-recipes location]))))))
;; good ;; good
(require '[status-im.test-helpers :as h]) (require '[test-helpers.unit :as h])
(re-frame/reg-sub (re-frame/reg-sub
:user/recipes :user/recipes

View File

@ -19,6 +19,11 @@ def getStatusGoSHA1 = { ->
android { android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig { defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion

View File

@ -59,8 +59,12 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.HashMap; import java.util.HashMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.Map; import java.util.Map;
import java.util.Stack; import java.util.Stack;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
@ -527,45 +531,33 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(r); StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod private void executeRunnableStatusGoMethod(Supplier<String> method, Callback callback) throws JSONException {
public void verify(final String address, final String password, final Callback callback) {
Log.d(TAG, "verify");
if (!checkAvailability()) { if (!checkAvailability()) {
callback.invoke(false); callback.invoke(false);
return; return;
} }
Runnable runnableTask = () -> {
String res = method.get();
callback.invoke(res);
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
}
@ReactMethod
public void verify(final String address, final String password, final Callback callback) throws JSONException {
Activity currentActivity = getCurrentActivity(); Activity currentActivity = getCurrentActivity();
final String absRootDirPath = this.getNoBackupDirectory(); final String absRootDirPath = this.getNoBackupDirectory();
final String newKeystoreDir = pathCombine(absRootDirPath, "keystore"); final String newKeystoreDir = pathCombine(absRootDirPath, "keystore");
Runnable r = new Runnable() { executeRunnableStatusGoMethod(() -> Statusgo.verifyAccountPassword(newKeystoreDir, address, password), callback);
@Override
public void run() {
String result = Statusgo.verifyAccountPassword(newKeystoreDir, address, password);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void verifyDatabasePassword(final String keyUID, final String password, final Callback callback) { public void verifyDatabasePassword(final String keyUID, final String password, final Callback callback) throws JSONException {
Log.d(TAG, "verifyDatabasePassword"); executeRunnableStatusGoMethod(() -> Statusgo.verifyDatabasePassword(keyUID, password), callback);
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.verifyDatabasePassword(keyUID, password);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
public String getKeyStorePath(String keyUID) { public String getKeyStorePath(String keyUID) {
@ -735,209 +727,59 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void addPeer(final String enode, final Callback callback) { public void addPeer(final String enode, final Callback callback) throws JSONException {
Log.d(TAG, "addPeer"); executeRunnableStatusGoMethod(() -> Statusgo.addPeer(enode), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.addPeer(enode);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void multiAccountStoreAccount(final String json, final Callback callback) { public void multiAccountStoreAccount(final String json, final Callback callback) throws JSONException {
Log.d(TAG, "multiAccountStoreAccount"); executeRunnableStatusGoMethod(() -> Statusgo.multiAccountStoreAccount(json), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountStoreAccount(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void multiAccountLoadAccount(final String json, final Callback callback) { public void multiAccountLoadAccount(final String json, final Callback callback) throws JSONException {
Log.d(TAG, "multiAccountLoadAccount"); executeRunnableStatusGoMethod(() -> Statusgo.multiAccountLoadAccount(json), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountLoadAccount(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void multiAccountReset(final Callback callback) { public void multiAccountReset(final Callback callback) throws JSONException {
Log.d(TAG, "multiAccountReset"); executeRunnableStatusGoMethod(() -> Statusgo.multiAccountReset(), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountReset();
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void multiAccountDeriveAddresses(final String json, final Callback callback) { public void multiAccountDeriveAddresses(final String json, final Callback callback) throws JSONException {
Log.d(TAG, "multiAccountDeriveAddresses"); executeRunnableStatusGoMethod(() -> Statusgo.multiAccountDeriveAddresses(json), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountDeriveAddresses(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void multiAccountGenerateAndDeriveAddresses(final String json, final Callback callback) { public void multiAccountGenerateAndDeriveAddresses(final String json, final Callback callback) throws JSONException {
Log.d(TAG, "multiAccountGenerateAndDeriveAddresses"); executeRunnableStatusGoMethod(() -> Statusgo.multiAccountGenerateAndDeriveAddresses(json), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountGenerateAndDeriveAddresses(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void multiAccountStoreDerived(final String json, final Callback callback) { public void multiAccountStoreDerived(final String json, final Callback callback) throws JSONException {
Log.d(TAG, "multiAccountStoreDerived"); executeRunnableStatusGoMethod(() -> Statusgo.multiAccountStoreDerivedAccounts(json), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountStoreDerivedAccounts(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void multiAccountImportMnemonic(final String json, final Callback callback) { public void multiAccountImportMnemonic(final String json, final Callback callback) throws JSONException {
Log.d(TAG, "multiAccountImportMnemonic"); executeRunnableStatusGoMethod(() -> Statusgo.multiAccountImportMnemonic(json), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountImportMnemonic(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void multiAccountImportPrivateKey(final String json, final Callback callback) { public void multiAccountImportPrivateKey(final String json, final Callback callback) throws JSONException {
Log.d(TAG, "multiAccountImportPrivateKey"); executeRunnableStatusGoMethod(() -> Statusgo.multiAccountImportPrivateKey(json), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountImportPrivateKey(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void hashTransaction(final String txArgsJSON, final Callback callback) { public void hashTransaction(final String txArgsJSON, final Callback callback) throws JSONException {
Log.d(TAG, "hashTransaction"); executeRunnableStatusGoMethod(() -> Statusgo.hashTransaction(txArgsJSON), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.hashTransaction(txArgsJSON);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void hashMessage(final String message, final Callback callback) { public void hashMessage(final String message, final Callback callback) throws JSONException {
Log.d(TAG, "hashMessage"); executeRunnableStatusGoMethod(() -> Statusgo.hashMessage(message), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.hashMessage(message);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
@ -947,20 +789,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
final String keyStorePath = this.getKeyStorePath(keyUID); final String keyStorePath = this.getKeyStorePath(keyUID);
jsonConfig.put("keystorePath", keyStorePath); jsonConfig.put("keystorePath", keyStorePath);
if (!checkAvailability()) { executeRunnableStatusGoMethod(() -> Statusgo.getConnectionStringForBootstrappingAnotherDevice(jsonConfig.toString()), callback);
callback.invoke(false);
return;
}
Runnable runnableTask = new Runnable() {
@Override
public void run() {
String res = Statusgo.getConnectionStringForBootstrappingAnotherDevice(jsonConfig.toString());
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
} }
@ReactMethod @ReactMethod
@ -969,6 +798,11 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
final String keyStorePath = pathCombine(this.getNoBackupDirectory(), "/keystore"); final String keyStorePath = pathCombine(this.getNoBackupDirectory(), "/keystore");
jsonConfig.put("keystorePath", keyStorePath); jsonConfig.put("keystorePath", keyStorePath);
executeRunnableStatusGoMethod(() -> Statusgo.inputConnectionStringForBootstrapping(connectionString, jsonConfig.toString()), callback);
}
@ReactMethod
public void multiformatSerializePublicKey(final String multiCodecKey, final String base58btc, final Callback callback) throws JSONException {
if (!checkAvailability()) { if (!checkAvailability()) {
callback.invoke(false); callback.invoke(false);
return; return;
@ -977,7 +811,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
Runnable runnableTask = new Runnable() { Runnable runnableTask = new Runnable() {
@Override @Override
public void run() { public void run() {
String res = Statusgo.inputConnectionStringForBootstrapping(connectionString,jsonConfig.toString()); String res = Statusgo.multiformatSerializePublicKey(multiCodecKey,base58btc);
callback.invoke(res); callback.invoke(res);
} }
}; };
@ -985,157 +819,117 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(runnableTask); StatusThreadPoolExecutor.getInstance().execute(runnableTask);
} }
@ReactMethod @ReactMethod
public void hashTypedData(final String data, final Callback callback) { public void multiformatDeserializePublicKey(final String multiCodecKey, final String base58btc, final Callback callback) throws JSONException {
Log.d(TAG, "hashTypedData");
if (!checkAvailability()) { if (!checkAvailability()) {
callback.invoke(false); callback.invoke(false);
return; return;
} }
Runnable r = new Runnable() { Runnable runnableTask = new Runnable() {
@Override @Override
public void run() { public void run() {
String res = Statusgo.hashTypedData(data); String res = Statusgo.multiformatDeserializePublicKey(multiCodecKey,base58btc);
callback.invoke(res); callback.invoke(res);
} }
}; };
StatusThreadPoolExecutor.getInstance().execute(r); StatusThreadPoolExecutor.getInstance().execute(runnableTask);
} }
@ReactMethod @ReactMethod
public void hashTypedDataV4(final String data, final Callback callback) { public void compressPublicKey(final String multiCodecKey, final Callback callback) throws JSONException {
Log.d(TAG, "hashTypedDataV4");
if (!checkAvailability()) { if (!checkAvailability()) {
callback.invoke(false); callback.invoke(false);
return; return;
} }
Runnable r = new Runnable() { Runnable runnableTask = new Runnable() {
@Override @Override
public void run() { public void run() {
String res = Statusgo.hashTypedDataV4(data); String res = Statusgo.compressPublicKey(multiCodecKey);
callback.invoke(res); callback.invoke(res);
} }
}; };
StatusThreadPoolExecutor.getInstance().execute(r); StatusThreadPoolExecutor.getInstance().execute(runnableTask);
} }
@ReactMethod @ReactMethod
public void sendTransactionWithSignature(final String txArgsJSON, final String signature, final Callback callback) { public void decompressPublicKey(final String multiCodecKey, final Callback callback) throws JSONException {
Log.d(TAG, "sendTransactionWithSignature");
if (!checkAvailability()) { if (!checkAvailability()) {
callback.invoke(false); callback.invoke(false);
return; return;
} }
Runnable r = new Runnable() { Runnable runnableTask = new Runnable() {
@Override @Override
public void run() { public void run() {
String res = Statusgo.sendTransactionWithSignature(txArgsJSON, signature); String res = Statusgo.decompressPublicKey(multiCodecKey);
callback.invoke(res); callback.invoke(res);
} }
}; };
StatusThreadPoolExecutor.getInstance().execute(r); StatusThreadPoolExecutor.getInstance().execute(runnableTask);
} }
@ReactMethod @ReactMethod
public void sendTransaction(final String txArgsJSON, final String password, final Callback callback) { public void deserializeAndCompressKey(final String desktopKey, final Callback callback) throws JSONException {
Log.d(TAG, "sendTransaction");
if (!checkAvailability()) { if (!checkAvailability()) {
callback.invoke(false); callback.invoke(false);
return; return;
} }
Runnable r = new Runnable() { Runnable runnableTask = new Runnable() {
@Override @Override
public void run() { public void run() {
String res = Statusgo.sendTransaction(txArgsJSON, password); String res = Statusgo.deserializeAndCompressKey(desktopKey);
callback.invoke(res); callback.invoke(res);
} }
}; };
StatusThreadPoolExecutor.getInstance().execute(r); StatusThreadPoolExecutor.getInstance().execute(runnableTask);
} }
@ReactMethod @ReactMethod
public void signMessage(final String rpcParams, final Callback callback) { public void hashTypedData(final String data, final Callback callback) throws JSONException {
Log.d(TAG, "signMessage"); executeRunnableStatusGoMethod(() -> Statusgo.hashTypedData(data), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.signMessage(rpcParams);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void recover(final String rpcParams, final Callback callback) { public void hashTypedDataV4(final String data, final Callback callback) throws JSONException {
Log.d(TAG, "recover"); executeRunnableStatusGoMethod(() -> Statusgo.hashTypedDataV4(data), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.recover(rpcParams);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void signTypedData(final String data, final String account, final String password, final Callback callback) { public void sendTransactionWithSignature(final String txArgsJSON, final String signature, final Callback callback) throws JSONException {
Log.d(TAG, "signTypedData"); executeRunnableStatusGoMethod(() -> Statusgo.sendTransactionWithSignature(txArgsJSON, signature), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.signTypedData(data, account, password);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void signTypedDataV4(final String data, final String account, final String password, final Callback callback) { public void sendTransaction(final String txArgsJSON, final String password, final Callback callback) throws JSONException {
Log.d(TAG, "signTypedDataV4"); executeRunnableStatusGoMethod(() -> Statusgo.sendTransaction(txArgsJSON, password), callback);
if (!checkAvailability()) { }
callback.invoke(false);
return;
}
Runnable r = new Runnable() { @ReactMethod
@Override public void signMessage(final String rpcParams, final Callback callback) throws JSONException {
public void run() { executeRunnableStatusGoMethod(() -> Statusgo.signMessage(rpcParams), callback);
String res = Statusgo.signTypedDataV4(data, account, password); }
callback.invoke(res);
} @ReactMethod
}; public void recover(final String rpcParams, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.recover(rpcParams), callback);
}
@ReactMethod
public void signTypedData(final String data, final String account, final String password, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.signTypedData(data, account, password), callback);
}
@ReactMethod
public void signTypedDataV4(final String data, final String account, final String password, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.signTypedDataV4(data, account, password), callback);
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
@ -1240,29 +1034,13 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void callRPC(final String payload, final Callback callback) { public void callRPC(final String payload, final Callback callback) throws JSONException {
Runnable r = new Runnable() { executeRunnableStatusGoMethod(() -> Statusgo.callRPC(payload), callback);
@Override
public void run() {
String res = Statusgo.callRPC(payload);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void callPrivateRPC(final String payload, final Callback callback) { public void callPrivateRPC(final String payload, final Callback callback) throws JSONException {
Runnable r = new Runnable() { executeRunnableStatusGoMethod(() -> Statusgo.callPrivateRPC(payload), callback);
@Override
public void run() {
String res = Statusgo.callPrivateRPC(payload);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
@ -1325,105 +1103,30 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void extractGroupMembershipSignatures(final String signaturePairs, final Callback callback) { public void extractGroupMembershipSignatures(final String signaturePairs, final Callback callback) throws JSONException {
Log.d(TAG, "extractGroupMembershipSignatures"); executeRunnableStatusGoMethod(() -> Statusgo.extractGroupMembershipSignatures(signaturePairs), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.extractGroupMembershipSignatures(signaturePairs);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void signGroupMembership(final String content, final Callback callback) { public void signGroupMembership(final String content, final Callback callback) throws JSONException {
Log.d(TAG, "signGroupMembership"); executeRunnableStatusGoMethod(() -> Statusgo.signGroupMembership(content), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.signGroupMembership(content);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void getNodeConfig(final Callback callback) { public void getNodeConfig(final Callback callback) throws JSONException {
Log.d(TAG, "getNodeConfig"); executeRunnableStatusGoMethod(() -> Statusgo.getNodeConfig(), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.getNodeConfig();
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void deleteMultiaccount(final String keyUID, final Callback callback) { public void deleteMultiaccount(final String keyUID, final Callback callback) throws JSONException {
Log.d(TAG, "deleteMultiaccount");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
final String keyStoreDir = this.getKeyStorePath(keyUID); final String keyStoreDir = this.getKeyStorePath(keyUID);
Runnable r = new Runnable() { executeRunnableStatusGoMethod(() -> Statusgo.deleteMultiaccount(keyUID, keyStoreDir), callback);
@Override
public void run() {
String res = Statusgo.deleteMultiaccount(keyUID, keyStoreDir);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void deleteImportedKey(final String keyUID, final String address, final String password, final Callback callback) { public void deleteImportedKey(final String keyUID, final String address, final String password, final Callback callback) throws JSONException {
Log.d(TAG, "deleteImportedKey");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
final String keyStoreDir = this.getKeyStorePath(keyUID); final String keyStoreDir = this.getKeyStorePath(keyUID);
Runnable r = new Runnable() { executeRunnableStatusGoMethod(() -> Statusgo.deleteImportedKey(address, password, keyStoreDir), callback);
@Override
public void run() {
String res = Statusgo.deleteImportedKey(address, password, keyStoreDir);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod(isBlockingSynchronousMethod = true) @ReactMethod(isBlockingSynchronousMethod = true)
@ -1432,24 +1135,8 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void generateAliasAsync(final String seed, final Callback callback) { public void generateAliasAsync(final String seed, final Callback callback) throws JSONException {
Log.d(TAG, "generateAliasAsync"); executeRunnableStatusGoMethod(() -> Statusgo.generateAlias(seed), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.generateAlias(seed);
Log.d(TAG, res);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod(isBlockingSynchronousMethod = true) @ReactMethod(isBlockingSynchronousMethod = true)
@ -1513,47 +1200,33 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void identiconAsync(final String seed, final Callback callback) { public void identiconAsync(final String seed, final Callback callback) throws JSONException {
Log.d(TAG, "identiconAsync"); executeRunnableStatusGoMethod(() -> Statusgo.identicon(seed), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.identicon(seed);
Log.d(TAG, res);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void generateAliasAndIdenticonAsync(final String seed, final Callback callback) { public void generateAliasAndIdenticonAsync(final String seed, final Callback callback) {
Log.d(TAG, "generateAliasAndIdenticonAsync");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() { Log.d(TAG, "generateAliasAndIdenticonAsync");
@Override if (!checkAvailability()) {
public void run() { callback.invoke(false);
String resIdenticon = Statusgo.identicon(seed); return;
String resAlias = Statusgo.generateAlias(seed); }
Log.d(TAG, resIdenticon); Runnable r = new Runnable() {
Log.d(TAG, resAlias); @Override
callback.invoke(resAlias, resIdenticon); public void run() {
} String resIdenticon = Statusgo.identicon(seed);
}; String resAlias = Statusgo.generateAlias(seed);
Log.d(TAG, resIdenticon);
Log.d(TAG, resAlias);
callback.invoke(resAlias, resIdenticon);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@Override @Override
@ -1575,24 +1248,8 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void validateMnemonic(final String seed, final Callback callback) { public void validateMnemonic(final String seed, final Callback callback) throws JSONException {
Log.d(TAG, "validateMnemonic"); executeRunnableStatusGoMethod(() -> Statusgo.validateMnemonic(seed), callback);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String resValidateMnemonic = Statusgo.validateMnemonic(seed);
Log.d(TAG, resValidateMnemonic);
callback.invoke(resValidateMnemonic);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
@ -1643,35 +1300,14 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void reEncryptDbAndKeystore(final String keyUID, final String password, final String newPassword, final Callback callback) { public void reEncryptDbAndKeystore(final String keyUID, final String password, final String newPassword, final Callback callback) throws JSONException {
Log.d(TAG, "reEncryptDbAndKeyStore"); executeRunnableStatusGoMethod(() -> Statusgo.changeDatabasePassword(keyUID, password, newPassword), callback);
Runnable r = new Runnable() {
@Override
public void run() {
// changes db password and re-encrypts keystore
String result = Statusgo.changeDatabasePassword(keyUID, password, newPassword);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod @ReactMethod
public void convertToKeycardAccount(final String keyUID, final String accountData, final String options, final String password, final String newPassword, final Callback callback) { public void convertToKeycardAccount(final String keyUID, final String accountData, final String options, final String password, final String newPassword, final Callback callback) throws JSONException {
Log.d(TAG, "convertToKeycardAccount");
final String keyStoreDir = this.getKeyStorePath(keyUID); final String keyStoreDir = this.getKeyStorePath(keyUID);
Runnable r = new Runnable() { executeRunnableStatusGoMethod(() -> Statusgo.convertToKeycardAccount(keyStoreDir, accountData, options, password, newPassword), callback);
@Override
public void run() {
String result = Statusgo.convertToKeycardAccount(keyStoreDir, accountData, options, password, newPassword);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
} }
} }

View File

@ -339,6 +339,38 @@ RCT_EXPORT_METHOD(inputConnectionStringForBootstrapping:(NSString *)cs
callback(@[result]); callback(@[result]);
} }
RCT_EXPORT_METHOD(multiformatSerializePublicKey:(NSString *)multiCodecKey
base58btc:(NSString *)base58btc
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoMultiformatSerializePublicKey(multiCodecKey,base58btc);
callback(@[result]);
}
RCT_EXPORT_METHOD(multiformatDeserializePublicKey:(NSString *)multiCodecKey
base58btc:(NSString *)base58btc
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoMultiformatDeserializePublicKey(multiCodecKey,base58btc);
callback(@[result]);
}
RCT_EXPORT_METHOD(decompressPublicKey:(NSString *)multiCodecKey
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoDecompressPublicKey(multiCodecKey);
callback(@[result]);
}
RCT_EXPORT_METHOD(compressPublicKey:(NSString *)multiCodecKey
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoCompressPublicKey(multiCodecKey);
callback(@[result]);
}
RCT_EXPORT_METHOD(deserializeAndCompressKey:(NSString *)desktopKey
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoDeserializeAndCompressKey(desktopKey);
callback(@[result]);
}
RCT_EXPORT_METHOD(hashTypedData:(NSString *)data RCT_EXPORT_METHOD(hashTypedData:(NSString *)data
callback:(RCTResponseSenderBlock)callback) { callback:(RCTResponseSenderBlock)callback) {
#if DEBUG #if DEBUG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -83,7 +83,7 @@
:optimizations :simple :optimizations :simple
:target :node-test :target :node-test
;; When running tests without a REPL you can uncomment below line to `make test-watch` a specific file ;; When running tests without a REPL you can uncomment below line to `make test-watch` a specific file
;; :ns-regexp "status-im.chat.models-test$" ;;:ns-regexp "status-im2.subs.subs-test$"
:main :main
status-im.test-runner/main status-im.test-runner/main
;; set :ui-driven to true to let shadow-cljs inject node-repl ;; set :ui-driven to true to let shadow-cljs inject node-repl

View File

@ -1,78 +1,14 @@
(ns i18n.i18n (ns i18n.i18n
(:require ["i18n-js" :as i18n] (:require ["i18n-js" :as i18n]
[clojure.string :as string] [clojure.string :as string]
[status-im.goog.i18n :as goog.i18n] [status-im.goog.i18n :as goog.i18n]))
[react-native.languages :as react-native-languages]))
(def default-device-language (react-native-languages/get-lang-keyword)) (defn setup
[default-device-language translations-by-locale]
(def languages
#{:ar :bn :de :el :en :es :es_419 :es_AR :fil :fr :hi :id :in :it :ja :ko :ms :nl :pl :pt :pt_BR :ru
:tr :vi :zh :zh_Hant :zh_TW})
(defn valid-language
[lang]
(if (contains? languages lang)
(keyword lang)
(let [parts (string/split (name lang) #"[\-\_]")
short-lang (keyword (str (first parts) "_" (second parts)))
shortest-lang (keyword (first parts))]
(if (and (> (count parts) 2) (contains? languages short-lang))
short-lang
(when (contains? languages shortest-lang)
shortest-lang)))))
(defn require-translation
[lang-key]
(when-let [lang (valid-language (keyword lang-key))]
(case lang
:ar (js/require "../translations/ar.json")
:bn (js/require "../translations/bn.json")
:de (js/require "../translations/de.json")
:el (js/require "../translations/el.json")
:en (js/require "../translations/en.json")
:es (js/require "../translations/es.json")
:es_419 (js/require "../translations/es_419.json")
:es_AR (js/require "../translations/es_AR.json")
:fil (js/require "../translations/fil.json")
:fr (js/require "../translations/fr.json")
:hi (js/require "../translations/hi.json")
:id (js/require "../translations/id.json")
:in (js/require "../translations/id.json")
:it (js/require "../translations/it.json")
:ja (js/require "../translations/ja.json")
:ko (js/require "../translations/ko.json")
:ms (js/require "../translations/ms.json")
:nl (js/require "../translations/nl.json")
:pl (js/require "../translations/pl.json")
:pt (js/require "../translations/pt.json")
:pt_BR (js/require "../translations/pt_BR.json")
:ru (js/require "../translations/ru.json")
:tr (js/require "../translations/tr.json")
:vi (js/require "../translations/vi.json")
:zh (js/require "../translations/zh.json")
:zh_Hant (js/require "../translations/zh_hant.json")
:zh_TW (js/require "../translations/zh_TW.json"))))
(def translations-by-locale
(cond-> {:en (require-translation :en)}
(not= :en default-device-language)
(assoc default-device-language
(require-translation (-> (name default-device-language)
(string/replace "-" "_")
keyword)))))
(set! (.-fallbacks i18n) true)
(set! (.-defaultSeparator i18n) "/")
(set! (.-locale i18n) (name default-device-language))
(set! (.-translations i18n) (clj->js translations-by-locale))
(defn init
[]
(set! (.-fallbacks i18n) true) (set! (.-fallbacks i18n) true)
(set! (.-defaultSeparator i18n) "/") (set! (.-defaultSeparator i18n) "/")
(set! (.-locale i18n) (name default-device-language)) (set! (.-locale i18n) default-device-language)
(set! (.-translations i18n) (clj->js translations-by-locale))) (set! (.-translations i18n) translations-by-locale))
(defn get-translations (defn get-translations
[] []
@ -134,12 +70,3 @@
(.-locale i18n)) (.-locale i18n))
(def format-currency goog.i18n/format-currency) (def format-currency goog.i18n/format-currency)
(defn load-language
[lang loaded-languages]
(when-let [lang-key (valid-language (keyword lang))]
(when-not (contains? @loaded-languages lang-key)
(aset (i18n/get-translations)
lang
(require-translation lang-key))
(swap! loaded-languages conj lang-key))))

View File

@ -141,7 +141,7 @@
(if profile-picture (if profile-picture
;; display image ;; display image
[fast-image/fast-image [fast-image/fast-image
{:source {:uri profile-picture} {:source profile-picture
:style (container-styling inner-dimensions outer-dimensions)}] :style (container-styling inner-dimensions outer-dimensions)}]
;; else display initials ;; else display initials
[container inner-dimensions outer-dimensions [container inner-dimensions outer-dimensions

View File

@ -32,9 +32,9 @@
:outline {:icon-color colors/neutral-50 :outline {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50 :icon-secondary-color colors/neutral-50
:label-color colors/neutral-100 :label-color colors/neutral-100
:border-color {:default colors/neutral-20 :border-color {:default colors/neutral-30
:pressed colors/neutral-40 :pressed colors/neutral-40
:disabled colors/neutral-20}} :disabled colors/neutral-30}}
:ghost {:icon-color colors/neutral-50 :ghost {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50 :icon-secondary-color colors/neutral-50
:label-color colors/neutral-100 :label-color colors/neutral-100
@ -138,7 +138,8 @@
(defn shape-style-container (defn shape-style-container
[type icon size] [type icon size]
{:border-radius (if (and icon (#{:primary :secondary :danger} type)) {:height size
:border-radius (if (and icon (#{:primary :secondary :danger} type))
24 24
(case size (case size
56 12 56 12

View File

@ -81,19 +81,21 @@
context)))) context))))
(defn- activity-message (defn- activity-message
[{:keys [title body]}] [{:keys [title body title-number-of-lines body-number-of-lines]}]
[rn/view {:style style/message-container} [rn/view {:style style/message-container}
(when title (when title
[text/text [text/text
{:size :paragraph-2 {:size :paragraph-2
:accessibility-label :activity-message-title :accessibility-label :activity-message-title
:style style/message-title} :style style/message-title
:number-of-lines title-number-of-lines}
title]) title])
(if (string? body) (if (string? body)
[text/text [text/text
{:style style/message-body {:style style/message-body
:accessibility-label :activity-message-body :accessibility-label :activity-message-body
:size :paragraph-1} :size :paragraph-1
:number-of-lines body-number-of-lines}
body] body]
body)]) body)])

View File

@ -0,0 +1,40 @@
(ns quo2.components.profile.profile-card.style
(:require [quo2.foundations.colors :as colors]))
(defn card-container
[customization-color]
{:flex-direction :column
:padding 12
:flex 1
:border-radius 16
:background-color (colors/custom-color customization-color 50 40)})
(def card-header
{:flex-direction :row
:justify-content :space-between})
(def name-container
{:flex-direction :row
:margin-top 8
:margin-bottom 2
:align-items :center
:padding-right 12})
(def user-name
{:margin-right 4
:color colors/white})
(def emoji-hash
{:margin-top 10})
(def user-hash
{:color colors/white-opa-60})
(def sign-button
{:margin-top 14})
(def keycard-icon
{:color colors/white-opa-40})
(def option-button
{:background-color colors/white-opa-5})

View File

@ -0,0 +1,64 @@
(ns quo2.components.profile.profile-card.view
(:require
[quo2.components.profile.profile-card.style :as style]
[quo2.foundations.colors :as colors]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.components.buttons.button :as button]
[react-native.core :as rn]))
(defn profile-card
[{:keys [show-sign-profile? key-card? profile-picture name hash customization-color sign-label
emoji-hash on-press-dots on-press-sign show-emoji-hash?]
:or {show-sign-profile? false
show-emoji-hash? false
customization-color :turquoise
key-card? false}}]
[rn/view
{:style (style/card-container customization-color)}
[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
:ring? true}]
[button/button
{:size 32
:type :blur-bg
:icon true
:override-theme :dark
:style style/option-button
:on-press on-press-dots}
: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))]
[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])
(when show-sign-profile?
[button/button
{:on-press on-press-sign
:type :community
:community-color (colors/custom-color customization-color 60)
:community-text-color colors/white
:style style/sign-button} sign-label])])

View File

@ -6,12 +6,6 @@
[quo2.theme :as quo2.theme] [quo2.theme :as quo2.theme]
[react-native.core :as rn])) [react-native.core :as rn]))
(defn padding-left-for-type
[type]
(case type
:group-avatar 3
8))
(defn trim-public-key (defn trim-public-key
[pk] [pk]
(str (subs pk 0 6) "..." (subs pk (- (count pk) 3)))) (str (subs pk 0 6) "..." (subs pk (- (count pk) 3))))
@ -30,7 +24,7 @@
:padding-left 8 :padding-left 8
:background-color (if (= theme :light) :background-color (if (= theme :light)
colors/neutral-10 colors/neutral-10
colors/neutral-80)} colors/neutral-90)}
style)] style)]
children)))) children))))
@ -40,7 +34,8 @@
[base-tag [base-tag
(-> opts (-> opts
(select-keys [:override-theme :style]) (select-keys [:override-theme :style])
(assoc-in [:style :padding-left] 3)) (assoc-in [:style :padding-left] 3)
(assoc-in [:style :padding-vertical] 2))
[group-avatar/group-avatar opts] [group-avatar/group-avatar opts]
[text/text [text/text
{:weight :medium {:weight :medium
@ -69,7 +64,7 @@
[rn/image [rn/image
{:style {:width 20 {:style {:width 20
:border-radius 10 :border-radius 10
:background-color :white :background-color :red
:height 20} :height 20}
:source photo}] :source photo}]
[rn/view [rn/view
@ -88,3 +83,44 @@
[] []
(fn [params username photo] (fn [params username photo]
[context-tag params {:uri photo} username])) [context-tag params {:uri photo} username]))
(defn audio-tag
[duration params]
[base-tag
(merge
{:style {:padding-left 2
:padding-vertical 2}}
params)
[rn/view
{:width 20
:height 20
:border-radius 10
:align-items :center
:justify-content :center
:background-color colors/primary-50}
[icons/icon
:i/play
{:color colors/white
:size 12}]]
[text/text
{:weight :medium
:size :paragraph-2
:style {:margin-left 4
:color (colors/theme-colors
colors/neutral-100
colors/white
(:override-theme params))}}
duration]])
(defn community-tag
[avatar community-name params]
[context-tag
(merge
{:style {:padding-vertical 2}
:text-style {:margin-left 2
:color (colors/theme-colors
colors/neutral-100
colors/white
(:override-theme params))}}
params)
avatar community-name])

View File

@ -52,6 +52,7 @@
quo2.components.tabs.tabs quo2.components.tabs.tabs
quo2.components.tags.context-tags quo2.components.tags.context-tags
quo2.components.tags.status-tags quo2.components.tags.status-tags
quo2.components.profile.profile-card.view
quo2.components.tags.tags)) quo2.components.tags.tags))
(def toast quo2.components.notifications.toast/toast) (def toast quo2.components.notifications.toast/toast)
@ -74,6 +75,8 @@
(def user-avatar-tag quo2.components.tags.context-tags/user-avatar-tag) (def user-avatar-tag quo2.components.tags.context-tags/user-avatar-tag)
(def context-tag quo2.components.tags.context-tags/context-tag) (def context-tag quo2.components.tags.context-tags/context-tag)
(def group-avatar-tag quo2.components.tags.context-tags/group-avatar-tag) (def group-avatar-tag quo2.components.tags.context-tags/group-avatar-tag)
(def audio-tag quo2.components.tags.context-tags/audio-tag)
(def community-tag quo2.components.tags.context-tags/community-tag)
(def tabs quo2.components.tabs.tabs/tabs) (def tabs quo2.components.tabs.tabs/tabs)
(def scrollable-tabs quo2.components.tabs.tabs/scrollable-tabs) (def scrollable-tabs quo2.components.tabs.tabs/scrollable-tabs)
(def account-selector quo2.components.tabs.account-selector/account-selector) (def account-selector quo2.components.tabs.account-selector/account-selector)
@ -127,6 +130,9 @@
(def notification-dot quo2.components.notifications.notification-dot/notification-dot) (def notification-dot quo2.components.notifications.notification-dot/notification-dot)
(def count-down-circle quo2.components.notifications.count-down-circle/circle-timer) (def count-down-circle quo2.components.notifications.count-down-circle/circle-timer)
;;;; PROFILE
(def profile-card quo2.components.profile.profile-card.view/profile-card)
;;;; SETTINGS ;;;; SETTINGS
(def privacy-option quo2.components.settings.privacy-option/card) (def privacy-option quo2.components.settings.privacy-option/card)
(def account quo2.components.settings.accounts.view/account) (def account quo2.components.settings.accounts.view/account)

View File

@ -184,9 +184,11 @@
(defn build-image-messages (defn build-image-messages
[{db :db} chat-id] [{db :db} chat-id]
(let [images (get-in db [:chat/inputs chat-id :metadata :sending-image])] (let [images (get-in db [:chat/inputs chat-id :metadata :sending-image])
album-id (str (random-uuid))]
(mapv (fn [[_ {:keys [uri]}]] (mapv (fn [[_ {:keys [uri]}]]
{:chat-id chat-id {:chat-id chat-id
:album-id album-id
:content-type constants/content-type-image :content-type constants/content-type-image
:image-path (utils/safe-replace uri #"file://" "") :image-path (utils/safe-replace uri #"file://" "")
:text (i18n/label :t/update-to-see-image {"locale" "en"})}) :text (i18n/label :t/update-to-see-image {"locale" "en"})})

View File

@ -4,9 +4,9 @@
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.data-store.chats :as data-store.chats] [status-im.data-store.chats :as data-store.chats]
[status-im.data-store.messages :as data-store.messages] [status-im.data-store.messages :as data-store.messages]
[utils.re-frame :as rf]
[status-im2.contexts.activity-center.events :as activity-center] [status-im2.contexts.activity-center.events :as activity-center]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defn cursor->clock-value (defn cursor->clock-value
[^js cursor] [^js cursor]
@ -101,6 +101,17 @@
:on-success #(re-frame/dispatch :on-success #(re-frame/dispatch
[::mark-all-read-in-community-successful %])}]})) [::mark-all-read-in-community-successful %])}]}))
;; For example, when a user receives a list of 4 image messages while inside the chat screen we
;; shouldn't group the images into albums. When the user exists the chat screen then enters the
;; chat screen again, we now need to group the images into albums (like WhatsApp). The albumize?
;; boolean is used to know whether we need to group these images into albums now or not. The
;; album-id can't be used for this because it will always be there.
(defn mark-album
[message]
(if (:album-id message)
(assoc message :albumize? true)
message))
(rf/defn messages-loaded (rf/defn messages-loaded
"Loads more messages for current chat" "Loads more messages for current chat"
{:events [::messages-loaded]} {:events [::messages-loaded]}
@ -131,8 +142,8 @@
current-clock-value (get-in db current-clock-value (get-in db
[:pagination-info chat-id [:pagination-info chat-id
:cursor-clock-value]) :cursor-clock-value])
clock-value (when cursor clock-value (when cursor (cursor->clock-value cursor))
(cursor->clock-value cursor))] new-messages (map mark-album new-messages)]
{:dispatch [:chat/add-senders-to-chat-users (vals senders)] {:dispatch [:chat/add-senders-to-chat-users (vals senders)]
:db (-> db :db (-> db
(update-in [:pagination-info chat-id :cursor-clock-value] (update-in [:pagination-info chat-id :cursor-clock-value]

View File

@ -20,7 +20,8 @@
from from
outgoing outgoing
whisper-timestamp whisper-timestamp
deleted-for-me?]}] deleted-for-me?
albumize?]}]
(-> {:whisper-timestamp whisper-timestamp (-> {:whisper-timestamp whisper-timestamp
:from from :from from
:one-to-one? (= constants/message-type-one-to-one message-type) :one-to-one? (= constants/message-type-one-to-one message-type)
@ -32,7 +33,8 @@
:clock-value clock-value :clock-value clock-value
:type :message :type :message
:message-id message-id :message-id message-id
:outgoing (boolean outgoing)} :outgoing (boolean outgoing)
:albumize? albumize?}
add-datemark add-datemark
add-timestamp)) add-timestamp))

View File

@ -1,5 +1,5 @@
(ns status-im.communities.core (ns status-im.communities.core
(:require [clojure.set :as clojure.set] (:require [clojure.set :as set]
[clojure.string :as string] [clojure.string :as string]
[clojure.walk :as walk] [clojure.walk :as walk]
[quo.design-system.colors :as colors] [quo.design-system.colors :as colors]
@ -28,10 +28,10 @@
(defn <-request-to-join-community-rpc (defn <-request-to-join-community-rpc
[r] [r]
(clojure.set/rename-keys r (set/rename-keys r
{:communityId :community-id {:communityId :community-id
:publicKey :public-key :publicKey :public-key
:chatId :chat-id})) :chatId :chat-id}))
(defn <-requests-to-join-community-rpc (defn <-requests-to-join-community-rpc
[requests] [requests]
@ -64,12 +64,12 @@
(defn <-rpc (defn <-rpc
[c] [c]
(-> c (-> c
(clojure.set/rename-keys {:canRequestAccess :can-request-access? (set/rename-keys {:canRequestAccess :can-request-access?
:canManageUsers :can-manage-users? :canManageUsers :can-manage-users?
:canDeleteMessageForEveryone :can-delete-message-for-everyone? :canDeleteMessageForEveryone :can-delete-message-for-everyone?
:canJoin :can-join? :canJoin :can-join?
:requestedToJoinAt :requested-to-join-at :requestedToJoinAt :requested-to-join-at
:isMember :is-member?}) :isMember :is-member?})
(update :members walk/stringify-keys) (update :members walk/stringify-keys)
(update :chats <-chats-rpc) (update :chats <-chats-rpc)
(update :categories <-categories-rpc))) (update :categories <-categories-rpc)))

View File

@ -205,9 +205,26 @@
(def ^:const community-member-role-manage-users 2) (def ^:const community-member-role-manage-users 2)
(def ^:const community-member-role-moderator 3) (def ^:const community-member-role-moderator 3)
(def local-pairing-connection-string-identifier (def ^:const local-pairing-connection-string-identifier
"If any string begins with cs we know its a connection string. "If any string begins with cs we know its a connection string.
This is useful when we read QR codes we know it is a connection string if it begins with this identifier. This is useful when we read QR codes we know it is a connection string if it begins with this identifier.
An example of a connection string is -> cs2:5vd6J6:Jfc:27xMmHKEYwzRGXcvTtuiLZFfXscMx4Mz8d9wEHUxDj4p7:EG7Z13QScfWBJNJ5cprszzDQ5fBVsYMirXo8MaQFJvpF:3 " An example of a connection string is -> cs2:5vd6J6:Jfc:27xMmHKEYwzRGXcvTtuiLZFfXscMx4Mz8d9wEHUxDj4p7:EG7Z13QScfWBJNJ5cprszzDQ5fBVsYMirXo8MaQFJvpF:3 "
"cs") "cs")
(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.
according to https://specs.status.im/spec/2#public-key-serialization we serialize
keys with base58btc encoding"
"z")
(def ^:const deserialization-key
"We pass this deserialization key as a parameter to MultiformatDeserializePublicKey
function at status-go, This key determines the output base of the deserialization.
according to https://specs.status.im/spec/2#public-key-serialization we deserialize
keys with base16 hexadecimal encoding"
"f")
(def ^:const multi-code-prefix
"We prefix our keys with 0xe701 prior to serialisation them"
"0xe701")

View File

@ -1,5 +1,5 @@
(ns status-im.contact.db (ns status-im.contact.db
(:require [clojure.set :as clojure.set] (:require [clojure.set :as set]
[clojure.string :as string] [clojure.string :as string]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
@ -68,8 +68,8 @@
(let [current-contact (some-> (let [current-contact (some->
current-account current-account
(select-keys [:name :preferred-name :public-key :identicon :images]) (select-keys [:name :preferred-name :public-key :identicon :images])
(clojure.set/rename-keys {:name :alias (set/rename-keys {:name :alias
:preferred-name :name})) :preferred-name :name}))
all-contacts (cond-> contacts all-contacts (cond-> contacts
current-contact current-contact
(assoc public-key current-contact))] (assoc public-key current-contact))]

View File

@ -1,5 +1,5 @@
(ns status-im.data-store.chats (ns status-im.data-store.chats
(:require [clojure.set :as clojure.set] (:require [clojure.set :as set]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.data-store.messages :as messages] [status-im.data-store.messages :as messages]
[utils.re-frame :as rf] [utils.re-frame :as rf]
@ -58,19 +58,19 @@
(defn <-rpc (defn <-rpc
[chat] [chat]
(-> chat (-> chat
(clojure.set/rename-keys {:id :chat-id (set/rename-keys {:id :chat-id
:communityId :community-id :communityId :community-id
:syncedFrom :synced-from :syncedFrom :synced-from
:syncedTo :synced-to :syncedTo :synced-to
:membershipUpdateEvents :membership-update-events :membershipUpdateEvents :membership-update-events
:deletedAtClockValue :deleted-at-clock-value :deletedAtClockValue :deleted-at-clock-value
:chatType :chat-type :chatType :chat-type
:unviewedMessagesCount :unviewed-messages-count :unviewedMessagesCount :unviewed-messages-count
:unviewedMentionsCount :unviewed-mentions-count :unviewedMentionsCount :unviewed-mentions-count
:lastMessage :last-message :lastMessage :last-message
:lastClockValue :last-clock-value :lastClockValue :last-clock-value
:invitationAdmin :invitation-admin :invitationAdmin :invitation-admin
:profile :profile-public-key}) :profile :profile-public-key})
rpc->type rpc->type
unmarshal-members unmarshal-members
(update :last-message #(when % (messages/<-rpc %))) (update :last-message #(when % (messages/<-rpc %)))

View File

@ -1,12 +1,12 @@
(ns status-im.data-store.contacts (ns status-im.data-store.contacts
(:require [clojure.set :as clojure.set] (:require [clojure.set :as set]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(defn <-rpc (defn <-rpc
[contact] [contact]
(-> contact (-> contact
(clojure.set/rename-keys (set/rename-keys
{:id :public-key {:id :public-key
:ensVerifiedAt :ens-verified-at :ensVerifiedAt :ens-verified-at
:displayName :display-name :displayName :display-name

View File

@ -1,5 +1,5 @@
(ns status-im.data-store.messages (ns status-im.data-store.messages
(:require [clojure.set :as clojure.set] (:require [clojure.set :as set]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
@ -10,39 +10,40 @@
(assoc :text (:text content) (assoc :text (:text content)
:sticker (:sticker content)) :sticker (:sticker content))
:always :always
(clojure.set/rename-keys {:chat-id :chat_id (set/rename-keys {:chat-id :chat_id
:whisper-timestamp :whisperTimestamp :whisper-timestamp :whisperTimestamp
:community-id :communityId :community-id :communityId
:clock-value :clock}))) :clock-value :clock})))
(defn <-rpc (defn <-rpc
[message] [message]
(-> message (-> message
(clojure.set/rename-keys {:id :message-id (set/rename-keys {:id :message-id
:whisperTimestamp :whisper-timestamp :whisperTimestamp :whisper-timestamp
:editedAt :edited-at :editedAt :edited-at
:contactVerificationState :contact-verification-state :contactVerificationState :contact-verification-state
:contactRequestState :contact-request-state :contactRequestState :contact-request-state
:commandParameters :command-parameters :commandParameters :command-parameters
:gapParameters :gap-parameters :gapParameters :gap-parameters
:messageType :message-type :messageType :message-type
:localChatId :chat-id :localChatId :chat-id
:communityId :community-id :communityId :community-id
:contentType :content-type :contentType :content-type
:clock :clock-value :clock :clock-value
:quotedMessage :quoted-message :quotedMessage :quoted-message
:outgoingStatus :outgoing-status :outgoingStatus :outgoing-status
:audioDurationMs :audio-duration-ms :audioDurationMs :audio-duration-ms
:deleted :deleted? :deleted :deleted?
:deletedForMe :deleted-for-me? :deletedForMe :deleted-for-me?
:new :new?}) :albumId :album-id
:new :new?})
(update :quoted-message (update :quoted-message
clojure.set/rename-keys set/rename-keys
{:parsedText :parsed-text :communityId :community-id}) {:parsedText :parsed-text :communityId :community-id})
(update :outgoing-status keyword) (update :outgoing-status keyword)
(update :command-parameters (update :command-parameters
clojure.set/rename-keys set/rename-keys
{:transactionHash :transaction-hash {:transactionHash :transaction-hash
:commandState :command-state}) :commandState :command-state})
(assoc :content {:chat-id (:chatId message) (assoc :content {:chat-id (:chatId message)

View File

@ -1,5 +1,5 @@
(ns status-im.data-store.pin-messages (ns status-im.data-store.pin-messages
(:require [clojure.set :as clojure.set] (:require [clojure.set :as set]
[status-im.data-store.messages :as messages] [status-im.data-store.messages :as messages]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
@ -8,8 +8,8 @@
[message] [message]
(-> message (-> message
(merge (messages/<-rpc (message :message))) (merge (messages/<-rpc (message :message)))
(clojure.set/rename-keys {:pinnedAt :pinned-at (set/rename-keys {:pinnedAt :pinned-at
:pinnedBy :pinned-by}) :pinnedBy :pinned-by})
(dissoc :message))) (dissoc :message)))
(defn pinned-message-by-chat-id-rpc (defn pinned-message-by-chat-id-rpc
@ -21,9 +21,9 @@
{:json-rpc/call [{:method "wakuext_chatPinnedMessages" {:json-rpc/call [{:method "wakuext_chatPinnedMessages"
:params [chat-id cursor limit] :params [chat-id cursor limit]
:on-success (fn [result] :on-success (fn [result]
(let [result (clojure.set/rename-keys result (let [result (set/rename-keys result
{:pinnedMessages {:pinnedMessages
:pinned-messages})] :pinned-messages})]
(on-success (update result :pinned-messages #(map <-rpc %))))) (on-success (update result :pinned-messages #(map <-rpc %)))))
:on-error on-error}]}) :on-error on-error}]})

View File

@ -1,24 +1,24 @@
(ns status-im.data-store.reactions (ns status-im.data-store.reactions
(:require [clojure.set :as clojure.set])) (:require [clojure.set :as set]))
(defn ->rpc (defn ->rpc
[message] [message]
(-> message (-> message
(clojure.set/rename-keys {:message-id :messageId (set/rename-keys {:message-id :messageId
:emoji-id :emojiId :emoji-id :emojiId
:chat-id :localChatId :chat-id :localChatId
:message-type :messageType :message-type :messageType
:emoji-reaction-id :id}))) :emoji-reaction-id :id})))
(defn <-rpc (defn <-rpc
[message] [message]
(-> message (-> message
(dissoc :chat_id) (dissoc :chat_id)
(clojure.set/rename-keys {:messageId :message-id (set/rename-keys {:messageId :message-id
:localChatId :chat-id :localChatId :chat-id
:emojiId :emoji-id :emojiId :emoji-id
:messageType :message-type :messageType :message-type
:id :emoji-reaction-id}))) :id :emoji-reaction-id})))
(defn reactions-by-chat-id-rpc (defn reactions-by-chat-id-rpc
[chat-id [chat-id

View File

@ -0,0 +1,41 @@
(ns status-im.data-store.switcher-cards
(:require [clojure.set :as set]
[clojure.walk :as walk]
[utils.re-frame :as rf]
[taoensso.timbre :as log]))
(defn <-rpc
[switcher-cards]
(walk/postwalk-replace
{:cardId :card-id
:screenId :screen-id}
switcher-cards))
(defn rpc->
[switcher-card]
(set/rename-keys switcher-card
{:card-id :cardId
:screen-id :screenId}))
(rf/defn upsert-switcher-card-rpc
[_ switcher-card]
{:json-rpc/call [{:method "wakuext_upsertSwitcherCard"
:params [(rpc-> switcher-card)]
:on-success #()
:on-error #()}]})
(rf/defn delete-switcher-card-rpc
[_ card-id]
{:json-rpc/call [{:method "wakuext_deleteSwitcherCard"
:params [card-id]
:on-success #()
:on-error #()}]})
(rf/defn fetch-switcher-cards-rpc
[_]
{:json-rpc/call [{:method "wakuext_switcherCards"
:params []
:on-success #(rf/dispatch
[:shell/switcher-cards-loaded
(:switcherCards ^js %)])
:on-error #(log/error "Failed to fetch switcher cards" %)}]})

View File

@ -1,18 +1,18 @@
(ns status-im.data-store.visibility-status-updates (ns status-im.data-store.visibility-status-updates
(:require [clojure.set :as clojure.set] (:require [clojure.set :as set]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(defn <-rpc (defn <-rpc
[visibility-status-update] [visibility-status-update]
(clojure.set/rename-keys visibility-status-update (set/rename-keys visibility-status-update
{:publicKey :public-key {:publicKey :public-key
:statusType :status-type})) :statusType :status-type}))
(defn <-rpc-settings (defn <-rpc-settings
[settings] [settings]
(-> settings (-> settings
(clojure.set/rename-keys (set/rename-keys
{:current-user-status :current-user-visibility-status}) {:current-user-status :current-user-visibility-status})
(update :current-user-visibility-status <-rpc))) (update :current-user-visibility-status <-rpc)))

View File

@ -9,6 +9,7 @@
[status-im.data-store.chats :as data-store.chats] [status-im.data-store.chats :as data-store.chats]
[status-im.data-store.invitations :as data-store.invitations] [status-im.data-store.invitations :as data-store.invitations]
[status-im.data-store.settings :as data-store.settings] [status-im.data-store.settings :as data-store.settings]
[status-im.data-store.switcher-cards :as switcher-cards-store]
[status-im.data-store.visibility-status-updates :as visibility-status-updates-store] [status-im.data-store.visibility-status-updates :as visibility-status-updates-store]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.eip55 :as eip55] [status-im.ethereum.eip55 :as eip55]
@ -473,7 +474,8 @@
(multiaccounts/get-profile-picture) (multiaccounts/get-profile-picture)
(multiaccounts/switch-preview-privacy-mode-flag) (multiaccounts/switch-preview-privacy-mode-flag)
(link-preview/request-link-preview-whitelist) (link-preview/request-link-preview-whitelist)
(visibility-status-updates-store/fetch-visibility-status-updates-rpc)))) (visibility-status-updates-store/fetch-visibility-status-updates-rpc)
(switcher-cards-store/fetch-switcher-cards-rpc))))
(defn get-new-auth-method (defn get-new-auth-method
[auth-method save-password?] [auth-method save-password?]

View File

@ -5,7 +5,8 @@
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.react-native :as react-native-utils] [status-im.utils.react-native :as react-native-utils]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[status-im.constants :as constants]))
(defn status (defn status
[] []
@ -278,6 +279,55 @@
:connection-string connection-string}) :connection-string connection-string})
(.inputConnectionStringForBootstrapping ^js (status) connection-string config-json callback)) (.inputConnectionStringForBootstrapping ^js (status) connection-string config-json callback))
(defn deserialize-and-compress-key
"Provides a community id (public key) to status-go which is first deserialized
and then compressed. Example input/output :
input key = zQ3shTAten2v9CwyQD1Kc7VXAqNPDcHZAMsfbLHCZEx6nFqk9 and
output key = 0x025596a7ff87da36860a84b0908191ce60a504afc94aac93c1abd774f182967ce6"
[key callback]
(log/info "[native-module] Deserializing and then compressing public key"
{:fn :deserialize-and-compress-key
:key key})
(.deserializeAndCompressKey ^js (status) key callback))
(defn public-key->compressed-key
"Provides public key to status-go and gets back a compressed key via serialization"
[public-key callback]
(let [serialization-key constants/serialization-key
multi-code-prefix constants/multi-code-prefix
multi-code-key (str multi-code-prefix (subs public-key 2))]
(log/info "[native-module] Serializing public key"
{:fn :public-key->compressed-key
:public-key public-key
:multi-code-key multi-code-key})
(.multiformatSerializePublicKey ^js (status) multi-code-key serialization-key callback)))
(defn compressed-key->public-key
"Provides compressed key to status-go and gets back the uncompressed public key via deserialization"
[public-key callback]
(let [deserialization-key constants/deserialization-key]
(log/info "[native-module] Deserializing compressed key"
{:fn :compressed-key->public-key
:public-key public-key})
(.multiformatDeserializePublicKey ^js (status) public-key deserialization-key callback)))
(defn decompress-public-key
"Provides compressed key to status-go and gets back the uncompressed public key"
[public-key callback]
(log/info "[native-module] Decompressing public key"
{:fn :decompress-public-key
:public-key public-key})
(.decompressPublicKey ^js (status) public-key callback))
(defn compress-public-key
"Provides a public key to status-go and gets back a 33bit compressed key back"
[public-key callback]
(log/info "[native-module] Compressing public key"
{:fn :compress-public-key
:public-key public-key})
(.compressPublicKey ^js (status) public-key callback))
(defn hash-typed-data (defn hash-typed-data
"used for keycard" "used for keycard"
[data callback] [data callback]

View File

@ -2,7 +2,7 @@
(:require ["react-native" :as rn] (:require ["react-native" :as rn]
["react-native-gesture-handler" :refer (gestureHandlerRootHOC)] ["react-native-gesture-handler" :refer (gestureHandlerRootHOC)]
["react-native-navigation" :refer (Navigation)] ["react-native-navigation" :refer (Navigation)]
[clojure.set :as clojure.set] [clojure.set :as set]
[quo.components.text-input :as quo.text-input] [quo.components.text-input :as quo.text-input]
[quo.design-system.colors :as quo.colors] [quo.design-system.colors :as quo.colors]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
@ -299,7 +299,7 @@
(fn [^js evn] (fn [^js evn]
(let [selected-tab-index (.-selectedTabIndex evn) (let [selected-tab-index (.-selectedTabIndex evn)
comp (get tab-root-ids selected-tab-index) comp (get tab-root-ids selected-tab-index)
tab-key (get (clojure.set/map-invert tab-key-idx) selected-tab-index)] tab-key (get (set/map-invert tab-key-idx) selected-tab-index)]
(re-frame/dispatch [:set :current-tab tab-key]) (re-frame/dispatch [:set :current-tab tab-key])
(when (= @state/root-comp-id comp) (when (= @state/root-comp-id comp)
(when (= :chat tab-key) (when (= :chat tab-key)

View File

@ -63,7 +63,8 @@
:sticker (js/require "../resources/images/mock/sticker.png") :sticker (js/require "../resources/images/mock/sticker.png")
:user-picture-female2 (js/require "../resources/images/mock/user_picture_female2.png") :user-picture-female2 (js/require "../resources/images/mock/user_picture_female2.png")
:user-picture-male4 (js/require "../resources/images/mock/user_picture_male4.png") :user-picture-male4 (js/require "../resources/images/mock/user_picture_male4.png")
:user-picture-male5 (js/require "../resources/images/mock/user_picture_male5.png")}) :user-picture-male5 (js/require "../resources/images/mock/user_picture_male5.png")
:coinbase (js/require "../resources/images/mock/coinbase.png")})
(defn get-theme-image (defn get-theme-image
[k] [k]

View File

@ -199,6 +199,12 @@
{:type :wallet-account {:type :wallet-account
:account (when account (string/lower-case account))}) :account (when account (string/lower-case account))})
(defn community-route-type
[route-params]
(if (string/starts-with? (:community-id route-params) "z")
:desktop-community
:community))
(defn handle-uri (defn handle-uri
[chain chats uri cb] [chain chats uri cb]
(let [{:keys [handler route-params query-params]} (match-uri uri)] (let [{:keys [handler route-params query-params]} (match-uri uri)]
@ -229,7 +235,8 @@
(cb {:type handler :community-id (:community-id route-params)}) (cb {:type handler :community-id (:community-id route-params)})
(= handler :community) (= handler :community)
(cb {:type handler :community-id (:community-id route-params)}) (cb {:type (community-route-type route-params)
:community-id (:community-id route-params)})
(= handler :community-chat) (= handler :community-chat)
(cb {:type handler :chat-id (:chat-id route-params)}) (cb {:type handler :chat-id (:chat-id route-params)})

View File

@ -1,5 +1,5 @@
(ns status-im.signing.core (ns status-im.signing.core
(:require [clojure.set :as clojure.set] (:require [clojure.set :as set]
[clojure.string :as string] [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.constants :as constants] [status-im.constants :as constants]
@ -596,4 +596,4 @@
(sign cofx (sign cofx
{:tx-obj (-> tx {:tx-obj (-> tx
(select-keys [:from :to :value :input :gas :nonce :hash]) (select-keys [:from :to :value :input :gas :nonce :hash])
(clojure.set/rename-keys {:input :data}))}))) (set/rename-keys {:input :data}))})))

View File

@ -5,9 +5,7 @@
[shadow.test :as st] [shadow.test :as st]
[shadow.test.env :as env] [shadow.test.env :as env]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[i18n.i18n :as i18n])) status-im2.setup.i18n-resources))
(i18n/init)
(defonce repl? (atom false)) (defonce repl? (atom false))

View File

@ -5,6 +5,7 @@
(defn build-message (defn build-message
[{:keys [chat-id [{:keys [chat-id
album-id
text text
response-to response-to
ens-name ens-name
@ -15,6 +16,7 @@
sticker sticker
content-type]}] content-type]}]
{:chatId chat-id {:chatId chat-id
:albumId album-id
:text text :text text
:responseTo response-to :responseTo response-to
:ensName ens-name :ensName ens-name

View File

@ -4,10 +4,11 @@
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.add-new.db :as db] [status-im.add-new.db :as db]
[status-im.chat.models :as chat.models] [status-im.chat.models :as chat.models]
[i18n.i18n :as i18n]
[status-im.react-native.resources :as resources] [status-im.react-native.resources :as resources]
[status-im.ui.components.icons.icons :as icons] [status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react]) [status-im.ui.components.react :as react]
[status-im2.setup.i18n-resources :as i18n-resources]
[i18n.i18n :as i18n])
(:require-macros [status-im.utils.views :as views])) (:require-macros [status-im.utils.views :as views]))
(defn- start-chat (defn- start-chat
@ -73,8 +74,8 @@
(defn get-language-topic (defn get-language-topic
[] []
(let [lang (subs (name i18n/default-device-language) 0 2) (let [lang (subs (name i18n-resources/default-device-language) 0 2)
lang3 (subs (name i18n/default-device-language) 0 3) lang3 (subs (name i18n-resources/default-device-language) 0 3)
lang-name (or (get lang-names lang3) (get lang-names lang))] lang-name (or (get lang-names lang3) (get lang-names lang))]
(when-not (= lang "en") (when-not (= lang "en")
(or lang-name (str "status-" lang))))) (or lang-name (str "status-" lang)))))

View File

@ -1,5 +1,5 @@
(ns status-im.ui.screens.communities.reorder-categories (ns status-im.ui.screens.communities.reorder-categories
(:require [clojure.set :as clojure.set] (:require [clojure.set :as set]
[clojure.string :as string] [clojure.string :as string]
[clojure.walk :as walk] [clojure.walk :as walk]
[quo.core :as quo] [quo.core :as quo]
@ -48,7 +48,7 @@
[{:keys [id community-id] :as home-item} is-active? drag] [{:keys [id community-id] :as home-item} is-active? drag]
(let [chat-id (string/replace id community-id "") (let [chat-id (string/replace id community-id "")
background-color (if is-active? colors/gray-lighter colors/white) background-color (if is-active? colors/gray-lighter colors/white)
home-item (clojure.set/rename-keys home-item {:id :chat-id})] home-item (set/rename-keys home-item {:id :chat-id})]
[rn/view [rn/view
{:accessibility-label :chat-item {:accessibility-label :chat-item
:style (merge styles/category-item :style (merge styles/category-item

View File

@ -108,7 +108,6 @@
[status-im.ui.screens.wallet.swap.views :as wallet.swap] [status-im.ui.screens.wallet.swap.views :as wallet.swap]
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions] [status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
[status-im2.contexts.chat.group-details.view :as group-details] [status-im2.contexts.chat.group-details.view :as group-details]
[status-im.ui2.screens.chat.photo-selector.view :as photo-selector]
[status-im.ui2.screens.chat.components.new-chat.view :as new-chat-aio])) [status-im.ui2.screens.chat.components.new-chat.view :as new-chat-aio]))
(defn right-button-options (defn right-button-options
@ -207,10 +206,6 @@
:options {:topBar {:visible false}} :options {:topBar {:visible false}}
:component pin-messages/pinned-messages} :component pin-messages/pinned-messages}
{:name :photo-selector
:options {:topBar {:visible false}}
:component photo-selector/photo-selector}
{:name :group-chat-profile {:name :group-chat-profile
;;TODO animated-header ;;TODO animated-header
:options {:topBar {:visible false}} :options {:topBar {:visible false}}

View File

@ -64,14 +64,9 @@
(i18n/label :t/message-deleted)]]) (i18n/label :t/message-deleted)]])
(defn reply-message (defn reply-message
[{:keys [chat-id id]} [{:keys [from identicon content-type contentType parsed-text content deleted? deleted-for-me?]}
in-chat-input? pin?] in-chat-input? pin?]
(let [reply-content-sub (-> [:chats/chat-messages chat-id] (let [contact-name (rf/sub [:contacts/contact-name-by-identity from])
rf/sub
(get id))
{:keys [from identicon content-type contentType parsed-text content deleted? deleted-for-me?]}
reply-content-sub
contact-name (rf/sub [:contacts/contact-name-by-identity from])
current-public-key (rf/sub [:multiaccount/public-key]) current-public-key (rf/sub [:multiaccount/public-key])
content-type (or content-type contentType)] content-type (or content-type contentType)]
[rn/view [rn/view

View File

@ -15,7 +15,7 @@
[status-im.ui2.screens.chat.composer.mentions :as mentions] [status-im.ui2.screens.chat.composer.mentions :as mentions]
[status-im.ui2.screens.chat.composer.reply :as reply] [status-im.ui2.screens.chat.composer.reply :as reply]
[status-im.ui2.screens.chat.composer.style :as style] [status-im.ui2.screens.chat.composer.style :as style]
[status-im.ui2.screens.chat.photo-selector.view :as photo-selector] [status-im2.contexts.chat.photo-selector.view :as photo-selector]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im2.contexts.chat.messages.list.view :refer [scroll-to-bottom]] [status-im2.contexts.chat.messages.list.view :refer [scroll-to-bottom]]
@ -164,7 +164,7 @@
:dy 0 ;used for gesture :dy 0 ;used for gesture
:pdy 0 ;used for gesture :pdy 0 ;used for gesture
:state :min ;:min, :custom-chat-available, :state :min ;:min, :custom-chat-available,
;:custom-chat-unavailable, :max ;:custom-chat-unavailable, :max
:clear false :clear false
:minimized-from-handlebar? false}) :minimized-from-handlebar? false})
keyboard-was-shown? (atom false) keyboard-was-shown? (atom false)
@ -188,19 +188,19 @@
360) 360)
(:top insets) (:top insets)
(:status-bar-height @navigation-const)) ; 360 (:status-bar-height @navigation-const)) ; 360
; - ; -
; default ; default
; height ; height
max-height (Math/abs (- max-y 56 (:bottom insets))) ; 56 max-height (Math/abs (- max-y 56 (:bottom insets))) ; 56
; - ; -
; top-bar ; top-bar
; height ; height
added-value (if (and (not (seq suggestions)) added-value (if (and (not (seq suggestions))
(or edit reply)) (or edit reply))
38 38
0) ; increased height 0) ; increased height
; of input box ; of input box
; needed when reply ; needed when reply
min-y (+ min-y (when (or edit reply) 38)) min-y (+ min-y (when (or edit reply) 38))
bg-opacity (reanimated/use-shared-value 0) bg-opacity (reanimated/use-shared-value 0)
bg-bottom (reanimated/use-shared-value (- bg-bottom (reanimated/use-shared-value (-

View File

@ -5,7 +5,7 @@
[quo2.foundations.colors :as colors] [quo2.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.reanimated :as reanimated] [react-native.reanimated :as reanimated]
[status-im.ui2.screens.chat.pin-limit-popover.style :as style] ;; TODO move to status-im2 [status-im.ui2.screens.chat.pin-limit-popover.style :as style]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
;; TODO (flexsurfer) this should be an in-app notification component in quo2 ;; TODO (flexsurfer) this should be an in-app notification component in quo2
@ -56,6 +56,6 @@
:right 16}} :right 16}}
[quo/icon :i/close [quo/icon :i/close
{:color (colors/theme-colors colors/white colors/neutral-100) {:color (colors/theme-colors colors/white colors/neutral-100)
:size 8}]]]))]) :size 12}]]]))])

View File

@ -14,7 +14,8 @@
[utils.re-frame :as rf] [utils.re-frame :as rf]
[status-im.wallet.choose-recipient.core :as choose-recipient] [status-im.wallet.choose-recipient.core :as choose-recipient]
[status-im2.navigation.events :as navigation] [status-im2.navigation.events :as navigation]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[status-im.native-module.core :as status]))
;; TODO(yenda) investigate why `handle-universal-link` event is ;; TODO(yenda) investigate why `handle-universal-link` event is
;; dispatched 7 times for the same link ;; dispatched 7 times for the same link
@ -76,7 +77,21 @@
(rf/defn handle-community (rf/defn handle-community
[cofx {:keys [community-id]}] [cofx {:keys [community-id]}]
(log/info "universal-links: handling community" community-id) (log/info "universal-links: handling community" community-id)
(navigation/navigate-to-cofx cofx :community {:community-id community-id})) (navigation/navigate-to-cofx cofx :community {:community-id community-id})
)
(rf/defn handle-navigation-to-desktop-community-from-mobile
{:events [:handle-navigation-to-desktop-community-from-mobile]}
[{:keys [db]} cofx deserialized-key]
(navigation/navigate-to-cofx cofx :community {:community-id deserialized-key})
)
(rf/defn handle-desktop-community
[cofx {:keys [community-id]}]
(status/deserialize-and-compress-key
community-id
(fn [deserialized-key]
(rf/dispatch [:handle-navigation-to-desktop-community-from-mobile cofx (str deserialized-key)]))))
(rf/defn handle-community-chat (rf/defn handle-community-chat
[cofx {:keys [chat-id]}] [cofx {:keys [chat-id]}]
@ -145,6 +160,7 @@
:private-chat (handle-private-chat cofx data) :private-chat (handle-private-chat cofx data)
:community-requests (handle-community-requests cofx data) :community-requests (handle-community-requests cofx data)
:community (handle-community cofx data) :community (handle-community cofx data)
:desktop-community (handle-desktop-community cofx data)
:community-chat (handle-community-chat cofx data) :community-chat (handle-community-chat cofx data)
:contact (handle-view-profile cofx data) :contact (handle-view-profile cofx data)
:browser (handle-browse cofx data) :browser (handle-browse cofx data)

View File

@ -1,5 +1,5 @@
(ns status-im.utils.views (ns status-im.utils.views
(:require [clojure.walk :as w])) (:require [clojure.walk :as walk]))
(defn atom? (defn atom?
[sub] [sub]
@ -9,9 +9,9 @@
(defn walk-sub (defn walk-sub
[sub form->sym] [sub form->sym]
(if (coll? sub) (if (coll? sub)
(w/postwalk (fn [f] (walk/postwalk (fn [f]
(or (form->sym f) f)) (or (form->sym f) f))
sub) sub)
(or (form->sym sub) sub))) (or (form->sym sub) sub)))
(defn prepare-subs (defn prepare-subs

View File

@ -1,6 +1,6 @@
(ns status-im.wallet.core (ns status-im.wallet.core
(:require (:require
[clojure.set :as clojure.set] [clojure.set :as set]
[clojure.string :as string] [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.async-storage.core :as async-storage] [status-im.async-storage.core :as async-storage]
@ -360,7 +360,7 @@
(rf/merge cofx (rf/merge cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:wallet/visible-tokens :wallet/visible-tokens
(update visible-tokens chain clojure.set/union chain-visible-tokens) (update visible-tokens chain set/union chain-visible-tokens)
{}) {})
(update-tokens-balances balances) (update-tokens-balances balances)
(prices/update-prices)))) (prices/update-prices))))

View File

@ -26,7 +26,7 @@
[quo/text {:style {:margin-left 10}} extra-text]])) [quo/text {:style {:margin-left 10}} extra-text]]))
(defn confirmation-drawer (defn confirmation-drawer
[{:keys [title description context button-text on-press extra-action extra-text]}] [{:keys [title description context button-text on-press extra-action extra-text accessibility-label]}]
(let [extra-action-selected? (reagent/atom false)] (let [extra-action-selected? (reagent/atom false)]
(fn [] (fn []
(let [{:keys [group-chat chat-id public-key color name]} context (let [{:keys [group-chat chat-id public-key color name]} context
@ -38,7 +38,9 @@
id])) id]))
photo-path (when-not (empty? (:images contact)) photo-path (when-not (empty? (:images contact))
(rf/sub [:chats/photo-path id]))] (rf/sub [:chats/photo-path id]))]
[rn/view {:style {:margin-horizontal 20}} [rn/view
{:style {:margin-horizontal 20}
:accessibility-label accessibility-label}
[quo/text [quo/text
{:weight :semi-bold {:weight :semi-bold
:size :heading-1} title] :size :heading-1} title]

View File

@ -13,6 +13,9 @@
(def ^:const content-type-community 9) (def ^:const content-type-community 9)
(def ^:const content-type-gap 10) (def ^:const content-type-gap 10)
(def ^:const content-type-contact-request 11) ;; TODO: temp, will be removed (def ^:const content-type-contact-request 11) ;; TODO: temp, will be removed
(def ^:const content-type-gif 12)
(def ^:const content-type-link 13)
(def ^:const content-type-album 14)
(def ^:const contact-request-state-none 0) (def ^:const contact-request-state-none 0)
(def ^:const contact-request-state-mutual 1) (def ^:const contact-request-state-mutual 1)
@ -198,3 +201,20 @@
(def ^:const delete-message-undo-time-limit-ms 4000) (def ^:const delete-message-undo-time-limit-ms 4000)
(def ^:const delete-message-for-me-undo-time-limit-ms 4000) (def ^:const delete-message-for-me-undo-time-limit-ms 4000)
(def ^:const album-image-sizes
{4 {0 146
1 146
2 146
3 146}
5 {0 146
1 146
2 97
3 97
4 97}
:default {0 146
1 146
2 72.5
3 72.5
4 72.5
5 72.5}})

View File

@ -8,19 +8,20 @@
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- entry (defn- entry
[{:keys [icon label on-press danger? sub-label chevron? add-divider?]}] [{:keys [icon label on-press danger? sub-label chevron? add-divider? accessibility-label]}]
{:pre [(keyword? icon) {:pre [(keyword? icon)
(string? label) (string? label)
(fn? on-press) (fn? on-press)
(boolean? danger?) (boolean? danger?)
(boolean? chevron?)]} (boolean? chevron?)]}
{:icon icon {:icon icon
:label label :label label
:on-press on-press :on-press on-press
:danger? danger? :danger? danger?
:sub-label sub-label :sub-label sub-label
:right-icon (when chevron? :i/chevron-right) :right-icon (when chevron? :i/chevron-right)
:add-divider? add-divider?}) :add-divider? add-divider?
:accessibility-label accessibility-label})
(defn hide-sheet-and-dispatch (defn hide-sheet-and-dispatch
[event] [event]
@ -54,11 +55,12 @@
[:bottom-sheet/show-sheet [:bottom-sheet/show-sheet
{:content (fn [] {:content (fn []
(confirmation-drawer/confirmation-drawer (confirmation-drawer/confirmation-drawer
{:title (i18n/label :t/clear-history?) {:title (i18n/label :t/clear-history?)
:description (i18n/label :t/clear-history-confirmation-content) :description (i18n/label :t/clear-history-confirmation-content)
:context item :context item
:button-text (i18n/label :t/clear-history) :accessibility-label :clear-history-confirm
:on-press #(hide-sheet-and-dispatch [:chat.ui/clear-history chat-id])}))}])) :button-text (i18n/label :t/clear-history)
:on-press #(hide-sheet-and-dispatch [:chat.ui/clear-history chat-id])}))}]))
(defn delete-chat-action (defn delete-chat-action
[{:keys [chat-id] :as item}] [{:keys [chat-id] :as item}]
@ -66,11 +68,12 @@
[:bottom-sheet/show-sheet [:bottom-sheet/show-sheet
{:content (fn [] {:content (fn []
(confirmation-drawer/confirmation-drawer (confirmation-drawer/confirmation-drawer
{:title (i18n/label :t/delete-chat?) {:title (i18n/label :t/delete-chat?)
:description (i18n/label :t/delete-chat-confirmation) :description (i18n/label :t/delete-chat-confirmation)
:context item :context item
:button-text (i18n/label :t/delete-chat) :accessibility-label :delete-chat-confirm
:on-press #(hide-sheet-and-dispatch [:chat.ui/remove-chat chat-id])}))}])) :button-text (i18n/label :t/delete-chat)
:on-press #(hide-sheet-and-dispatch [:chat.ui/remove-chat chat-id])}))}]))
(defn leave-group-action (defn leave-group-action
[item chat-id] [item chat-id]
@ -78,14 +81,15 @@
[:bottom-sheet/show-sheet [:bottom-sheet/show-sheet
{:content (fn [] {:content (fn []
(confirmation-drawer/confirmation-drawer (confirmation-drawer/confirmation-drawer
{:title (i18n/label :t/leave-group?) {:title (i18n/label :t/leave-group?)
:description (i18n/label :t/leave-chat-confirmation) :description (i18n/label :t/leave-chat-confirmation)
:context item :context item
:button-text (i18n/label :t/leave-group) :accessibility-label :leave-group
:on-press #(do :button-text (i18n/label :t/leave-group)
(rf/dispatch [:navigate-back]) :on-press #(do
(hide-sheet-and-dispatch [:group-chats.ui/leave-chat-confirmed (rf/dispatch [:navigate-back])
chat-id]))}))}])) (hide-sheet-and-dispatch [:group-chats.ui/leave-chat-confirmed
chat-id]))}))}]))
(defn block-user-action (defn block-user-action
[{:keys [public-key] :as item}] [{:keys [public-key] :as item}]
@ -93,255 +97,281 @@
[:bottom-sheet/show-sheet [:bottom-sheet/show-sheet
{:content (fn [] {:content (fn []
(confirmation-drawer/confirmation-drawer (confirmation-drawer/confirmation-drawer
{:title (i18n/label :t/block-user?) {:title (i18n/label :t/block-user?)
:description (i18n/label :t/block-contact-details) :description (i18n/label :t/block-contact-details)
:context item :context item
:button-text (i18n/label :t/block-user) :accessibility-label :block-user
:on-press #(hide-sheet-and-dispatch [:contact.ui/block-contact-confirmed :button-text (i18n/label :t/block-user)
public-key])}))}])) :on-press #(hide-sheet-and-dispatch [:contact.ui/block-contact-confirmed
public-key])}))}]))
(defn mute-chat-entry (defn mute-chat-entry
[chat-id] [chat-id]
(let [muted? (rf/sub [:chats/muted chat-id])] (let [muted? (rf/sub [:chats/muted chat-id])]
(entry {:icon (if muted? :i/muted :i/activity-center) (entry {:icon (if muted? :i/muted :i/activity-center)
:label (i18n/label :label (i18n/label
(if muted? (if muted?
:unmute-chat :unmute-chat
:mute-chat)) :mute-chat))
:on-press (if muted? :on-press (if muted?
#(unmute-chat-action chat-id) #(unmute-chat-action chat-id)
#(mute-chat-action chat-id)) #(mute-chat-action chat-id))
:danger? false :danger? false
:sub-label nil :accessibility-label :mute-chat
:chevron? true}))) :sub-label nil
:chevron? true})))
(defn mark-as-read-entry (defn mark-as-read-entry
[chat-id] [chat-id]
(entry {:icon :i/correct (entry {:icon :i/correct
:label (i18n/label :t/mark-as-read) :label (i18n/label :t/mark-as-read)
:on-press #(mark-all-read-action chat-id) :on-press #(mark-all-read-action chat-id)
:danger? false :danger? false
:sub-label nil :accessibility-label :mark-as-read
:chevron? false :sub-label nil
:add-divider? true})) :chevron? false
:add-divider? true}))
(defn clear-history-entry (defn clear-history-entry
[chat-id] [chat-id]
(entry {:icon :i/delete (entry {:icon :i/delete
:label (i18n/label :t/clear-history) :label (i18n/label :t/clear-history)
:on-press #(clear-history-action chat-id) :on-press #(clear-history-action chat-id)
:danger? true :danger? true
:sub-label nil :sub-label nil
:chevron? false :accessibility-label :clear-history
:add-divider? true})) :chevron? false
:add-divider? true}))
(defn delete-chat-entry (defn delete-chat-entry
[item] [item]
(entry {:icon :i/delete (entry {:icon :i/delete
:label (i18n/label :t/delete-chat) :label (i18n/label :t/delete-chat)
:on-press #(delete-chat-action item) :on-press #(delete-chat-action item)
:danger? true :danger? true
:sub-label nil :accessibility-label :delete-chat
:chevron? false})) :sub-label nil
:chevron? false}))
(defn leave-group-entry (defn leave-group-entry
[item extra-data] [item extra-data]
(entry {:icon :i/log-out (entry
:label (i18n/label :t/leave-group) {:icon :i/log-out
:on-press #(leave-group-action item (if extra-data (:chat-id extra-data) (:chat-id item))) :label (i18n/label :t/leave-group)
:danger? true :on-press #(leave-group-action item (if extra-data (:chat-id extra-data) (:chat-id item)))
:sub-label nil :danger? true
:chevron? false :accessibility-label :leave-group
:add-divider? extra-data})) :sub-label nil
:chevron? false
:add-divider? extra-data}))
(defn view-profile-entry (defn view-profile-entry
[chat-id] [chat-id]
(entry {:icon :i/friend (entry {:icon :i/friend
:label (i18n/label :t/view-profile) :label (i18n/label :t/view-profile)
:on-press #(show-profile-action chat-id) :on-press #(show-profile-action chat-id)
:danger? false :danger? false
:sub-label nil :accessibility-label :view-profile
:chevron? false})) :sub-label nil
:chevron? false}))
(defn edit-nickname-entry (defn edit-nickname-entry
[chat-id] [chat-id]
(entry {:icon :i/edit (entry {:icon :i/edit
:label (i18n/label :t/edit-nickname) :label (i18n/label :t/edit-nickname)
:on-press #(edit-nickname-action chat-id) :on-press #(edit-nickname-action chat-id)
:danger? false :danger? false
:sub-label nil :accessibility-label :edit-nickname
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): Requires design input. ;; TODO(OmarBasem): Requires design input.
(defn edit-name-image-entry (defn edit-name-image-entry
[] []
(entry {:icon :i/edit (entry {:icon :i/edit
:label (i18n/label :t/edit-name-and-image) :label (i18n/label :t/edit-name-and-image)
:on-press #(js/alert "TODO: to be implemented, requires design input") :on-press #(js/alert "TODO: to be implemented, requires design input")
:danger? false :danger? false
:sub-label nil :accessibility-label :edit-name-and-image
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): Requires design input. ;; TODO(OmarBasem): Requires design input.
(defn notifications-entry (defn notifications-entry
[add-divider?] [add-divider?]
(entry {:icon :i/notifications (entry {:icon :i/notifications
:label (i18n/label :t/notifications) :label (i18n/label :t/notifications)
:on-press #(js/alert "TODO: to be implemented, requires design input") :on-press #(js/alert "TODO: to be implemented, requires design input")
:danger? false :danger? false
:sub-label "All messages" ; TODO: placeholder :sub-label "All messages" ; TODO: placeholder
:chevron? true :accessibility-label :manage-notifications
:add-divider? add-divider?})) :chevron? true
:add-divider? add-divider?}))
;; TODO(OmarBasem): Requires design input. ;; TODO(OmarBasem): Requires design input.
(defn fetch-messages-entry (defn fetch-messages-entry
[] []
(entry {:icon :i/save (entry {:icon :i/save
:label (i18n/label :t/fetch-messages) :label (i18n/label :t/fetch-messages)
:on-press #(js/alert "TODO: to be implemented, requires design input") :on-press #(js/alert "TODO: to be implemented, requires design input")
:danger? false :danger? false
:sub-label nil :accessibility-label :fetch-messages
:chevron? true})) :sub-label nil
:chevron? true}))
;; TODO(OmarBasem): Requires design input. ;; TODO(OmarBasem): Requires design input.
(defn pinned-messages-entry (defn pinned-messages-entry
[] []
(entry {:icon :i/pin (entry {:icon :i/pin
:label (i18n/label :t/pinned-messages) :label (i18n/label :t/pinned-messages)
:on-press #(js/alert "TODO: to be implemented, requires design input") :on-press #(js/alert "TODO: to be implemented, requires design input")
:danger? false :danger? false
:sub-label nil :accessibility-label :pinned-messages
:chevron? true})) :sub-label nil
:chevron? true}))
(defn remove-from-contacts-entry (defn remove-from-contacts-entry
[contact] [contact]
(entry {:icon :i/remove-user (entry {:icon :i/remove-user
:label (i18n/label :t/remove-from-contacts) :label (i18n/label :t/remove-from-contacts)
:on-press #(hide-sheet-and-dispatch [:contact.ui/remove-contact-pressed contact]) :on-press #(hide-sheet-and-dispatch [:contact.ui/remove-contact-pressed contact])
:danger? false :danger? false
:sub-label nil :accessibility-label :remove-from-contacts
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): Requires design input. ;; TODO(OmarBasem): Requires design input.
(defn rename-entry (defn rename-entry
[] []
(entry {:icon :i/edit (entry {:icon :i/edit
:label (i18n/label :t/rename) :label (i18n/label :t/rename)
:on-press #(js/alert "TODO: to be implemented, requires design input") :on-press #(js/alert "TODO: to be implemented, requires design input")
:danger? false :danger? false
:sub-label nil :accessibility-label :rename-contact
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): Requires design input. ;; TODO(OmarBasem): Requires design input.
(defn show-qr-entry (defn show-qr-entry
[] []
(entry {:icon :i/qr-code (entry {:icon :i/qr-code
:label (i18n/label :t/show-qr) :label (i18n/label :t/show-qr)
:on-press #(js/alert "TODO: to be implemented, requires design input") :on-press #(js/alert "TODO: to be implemented, requires design input")
:danger? false :danger? false
:sub-label nil :accessibility-label :show-qr-code
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): to be implemented. ;; TODO(OmarBasem): to be implemented.
(defn share-profile-entry (defn share-profile-entry
[] []
(entry {:icon :i/share (entry {:icon :i/share
:label (i18n/label :t/share-profile) :label (i18n/label :t/share-profile)
:on-press #(js/alert "TODO: to be implemented") :on-press #(js/alert "TODO: to be implemented")
:danger? false :danger? false
:sub-label nil :accessibility-label :share-profile
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): to be implemented. ;; TODO(OmarBasem): to be implemented.
(defn share-group-entry (defn share-group-entry
[] []
(entry {:icon :i/share (entry {:icon :i/share
:label (i18n/label :t/share) :label (i18n/label :t/share)
:on-press #(js/alert "TODO: to be implemented") :on-press #(js/alert "TODO: to be implemented")
:danger? false :danger? false
:sub-label nil :accessibility-label :share-group
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): Requires status-go impl. ;; TODO(OmarBasem): Requires status-go impl.
(defn mark-untrustworthy-entry (defn mark-untrustworthy-entry
[] []
(entry {:icon :i/alert (entry {:icon :i/alert
:label (i18n/label :t/mark-untrustworthy) :label (i18n/label :t/mark-untrustworthy)
:on-press #(js/alert "TODO: to be implemented, requires status-go impl.") :on-press #(js/alert "TODO: to be implemented, requires status-go impl.")
:danger? true :danger? true
:sub-label nil :accessibility-label :mark-untrustworthy
:chevron? false :sub-label nil
:add-divider? true})) :chevron? false
:add-divider? true}))
(defn block-user-entry (defn block-user-entry
[item] [item]
(entry {:icon :i/block (entry {:icon :i/block
:label (i18n/label :t/block-user) :label (i18n/label :t/block-user)
:on-press #(block-user-action item) :on-press #(block-user-action item)
:danger? true :danger? true
:sub-label nil :accessibility-label :block-user
:chevron? false})) :sub-label nil
:chevron? false}))
(defn remove-from-group-entry (defn remove-from-group-entry
[{:keys [public-key]} chat-id] [{:keys [public-key]} chat-id]
(let [username (first (rf/sub [:contacts/contact-two-names-by-identity public-key]))] (let [username (first (rf/sub [:contacts/contact-two-names-by-identity public-key]))]
(entry {:icon :i/placeholder (entry {:icon :i/placeholder
:label (i18n/label :t/remove-user-from-group {:username username}) :label (i18n/label :t/remove-user-from-group {:username username})
:on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-member-pressed chat-id :on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-member-pressed chat-id
public-key true]) public-key true])
:danger? true :danger? true
:sub-label nil :accessibility-label :remove-from-group
:chevron? false :sub-label nil
:add-divider? true}))) :chevron? false
:add-divider? true})))
(defn group-details-entry (defn group-details-entry
[chat-id] [chat-id]
(entry {:icon :i/members (entry {:icon :i/members
:label (i18n/label :t/group-details) :label (i18n/label :t/group-details)
:on-press #(hide-sheet-and-dispatch [:show-group-chat-profile chat-id]) :on-press #(hide-sheet-and-dispatch [:show-group-chat-profile chat-id])
:danger? false :danger? false
:sub-label nil :accessibility-label :group-details
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): to be implemented. ;; TODO(OmarBasem): to be implemented.
(defn add-members-entry (defn add-members-entry
[] []
(entry {:icon :i/add-user (entry {:icon :i/add-user
:label (i18n/label :t/add-members) :label (i18n/label :t/add-members)
:on-press #(js/alert "TODO: to be implemented") :on-press #(js/alert "TODO: to be implemented")
:danger? false :danger? false
:sub-label nil :accessibility-label :add-members
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): to be implemented. ;; TODO(OmarBasem): to be implemented.
(defn manage-members-entry (defn manage-members-entry
[] []
(entry {:icon :i/add-user (entry {:icon :i/add-user
:label (i18n/label :t/manage-members) :label (i18n/label :t/manage-members)
:on-press #(js/alert "TODO: to be implemented") :on-press #(js/alert "TODO: to be implemented")
:danger? false :danger? false
:sub-label nil :accessibility-label :manage-members
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): to be implemented. ;; TODO(OmarBasem): to be implemented.
(defn edit-group-entry (defn edit-group-entry
[] []
(entry {:icon :i/edit (entry {:icon :i/edit
:label (i18n/label :t/edit-name-and-image) :label (i18n/label :t/edit-name-and-image)
:on-press #(js/alert "TODO: to be implemented") :on-press #(js/alert "TODO: to be implemented")
:danger? false :danger? false
:sub-label nil :accessibility-label :edit-group
:chevron? false})) :sub-label nil
:chevron? false}))
;; TODO(OmarBasem): to be implemented. ;; TODO(OmarBasem): to be implemented.
(defn group-privacy-entry (defn group-privacy-entry
[] []
(entry {:icon :i/privacy (entry {:icon :i/privacy
:label (i18n/label :t/change-group-privacy) :label (i18n/label :t/change-group-privacy)
:on-press #(js/alert "TODO: to be implemented") :on-press #(js/alert "TODO: to be implemented")
:danger? false :danger? false
:sub-label nil :accessibility-label :group-privacy
:chevron? false})) :sub-label nil
:chevron? false}))
(defn destructive-actions (defn destructive-actions
[{:keys [group-chat] :as item}] [{:keys [group-chat] :as item}]

View File

@ -2,7 +2,7 @@
(:require [cljs.test :refer [deftest is testing]] (:require [cljs.test :refer [deftest is testing]]
[status-im.constants :as constants] [status-im.constants :as constants]
status-im.events status-im.events
[status-im.test-helpers :as h] [test-helpers.unit :as h]
[status-im2.contexts.activity-center.events :as activity-center] [status-im2.contexts.activity-center.events :as activity-center]
[status-im2.contexts.activity-center.notification-types :as types] [status-im2.contexts.activity-center.notification-types :as types]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))

View File

@ -17,7 +17,7 @@
;; `:chat.ui/navigate-to-chat`, otherwise the chat screen ;; `:chat.ui/navigate-to-chat`, otherwise the chat screen
;; looks completely broken if it has never been opened ;; looks completely broken if it has never been opened
;; before for the accepted contact. ;; before for the accepted contact.
[rn/touchable-without-feedback [rn/touchable-opacity
{:on-press (fn [] {:on-press (fn []
(rf/dispatch [:hide-popover]) (rf/dispatch [:hide-popover])
(rf/dispatch [:contact.ui/send-message-pressed (rf/dispatch [:contact.ui/send-message-pressed

View File

@ -9,12 +9,19 @@
[status-im2.contexts.activity-center.notification.mentions.style :as style] [status-im2.contexts.activity-center.notification.mentions.style :as style]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(def tag-params
{:size :small
:override-theme :dark
:color colors/primary-50
:style style/tag
:text-style style/tag-text})
(defn message-body (defn message-body
[message] [message]
(let [parsed-text (get-in message [:content :parsed-text]) (let [parsed-text (get-in message [:content :parsed-text])
parsed-text-children (:children (first parsed-text))] parsed-text-children (:children (first parsed-text))]
(into [quo/text (into [quo/text
{:number-of-lines 2 {:number-of-lines 1
:style style/tag-text :style style/tag-text
:accessibility-label :activity-message-body :accessibility-label :activity-message-body
:size :paragraph-1}] :size :paragraph-1}]
@ -29,25 +36,25 @@
parsed-text-children)))) parsed-text-children))))
(defn view (defn view
[{:keys [author chat-name chat-id message] :as notification}] [{:keys [author chat-name chat-id message read timestamp]}]
[rn/touchable-without-feedback (let [chat (rf/sub [:chats/chat chat-id])
{:on-press (fn [] community-id (:community-id chat)
(rf/dispatch [:hide-popover]) is-chat-from-community? (not (nil? community-id))
(rf/dispatch [:chat.ui/navigate-to-chat chat-id]))} community (rf/sub [:communities/community community-id])
[quo/activity-log community-name (:name community)
{:title (i18n/label :t/mention) community-image (get-in community [:images :thumbnail :uri])]
:icon :i/mention [rn/touchable-opacity
:timestamp (datetime/timestamp->relative (:timestamp notification)) {:on-press (fn []
:unread? (not (:read notification)) (rf/dispatch [:hide-popover])
:context [[common/user-avatar-tag author] (rf/dispatch [:chat.ui/navigate-to-chat chat-id]))}
[quo/text {:style style/tag-text} (string/lower-case (i18n/label :t/on))] [quo/activity-log
;; TODO (@smohamedjavid): The `group-avatar-tag` component {:title (i18n/label :t/mention)
;; does NOT support displaying channel name along with community/chat name. :icon :i/mention
;; Need to update the component to support it. :timestamp (datetime/timestamp->relative timestamp)
[quo/group-avatar-tag chat-name :unread? (not read)
{:size :small :context [[common/user-avatar-tag author]
:override-theme :dark [quo/text {:style style/tag-text} (string/lower-case (i18n/label :t/on))]
:color colors/primary-50 (if is-chat-from-community?
:style style/tag [quo/context-tag tag-params {:uri community-image} community-name chat-name]
:text-style style/tag-text}]] [quo/group-avatar-tag chat-name tag-params])]
:message {:body (message-body message)}}]]) :message {:body (message-body message)}}]]))

View File

@ -0,0 +1,12 @@
(ns status-im2.contexts.activity-center.notification.reply.style
(:require [quo2.foundations.colors :as colors]))
(def tag
{:background-color colors/white-opa-10})
(def tag-text
{:color colors/white})
(def lowercase-text
{:color colors/white
:text-transform :lowercase})

View File

@ -0,0 +1,53 @@
(ns status-im2.contexts.activity-center.notification.reply.view
(:require [i18n.i18n :as i18n]
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[status-im.ui2.screens.chat.messages.message :as old-message]
[status-im2.common.constants :as constants]
[status-im2.contexts.activity-center.notification.common.view :as common]
[status-im2.contexts.activity-center.notification.reply.style :as style]
[utils.datetime :as datetime]
[utils.re-frame :as rf]))
(def tag-params
{:size :small
:override-theme :dark
:color colors/primary-50
:style style/tag
:text-style style/tag-text})
;; NOTE: Replies support text, image and stickers only.
(defn get-message-content
[{:keys [content-type] :as message}]
(case content-type
constants/content-type-text (get-in message [:content :text])
constants/content-type-image [old-message/message-content-image message]
constants/content-type-sticker [old-message/sticker message]))
(defn view
[{:keys [author chat-name chat-id message read timestamp]}]
(let [chat (rf/sub [:chats/chat chat-id])
community-id (:community-id chat)
is-chat-from-community? (not (nil? community-id))
community (rf/sub [:communities/community community-id])
community-name (:name community)
community-image (get-in community [:images :thumbnail :uri])]
[rn/touchable-opacity
{:on-press (fn []
(rf/dispatch [:hide-popover])
(rf/dispatch [:chat.ui/navigate-to-chat chat-id]))}
[quo/activity-log
{:title (i18n/label :t/message-reply)
:icon :i/reply
:timestamp (datetime/timestamp->relative timestamp)
:unread? (not read)
:context [[common/user-avatar-tag author]
[quo/text {:style style/lowercase-text} (i18n/label :t/on)]
(if is-chat-from-community?
[quo/context-tag tag-params {:uri community-image} community-name chat-name]
[quo/group-avatar-tag chat-name tag-params])]
:message {:body-number-of-lines 1
:body (get-message-content message)}}]]))

View File

@ -9,6 +9,7 @@
[status-im2.contexts.activity-center.notification.contact-verification.view :as [status-im2.contexts.activity-center.notification.contact-verification.view :as
contact-verification] contact-verification]
[status-im2.contexts.activity-center.notification.mentions.view :as mentions] [status-im2.contexts.activity-center.notification.mentions.view :as mentions]
[status-im2.contexts.activity-center.notification.reply.view :as reply]
[status-im2.contexts.activity-center.style :as style] [status-im2.contexts.activity-center.style :as style]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -107,6 +108,9 @@
types/mention types/mention
[mentions/view notification] [mentions/view notification]
types/reply
[reply/view notification]
nil)]) nil)])
(defn view (defn view

View File

@ -0,0 +1,22 @@
(ns status-im2.contexts.chat.messages.content.album.style
(:require [quo2.foundations.colors :as colors]))
(def album-container
{:flex-direction :row
:flex-wrap :wrap
:overflow :hidden})
(defn image
[size index]
{:width size
:height size
:margin-left (when (and (not= index 0) (not= index 2)) 1)
:margin-bottom (when (< index 2) 1)})
(def overlay
{:position :absolute
:width 73
:height 73
:background-color colors/neutral-80-opa-60
:justify-content :center
:align-items :center})

View File

@ -0,0 +1,51 @@
(ns status-im2.contexts.chat.messages.content.album.view
(:require [quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]
[status-im2.contexts.chat.messages.content.album.style :as style]
[status-im2.common.constants :as constants]))
(def max-display-count 6)
(defn border-tlr
[index]
(when (= index 0) 12))
(defn border-trr
[index]
(when (= index 1) 12))
(defn border-blr
[index count]
(when (and (= index 2) (> count 2)) 12))
(defn border-brr
[index count]
(when (and (= index (- (min count max-display-count) 1)) (> count 2)) 12))
(defn album-message
[message]
[rn/view
{:style style/album-container}
(map-indexed
(fn [index item]
(let [images-count (count (:album message))
images-size-key (if (< images-count 6) images-count :default)
size (get-in constants/album-image-sizes [images-size-key index])]
[rn/view {:key (:message-id item)}
[fast-image/fast-image
{:style (merge (style/image size index)
{:border-top-left-radius (border-tlr index)
:border-top-right-radius (border-trr index)
:border-bottom-left-radius (border-blr index images-count)
:border-bottom-right-radius (border-brr index images-count)})
:source {:uri (:image (:content item))}}]
(when (and (> images-count max-display-count) (= index (- max-display-count 1)))
[rn/view
{:style (merge style/overlay {:border-bottom-right-radius (border-brr index images-count)})}
[quo/text
{:weight :bold
:size :heading-2
:style {:color colors/white}} (str "+" (- images-count 5))]])]))
(:album message))])

View File

@ -10,6 +10,7 @@
[status-im2.contexts.chat.messages.content.reactions.view :as reactions] [status-im2.contexts.chat.messages.content.reactions.view :as reactions]
[status-im2.contexts.chat.messages.content.status.view :as status] [status-im2.contexts.chat.messages.content.status.view :as status]
[status-im2.contexts.chat.messages.content.system.text.view :as system.text] [status-im2.contexts.chat.messages.content.system.text.view :as system.text]
[status-im2.contexts.chat.messages.content.album.view :as album]
[quo2.core :as quo] [quo2.core :as quo]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[status-im.ui2.screens.chat.messages.message :as old-message] [status-im.ui2.screens.chat.messages.message :as old-message]
@ -94,6 +95,8 @@
constants/content-type-audio [not-implemented/not-implemented constants/content-type-audio [not-implemented/not-implemented
[old-message/audio message-data]] [old-message/audio message-data]]
constants/content-type-album [album/album-message message-data]
[not-implemented/not-implemented [content.unknown/unknown-content message-data]]) [not-implemented/not-implemented [content.unknown/unknown-content message-data]])
[status/status message-data]]]]])) [status/status message-data]]]]]))

View File

@ -179,7 +179,9 @@
:on-layout on-messages-view-layout})] :on-layout on-messages-view-layout})]
[quo/floating-shell-button [quo/floating-shell-button
(merge {:jump-to (merge {:jump-to
{:on-press #(rf/dispatch [:shell/navigate-to-jump-to]) {:on-press #(do
(rf/dispatch [:close-chat])
(rf/dispatch [:shell/navigate-to-jump-to]))
:label (i18n/label :t/jump-to)}} :label (i18n/label :t/jump-to)}}
(when @show-floating-scroll-down-button (when @show-floating-scroll-down-button
{:scroll-to-bottom {:on-press scroll-to-bottom}})) {:scroll-to-bottom {:on-press scroll-to-bottom}}))

View File

@ -1,4 +1,4 @@
(ns status-im.ui2.screens.chat.photo-selector.style (ns status-im2.contexts.chat.photo-selector.style
(:require [quo2.foundations.colors :as colors] (:require [quo2.foundations.colors :as colors]
[react-native.platform :as platform])) [react-native.platform :as platform]))
@ -37,6 +37,11 @@
:margin-left 20 :margin-left 20
:margin-bottom 24}) :margin-bottom 24})
(def title-container
{:flex-direction :row
:position :absolute
:align-self :center})
(defn chevron-container (defn chevron-container
[] []
{:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80) {:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
@ -68,3 +73,4 @@
:border-radius 8 :border-radius 8
:top 8 :top 8
:right 8}) :right 8})

View File

@ -1,69 +1,82 @@
(ns status-im.ui2.screens.chat.photo-selector.view (ns status-im2.contexts.chat.photo-selector.view
(:require [i18n.i18n :as i18n] (:require [i18n.i18n :as i18n]
[quo.components.safe-area :as safe-area] [quo.components.safe-area :as safe-area]
[quo2.components.notifications.info-count :as info-count] [quo2.components.notifications.info-count :as info-count]
[quo2.core :as quo2] [quo2.core :as quo]
[quo2.foundations.colors :as colors] [quo2.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient] [react-native.linear-gradient :as linear-gradient]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.ui2.screens.chat.photo-selector.style :as style] [status-im2.contexts.chat.photo-selector.style :as style]
[status-im.utils.core :as utils] [status-im.utils.core :as utils]
[quo.react]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(def selected (reagent/atom [])) (def selected (reagent/atom []))
(defn on-press-confirm-selection
[chat-id]
(rf/dispatch [:chat.ui/clear-sending-images chat-id])
(doseq [item @selected]
(rf/dispatch [:chat.ui/camera-roll-pick item]))
(reset! selected [])
(rf/dispatch [:bottom-sheet/hide]))
(defn bottom-gradient (defn bottom-gradient
[chat-id selected-images] [chat-id selected-images]
[:f> [:f>
(fn [] (fn []
(let [safe-area (safe-area/use-safe-area)] (let [safe-area (safe-area/use-safe-area)]
(when (or (pos? (count @selected)) selected-images) (when (or (seq @selected) selected-images)
[linear-gradient/linear-gradient [linear-gradient/linear-gradient
{:colors [:black :transparent] {:colors [:black :transparent]
:start {:x 0 :y 1} :start {:x 0 :y 1}
:end {:x 0 :y 0} :end {:x 0 :y 0}
:style (style/gradient-container safe-area)} :style (style/gradient-container safe-area)}
[quo2/button [quo/button
{:style {:align-self :stretch {:style {:align-self :stretch
:margin-horizontal 20} :margin-horizontal 20}
:on-press #(do :on-press #(on-press-confirm-selection chat-id)
(rf/dispatch [:chat.ui/clear-sending-images chat-id]) :accessibility-label :confirm-selection}
(doseq [item @selected]
(rf/dispatch [:chat.ui/camera-roll-pick item]))
(reset! selected [])
(rf/dispatch [:bottom-sheet/hide]))}
(i18n/label :t/confirm-selection)]])))]) (i18n/label :t/confirm-selection)]])))])
(defn clear-button (defn clear-button
[] []
(when (pos? (count @selected)) (when (seq @selected)
[rn/touchable-opacity [rn/touchable-opacity
{:on-press #(reset! selected []) {:on-press #(reset! selected [])
:style (style/clear-container)} :style (style/clear-container)
[quo2/text {:weight :medium} (i18n/label :t/clear)]])) :accessibility-label :clear}
[quo/text {:weight :medium} (i18n/label :t/clear)]]))
(defn remove-selected
[coll item]
(vec (remove #(= % item) coll)))
(defn image (defn image
[item index _ {:keys [window-width]}] [item index _ {:keys [window-width]}]
[rn/touchable-opacity [rn/touchable-opacity
{:active-opacity 1 {:active-opacity 1
:on-press (fn [] :on-press (fn []
(if (some #{item} @selected) (if (some #{item} @selected)
(reset! selected (vec (remove #(= % item) @selected))) (swap! selected remove-selected item)
(swap! selected conj item)))} (swap! selected conj item)))
:accessibility-label (str "image-" index)}
[rn/image [rn/image
{:source {:uri item} {:source {:uri item}
:style (style/image window-width index)}] :style (style/image window-width index)}]
(when (some #{item} @selected) (when (some #{item} @selected)
[rn/view {:style (style/overlay window-width)}]) [rn/view {:style (style/overlay window-width)}])
(when (some #{item} @selected) (when (some #{item} @selected)
[info-count/info-count {:style style/image-count} [info-count/info-count
{:style style/image-count
:accessibility-label (str "count-" index)}
(inc (utils/first-index #(= item %) @selected))])]) (inc (utils/first-index #(= item %) @selected))])])
(defn photo-selector (defn photo-selector
[chat-id] [chat-id]
(rf/dispatch [:chat.ui/camera-roll-get-photos 20]) (rf/dispatch [:chat.ui/camera-roll-get-photos 20])
(let [selected-images (keys (get-in (rf/sub [:chat/inputs]) [chat-id :metadata :sending-image]))] (let [selected-images (keys (rf/sub [:chats/sending-image]))]
(when selected-images (when selected-images
(reset! selected (vec selected-images))) (reset! selected (vec selected-images)))
[:f> [:f>
@ -78,17 +91,15 @@
[rn/touchable-opacity [rn/touchable-opacity
{:on-press #(js/alert "Camera: not implemented") {:on-press #(js/alert "Camera: not implemented")
:style (style/camera-button-container)} :style (style/camera-button-container)}
[quo2/icon :i/camera {:color (colors/theme-colors colors/neutral-100 colors/white)}]] [quo/icon :i/camera {:color (colors/theme-colors colors/neutral-100 colors/white)}]]
[rn/view [rn/view
{:style {:flex-direction :row {:style style/title-container}
:position :absolute [quo/text {:weight :medium} (i18n/label :t/recent)]
:align-self :center}}
[quo2/text {:weight :medium} (i18n/label :t/recent)]
[rn/view {:style (style/chevron-container)} [rn/view {:style (style/chevron-container)}
[quo2/icon :i/chevron-down {:color (colors/theme-colors colors/neutral-100 colors/white)}]]] [quo/icon :i/chevron-down {:color (colors/theme-colors colors/neutral-100 colors/white)}]]]
[clear-button] [clear-button]
[rn/flat-list [rn/flat-list
{:key-fn (fn [item] item) {:key-fn identity
:render-fn image :render-fn image
:render-data {:window-width window-width} :render-data {:window-width window-width}
:data camera-roll-photos :data camera-roll-photos
@ -100,3 +111,4 @@
has-next-page?])}] has-next-page?])}]
[bottom-gradient chat-id selected-images]]))])) [bottom-gradient chat-id selected-images]]))]))

View File

@ -45,11 +45,13 @@
[status-im2.contexts.quo-preview.notifications.activity-logs :as activity-logs] [status-im2.contexts.quo-preview.notifications.activity-logs :as activity-logs]
[status-im2.contexts.quo-preview.notifications.toast :as toast] [status-im2.contexts.quo-preview.notifications.toast :as toast]
[status-im2.contexts.quo-preview.posts-and-attachments.messages-skeleton :as messages-skeleton] [status-im2.contexts.quo-preview.posts-and-attachments.messages-skeleton :as messages-skeleton]
[status-im2.contexts.quo-preview.profile.profile-card :as profile-card]
[status-im2.contexts.quo-preview.reactions.react :as react] [status-im2.contexts.quo-preview.reactions.react :as react]
[status-im2.contexts.quo-preview.record-audio.record-audio :as record-audio] [status-im2.contexts.quo-preview.record-audio.record-audio :as record-audio]
[status-im2.contexts.quo-preview.selectors.disclaimer :as disclaimer] [status-im2.contexts.quo-preview.selectors.disclaimer :as disclaimer]
[status-im2.contexts.quo-preview.selectors.filter :as filter] [status-im2.contexts.quo-preview.selectors.filter :as filter]
[status-im2.contexts.quo-preview.selectors.selectors :as selectors] [status-im2.contexts.quo-preview.selectors.selectors :as selectors]
[status-im2.contexts.quo-preview.settings.accounts :as accounts]
[status-im2.contexts.quo-preview.settings.privacy-option :as privacy-option] [status-im2.contexts.quo-preview.settings.privacy-option :as privacy-option]
[status-im2.contexts.quo-preview.switcher.switcher-cards :as switcher-cards] [status-im2.contexts.quo-preview.switcher.switcher-cards :as switcher-cards]
[status-im2.contexts.quo-preview.tabs.account-selector :as account-selector] [status-im2.contexts.quo-preview.tabs.account-selector :as account-selector]
@ -63,7 +65,6 @@
[status-im2.contexts.quo-preview.wallet.lowest-price :as lowest-price] [status-im2.contexts.quo-preview.wallet.lowest-price :as lowest-price]
[status-im2.contexts.quo-preview.wallet.network-amount :as network-amount] [status-im2.contexts.quo-preview.wallet.network-amount :as network-amount]
[status-im2.contexts.quo-preview.wallet.network-breakdown :as network-breakdown] [status-im2.contexts.quo-preview.wallet.network-breakdown :as network-breakdown]
[status-im2.contexts.quo-preview.settings.accounts :as accounts]
[status-im2.contexts.quo-preview.wallet.token-overview :as token-overview])) [status-im2.contexts.quo-preview.wallet.token-overview :as token-overview]))
(def screens-categories (def screens-categories
@ -175,6 +176,9 @@
:posts-and-attachments [{:name :messages-skeleton :posts-and-attachments [{:name :messages-skeleton
:insets {:top false} :insets {:top false}
:component messages-skeleton/preview-messages-skeleton}] :component messages-skeleton/preview-messages-skeleton}]
:profile [{:name :profile-card
:insets {:top false}
:component profile-card/preview-profile-card}]
:reactions [{:name :react :reactions [{:name :react
:insets {:top false} :insets {:top false}
:component react/preview-react}] :component react/preview-react}]

View File

@ -108,6 +108,7 @@
:show-cancel false :show-cancel false
:style {:border-radius 4 :style {:border-radius 4
:border-width 1 :border-width 1
:color (colors/theme-colors colors/neutral-100 colors/white)
:border-color (colors/theme-colors colors/neutral-100 colors/white)} :border-color (colors/theme-colors colors/neutral-100 colors/white)}
:on-change-text #(do :on-change-text #(do
(reset! state* (if (and suffix (> (count %) (count @state*))) (reset! state* (if (and suffix (> (count %) (count @state*)))

View File

@ -0,0 +1,94 @@
(ns status-im2.contexts.quo-preview.profile.profile-card
(:require [quo2.foundations.colors :as colors]
[react-native.core :as rn]
[reagent.core :as reagent]
[quo2.core :as quo]
[status-im.react-native.resources :as resources]
[status-im2.contexts.quo-preview.preview :as preview]))
(def descriptor
[{:label "Show: Sign in to this profile?"
:key :show-sign-profile?
:type :boolean}
{:label "Show: Is from key card?"
:key :key-card?
:type :boolean}
{:label "Show: Emoji hash?"
:key :show-emoji-hash?
:type :boolean}
{:label "Customization Color"
:key :customization-color
:type :select
:options [{:key :primary
:value "Primary"}
{:key :purple
:value "Purple"}
{:key :indigo
:value "Indigo"}
{:key :turquoise
:value "Turquoise"}
{:key :blue
:value "Blue"}
{:key :green
:value "Green"}
{:key :yellow
:value "Yellow"}
{:key :orange
:value "Orange"}
{:key :red
:value "Red"}
{:key :pink
:value "Pink"}
{:key :brown
:value "Brown"}
{:key :beige
:value "Beige"}]}
{:label "Name"
:key :name
:type :text}
{:label "Hash"
:key :hash
:type :text}
{:label "Emoji hash"
:key :emoji-hash
:type :text}
{:label "Sign button label"
:key :sign-label
:type :text}])
(defn cool-preview
[]
(let [state (reagent/atom {:show-sign-profile? true
:key-card? true
:name "Matt Grote"
:sign-label "Sign in to this profile"
:on-press-dots nil
:on-press-sign nil
:customization-color :turquoise
:profile-picture (resources/get-mock-image :user-picture-male5)
:show-emoji-hash? true
:hash "zQ3k83euenmcikw7474hfu73t5N"
:emoji-hash "😄😂🫣🍑😇🤢😻🥷🏻🦸🏻‍♀️🦸🏻🦸🏻‍♂️🦹🏻‍♀️🧑🏻‍🎄🎅🏻"})]
(fn []
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
[rn/view {:padding-bottom 150}
[rn/view {:flex 1}
[preview/customizer state descriptor]]
[rn/view
{:padding-vertical 60
:flex-direction :row
:margin-horizontal 20
:justify-content :center}
[quo/profile-card @state]]]])))
(defn preview-profile-card
[]
[rn/view
{:background-color (colors/theme-colors colors/white
colors/neutral-90)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:key-fn str}]])

View File

@ -2,6 +2,7 @@
(:require [quo2.foundations.colors :as colors] (:require [quo2.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im2.common.constants :as constants]
[status-im.react-native.resources :as resources] [status-im.react-native.resources :as resources]
[status-im2.contexts.quo-preview.preview :as preview] [status-im2.contexts.quo-preview.preview :as preview]
[status-im2.contexts.shell.cards.view :as switcher-cards] [status-im2.contexts.shell.cards.view :as switcher-cards]
@ -19,6 +20,8 @@
:value "Group Messaging"} :value "Group Messaging"}
{:key shell.constants/community-card {:key shell.constants/community-card
:value "Community Card"} :value "Community Card"}
{:key shell.constants/community-channel-card
:value "Community Channel Card"}
{:key shell.constants/browser-card {:key shell.constants/browser-card
:value "Browser Card"} :value "Browser Card"}
{:key shell.constants/wallet-card {:key shell.constants/wallet-card
@ -51,26 +54,20 @@
{:label "Content Type" {:label "Content Type"
:key :content-type :key :content-type
:type :select :type :select
:options [{:key :text :options [{:key constants/content-type-text
:value :text} :value :text}
{:key :photo {:key constants/content-type-image
:value :photo} :value :photo}
{:key :sticker {:key constants/content-type-sticker
:value :sticker} :value :sticker}
{:key :gif {:key constants/content-type-gif
:value :gif} :value :gif}
{:key :audio {:key constants/content-type-audio
:value :audio} :value :audio}
{:key :community {:key constants/content-type-community
:value :community} :value :community}
{:key :link {:key constants/content-type-link
:value :link} :value :link}]}
{:key :code
:value :code}
{:key :channel
:value :channel}
{:key :community-info
:value :community-info}]}
{:label "Last Message" {:label "Last Message"
:key :last-message :key :last-message
:type :text} :type :text}
@ -90,6 +87,7 @@
(def sticker {:source (resources/get-mock-image :sticker)}) (def sticker {:source (resources/get-mock-image :sticker)})
(def community-avatar {:source (resources/get-mock-image :community-logo)}) (def community-avatar {:source (resources/get-mock-image :community-logo)})
(def gif {:source (resources/get-mock-image :gif)}) (def gif {:source (resources/get-mock-image :gif)})
(def coinbase-community (resources/get-mock-image :coinbase))
(def photos-list (def photos-list
[{:source (resources/get-mock-image :photo1)} [{:source (resources/get-mock-image :photo1)}
@ -102,13 +100,27 @@
(defn get-mock-content (defn get-mock-content
[data] [data]
(case (:content-type data) (case (:content-type data)
:text (:last-message data) constants/content-type-text
:photo photos-list (:last-message data)
:sticker sticker
:gif gif constants/content-type-image
:channel {:emoji "🍑" :channel-name "# random"} photos-list
:community-info {:type :kicked}
(:audio :community :link :code) nil)) constants/content-type-sticker
sticker
constants/content-type-gif
gif
constants/content-type-audio
"00:32"
constants/content-type-community
{:avatar coinbase-community
:community-name "Coinbase"}
constants/content-type-link
nil))
(defn get-mock-data (defn get-mock-data
[{:keys [type] :as data}] [{:keys [type] :as data}]
@ -120,11 +132,19 @@
:notification-indicator (:notification-indicator data) :notification-indicator (:notification-indicator data)
:counter-label (:counter-label data) :counter-label (:counter-label data)
:content-type (:content-type data) :content-type (:content-type data)
:community-channel {:emoji "🍑" :channel-name "# random"}
:community-info {:type :kicked}
:data (get-mock-content data)}} :data (get-mock-content data)}}
(case type (case type
shell.constants/one-to-one-chat-card {:avatar-params {:full-name (:title data)}} shell.constants/one-to-one-chat-card
shell.constants/private-group-chat-card {} {:avatar-params {:full-name (:title data)}}
shell.constants/community-card {:avatar-params community-avatar}
shell.constants/private-group-chat-card
{}
(shell.constants/community-card
shell.constants/community-channel-card)
{:avatar-params community-avatar}
{}))) {})))
(defn cool-preview (defn cool-preview
@ -136,7 +156,7 @@
:banner? false :banner? false
:notification-indicator :counter :notification-indicator :counter
:counter-label 2 :counter-label 2
:content-type :text :content-type constants/content-type-text
:last-message "This is fantastic! Ethereum" :last-message "This is fantastic! Ethereum"
:preview-label-color colors/white})] :preview-label-color colors/white})]
(fn [] (fn []
@ -151,7 +171,7 @@
(defn preview-switcher-cards (defn preview-switcher-cards
[] []
[rn/view [rn/view
{:background-color colors/neutral-100 {:background-color (colors/theme-colors colors/white colors/neutral-90)
:flex 1} :flex 1}
[rn/flat-list [rn/flat-list
{:flex 1 {:flex 1

View File

@ -3,6 +3,7 @@
[quo2.foundations.colors :as colors] [quo2.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.react-native.resources :as resources]
[status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.core :as multiaccounts]
[status-im2.contexts.quo-preview.preview :as preview])) [status-im2.contexts.quo-preview.preview :as preview]))
@ -19,6 +20,8 @@
(def example-photo2 (def example-photo2
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=") "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=")
(def coinbase-community (resources/get-mock-image :coinbase))
(def main-descriptor (def main-descriptor
[{:label "Type" [{:label "Type"
:key :type :key :type
@ -30,7 +33,11 @@
{:key :group-avatar {:key :group-avatar
:value "Group avatar"} :value "Group avatar"}
{:key :context-tag {:key :context-tag
:value "Context tag"}]}]) :value "Context tag"}
{:key :audio
:value "Audio"}
{:key :community
:value "Community"}]}])
(def context-tag-descriptor (def context-tag-descriptor
[{:label "Label" [{:label "Label"
@ -100,13 +107,17 @@
:public-key :public-key
[quo2/public-key-tag {} example-pk] [quo2/public-key-tag {} example-pk]
:avatar :avatar
[quo2/user-avatar-tag {} current-username (:photo @state)])]]])))) [quo2/user-avatar-tag {} current-username (:photo @state)]
:audio
[quo2/audio-tag "00:32"]
:community
[quo2/community-tag coinbase-community "Coinbase"])]]]))))
(defn preview-context-tags (defn preview-context-tags
[] []
[rn/view [rn/view
{:background-color (colors/theme-colors colors/white {:background-color (colors/theme-colors colors/white
colors/neutral-90) colors/neutral-95)
:flex 1} :flex 1}
[rn/flat-list [rn/flat-list
{:flex 1 {:flex 1

View File

@ -5,59 +5,85 @@
[quo2.foundations.colors :as colors] [quo2.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.fast-image :as fast-image] [react-native.fast-image :as fast-image]
[status-im2.common.constants :as constants]
[status-im2.contexts.shell.cards.style :as style] [status-im2.contexts.shell.cards.style :as style]
[status-im2.contexts.shell.constants :as shell.constants])) [status-im2.contexts.shell.constants :as shell.constants]))
(defn content-container (defn content-container
[{:keys [content-type data new-notifications? color-50]}] [type {:keys [content-type data new-notifications? color-50 community-info community-channel]}]
[rn/view {:style (style/content-container new-notifications?)} [rn/view {:style (style/content-container new-notifications?)}
;; TODO - Use status-im2.common.shell.constants for content type (case type
(case content-type shell.constants/community-card
:text [quo/text (case (:type community-info)
{:size :paragraph-2 :pending [quo/status-tag
:weight :regular {:status {:type :pending}
:number-of-lines 1 :label (i18n/label :t/pending)
:ellipsize-mode :tail :size :small
:style style/last-message-text} :override-theme :dark}]
data] :kicked [quo/status-tag
:photo [quo/preview-list {:status {:type :negative}
{:type :photo :size :small
:more-than-99-label (i18n/label :counter-99-plus) :override-theme :dark
:size 24 :label (i18n/label :t/kicked)}]
:override-theme :dark} data] (:count :permission) [:<>]) ;; Add components for these cases
:sticker [fast-image/fast-image
{:source (:source data) shell.constants/community-channel-card
:style style/sticker}] [rn/view
:gif [fast-image/fast-image {:style {:flex-direction :row
{:source (:source data) :align-items :center}}
:style style/gif}] [quo/channel-avatar
:channel [rn/view {:emoji (:emoji community-channel)
{:style {:flex-direction :row :emoji-background-color (colors/alpha color-50 0.1)}]
:align-items :center}} [quo/text
[quo/channel-avatar {:size :paragraph-2
{:emoji (:emoji data) :weight :medium
:emoji-background-color (colors/alpha color-50 0.1)}] :number-of-lines 1
[quo/text :ellipsize-mode :tail
{:size :paragraph-2 :style style/community-channel}
:weight :medium (:channel-name community-channel)]]
:number-of-lines 1
:ellipsize-mode :tail (case content-type
:style style/community-channel} constants/content-type-text
(:channel-name data)]] [quo/text
:community-info (case (:type data) {:size :paragraph-2
:pending [quo/status-tag :weight :regular
{:status {:type :pending} :number-of-lines 1
:label (i18n/label :t/pending) :ellipsize-mode :tail
:size :small :style style/last-message-text}
:override-theme :dark}] data]
:kicked [quo/status-tag
{:status {:type :negative} constants/content-type-image
:size :small [quo/preview-list
:override-theme :dark {:type :photo
:label (i18n/label :t/kicked)}] :more-than-99-label (i18n/label :counter-99-plus)
(:count :permission) [:<>]) ;; Add components for these cases :size 24
(:audio :community :link :code) ;; Components not available :override-theme :dark} data]
[:<>])])
constants/content-type-sticker
[fast-image/fast-image
{:source (:source data)
:style style/sticker}]
constants/content-type-gif
[fast-image/fast-image
{:source (:source data)
:style style/gif}]
constants/content-type-audio
[quo/audio-tag data {:override-theme :dark}]
constants/content-type-community
[quo/community-tag
(:avatar data)
(:community-name data)
{:override-theme :dark}]
(constants/content-type-link) ;; Components not available
;; Code snippet content type is not supported yet
[:<>]
nil))])
(defn notification-container (defn notification-container
[{:keys [notification-indicator counter-label color-60]}] [{:keys [notification-indicator counter-label color-60]}]
@ -70,9 +96,9 @@
[rn/view {:style (style/unread-dot color-60)}])]) [rn/view {:style (style/unread-dot color-60)}])])
(defn bottom-container (defn bottom-container
[{:keys [new-notifications?] :as content}] [type {:keys [new-notifications?] :as content}]
[:<> [:<>
[content-container content] [content-container type content]
(when new-notifications? (when new-notifications?
[notification-container content])]) [notification-container content])])
@ -92,7 +118,8 @@
:size :large :size :large
:override-theme :dark}] :override-theme :dark}]
shell.constants/community-card (shell.constants/community-card
shell.constants/community-channel-card)
(if (:source avatar-params) (if (:source avatar-params)
[fast-image/fast-image [fast-image/fast-image
{:source (:source avatar-params) {:source (:source avatar-params)
@ -106,19 +133,41 @@
(string/upper-case (first (:name avatar-params)))]]))) (string/upper-case (first (:name avatar-params)))]])))
(defn subtitle (defn subtitle
[{:keys [content-type data]}] [type {:keys [content-type data]}]
(case content-type (case type
:text (i18n/label :t/message) shell.constants/community-card
:photo (i18n/label :t/n-photos {:count (count data)}) (i18n/label :t/community)
:sticker (i18n/label :t/sticker)
:gif (i18n/label :t/gif) shell.constants/community-channel-card
:audio (i18n/label :t/audio-message) (i18n/label :t/community-channel)
:community (i18n/label :t/link-to-community)
:link (i18n/label :t/external-link) (case content-type
:code (i18n/label :t/code-snippet) constants/content-type-text
:channel (i18n/label :t/community-channel) (i18n/label :t/message)
:community-info (i18n/label :t/community)
(i18n/label :t/community))) constants/content-type-image
(i18n/label
(if (= (count data) 1)
:t/one-photo
:t/n-photos)
{:count (count data)})
constants/content-type-sticker
(i18n/label :t/sticker)
constants/content-type-gif
(i18n/label :t/gif)
constants/content-type-audio
(i18n/label :t/audio-message)
constants/content-type-community
(i18n/label :t/link-to-community)
constants/content-type-link
(i18n/label :t/external-link)
"")))
;; Screens Card ;; Screens Card
(defn screens-card (defn screens-card
@ -144,8 +193,8 @@
{:size :paragraph-2 {:size :paragraph-2
:weight :medium :weight :medium
:style style/subtitle} :style style/subtitle}
(subtitle content)] (subtitle type content)]
[bottom-container (merge {:color-50 color-50 :color-60 color-60} content)]] [bottom-container type (merge {:color-50 color-50 :color-60 color-60} content)]]
(when avatar-params (when avatar-params
[rn/view {:style style/avatar-container} [rn/view {:style style/avatar-container}
[avatar avatar-params type customization-color]]) [avatar avatar-params type customization-color]])
@ -188,30 +237,26 @@
(defn card (defn card
[{:keys [type] :as data}] [{:keys [type] :as data}]
(case type (case type
shell.constants/empty-card ;; Placeholder
shell.constants/empty-card ;; Placeholder
[empty-card] [empty-card]
shell.constants/one-to-one-chat-card ;; Screens Card (shell.constants/one-to-one-chat-card ;; Screens Card
shell.constants/private-group-chat-card
shell.constants/community-card
shell.constants/community-channel-card)
[screens-card data] [screens-card data]
shell.constants/private-group-chat-card ;; Screens Card shell.constants/browser-card ;; Browser Card
[screens-card data]
shell.constants/community-card ;; Screens Card
[screens-card data]
shell.constants/browser-card ;; Browser Card
[browser-card data] [browser-card data]
shell.constants/wallet-card ;; Wallet Card shell.constants/wallet-card ;; Wallet Card
[wallet-card data] [wallet-card data]
shell.constants/wallet-collectible ;; Wallet Card shell.constants/wallet-collectible ;; Wallet Card
[wallet-collectible data] [wallet-collectible data]
shell.constants/wallet-graph ;; Wallet Card shell.constants/wallet-graph ;; Wallet Card
[wallet-graph data] [wallet-graph data]
shell.constants/communities-discover ;; Home Card shell.constants/communities-discover ;; Home Card
[communities-discover data])) [communities-discover data]))

View File

@ -60,8 +60,9 @@
(def ^:const one-to-one-chat-card 1) (def ^:const one-to-one-chat-card 1)
(def ^:const private-group-chat-card 2) (def ^:const private-group-chat-card 2)
(def ^:const community-card 3) (def ^:const community-card 3)
(def ^:const browser-card 4) (def ^:const community-channel-card 4)
(def ^:const wallet-card 5) (def ^:const browser-card 5)
(def ^:const wallet-collectible 6) (def ^:const wallet-card 6)
(def ^:const wallet-graph 7) (def ^:const wallet-collectible 7)
(def ^:const communities-discover 8) (def ^:const wallet-graph 8)
(def ^:const communities-discover 9)

View File

@ -1,10 +1,12 @@
(ns status-im2.contexts.shell.events (ns status-im2.contexts.shell.events
(:require [re-frame.core :as re-frame] (:require [utils.re-frame :as rf]
[re-frame.core :as re-frame]
[status-im.utils.core :as utils]
[status-im2.common.constants :as constants] [status-im2.common.constants :as constants]
[status-im2.navigation.events :as navigation]
[status-im2.contexts.shell.animation :as animation] [status-im2.contexts.shell.animation :as animation]
[status-im2.contexts.shell.constants :as shell.constants] [status-im2.contexts.shell.constants :as shell.constants]
[status-im2.navigation.events :as navigation] [status-im.data-store.switcher-cards :as switcher-cards-store]))
[utils.re-frame :as rf]))
;; Effects ;; Effects
@ -29,62 +31,76 @@
;; Events ;; Events
(rf/defn add-switcher-card (rf/defn switcher-cards-loaded
{:events [:shell/add-switcher-card]} {:events [:shell/switcher-cards-loaded]}
[{:keys [db now] :as cofx} view-id id] [{:keys [db]} loaded-switcher-cards]
{:db (assoc db
:shell/switcher-cards
(utils/index-by :card-id (switcher-cards-store/<-rpc loaded-switcher-cards)))})
(defn calculate-card-data
[db now view-id id]
(case view-id (case view-id
:chat :chat
(let [chat (get-in db [:chats id])] (let [chat (get-in db [:chats id])]
(case (:chat-type chat) (case (:chat-type chat)
constants/one-to-one-chat-type constants/one-to-one-chat-type
{:shell/navigate-from-shell-fx :chats-stack {:navigate-from :chats-stack
:db (assoc-in :card-id id
db :switcher-card {:type shell.constants/one-to-one-chat-card
[:shell/switcher-cards id] :card-id id
{:type shell.constants/one-to-one-chat-card :clock now
:id id :screen-id id}}
:clock now})}
constants/private-group-chat-type constants/private-group-chat-type
{:shell/navigate-from-shell-fx :chats-stack {:navigate-from :chats-stack
:db (assoc-in :card-id id
db :switcher-card {:type shell.constants/private-group-chat-card
[:shell/switcher-cards id] :card-id id
{:type shell.constants/private-group-chat-card :clock now
:id id :screen-id id}}
:clock now})}
constants/community-chat-type constants/community-chat-type
{:shell/navigate-from-shell-fx :communities-stack {:navigate-from :communities-stack
:db (assoc-in :card-id (:community-id chat)
db :switcher-card {:type shell.constants/community-channel-card
[:shell/switcher-cards (:community-id chat)] :card-id (:community-id chat)
{:type shell.constants/community-card :clock now
:id (:community-id chat) :screen-id (:chat-id chat)}}
:clock now
:content {:content-type :channel
:data {:emoji (:emoji chat)
:channel-id (:chat-id chat)
:channel-name (:chat-name
chat)}}})}
nil)) nil))
:community :community
{:shell/navigate-from-shell-fx :communities-stack {:navigate-from :communities-stack
:db (assoc-in :card-id (:community-id id)
db :switcher-card {:type shell.constants/community-card
[:shell/switcher-cards (:community-id id)] :card-id (:community-id id)
{:type shell.constants/community-card :clock now
:id (:community-id id) :screen-id (:community-id id)}}
:clock now})}
nil)) nil))
(rf/defn add-switcher-card
{:events [:shell/add-switcher-card]}
[{:keys [db now] :as cofx} view-id id]
(let [card-data (calculate-card-data db now view-id id)
switcher-card (:switcher-card card-data)]
(when card-data
(rf/merge
cofx
{:db (assoc-in
db
[:shell/switcher-cards (:card-id card-data)]
switcher-card)
:shell/navigate-from-shell-fx (:navigate-from card-data)}
(switcher-cards-store/upsert-switcher-card-rpc switcher-card)))))
(rf/defn close-switcher-card (rf/defn close-switcher-card
{:events [:shell/close-switcher-card]} {:events [:shell/close-switcher-card]}
[{:keys [db]} id] [{:keys [db] :as cofx} card-id]
{:db (update-in db [:shell/switcher-cards] dissoc id)}) (rf/merge
cofx
{:db (update db :shell/switcher-cards dissoc card-id)}
(switcher-cards-store/delete-switcher-card-rpc card-id)))
(rf/defn navigate-to-jump-to (rf/defn navigate-to-jump-to
{:events [:shell/navigate-to-jump-to]} {:events [:shell/navigate-to-jump-to]}

View File

@ -51,9 +51,13 @@
:accessibility-label :shell-placeholder-view}) :accessibility-label :shell-placeholder-view})
(def placeholder-image (def placeholder-image
{:margin-top 186 {:margin-top 186
:width 120 :width 120
:height 120}) :height 120
;; Code to remove once placeholder image/vector will be available
:border-width 5
:border-radius 10
:border-color :red})
(def placeholder-title (def placeholder-title
{:margin-top 20 {:margin-top 20

View File

@ -47,20 +47,19 @@
(i18n/label :t/jump-to)]) (i18n/label :t/jump-to)])
(defn render-card (defn render-card
[{:keys [id type content] :as card}] [{:keys [type screen-id] :as card}]
(let [card-data (case type (let [card-data (case type
shell.constants/one-to-one-chat-card shell.constants/one-to-one-chat-card
(rf/sub [:shell/one-to-one-chat-card id]) (rf/sub [:shell/one-to-one-chat-card screen-id])
shell.constants/private-group-chat-card shell.constants/private-group-chat-card
(rf/sub [:shell/private-group-chat-card id]) (rf/sub [:shell/private-group-chat-card screen-id])
shell.constants/community-card shell.constants/community-card
(if content (rf/sub [:shell/community-card screen-id])
(rf/sub [:shell/community-channel-card
id (get-in content [:data :channel-id]) shell.constants/community-channel-card
content]) (rf/sub [:shell/community-channel-card screen-id])
(rf/sub [:shell/community-card id]))
nil)] nil)]
[switcher-cards/card (merge card card-data)])) [switcher-cards/card (merge card card-data)]))

View File

@ -10,8 +10,7 @@
[status-im2.navigation.state :as state] [status-im2.navigation.state :as state]
[status-im2.navigation.view :as views] [status-im2.navigation.view :as views]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.re-frame :as rf] ;; TODO (14/11/22 flexsurfer) move to status-im2 namespace [utils.re-frame :as rf]))
))
;; REGISTER COMPONENT (LAZY) ;; REGISTER COMPONENT (LAZY)
(defn reg-comp (defn reg-comp

View File

@ -12,8 +12,7 @@
[status-im.ui.screens.signing.views :as signing] [status-im.ui.screens.signing.views :as signing]
[status-im.ui.screens.wallet-connect.session-proposal.views :as wallet-connect] [status-im.ui.screens.wallet-connect.session-proposal.views :as wallet-connect]
[status-im.ui.screens.wallet.send.views :as wallet.send.views] [status-im.ui.screens.wallet.send.views :as wallet.send.views]
[status-im2.common.toasts.view :as toasts] ;; TODO (14/11/22 flexsurfer) move to status-im2 [status-im2.common.toasts.view :as toasts]
;; namespace
[status-im2.navigation.screens :as screens] [status-im2.navigation.screens :as screens]
[status-im2.setup.config :as config] [status-im2.setup.config :as config]
[status-im2.setup.hot-reload :as reloader])) [status-im2.setup.hot-reload :as reloader]))

View File

@ -1,7 +1,6 @@
(ns status-im2.setup.config (ns status-im2.setup.config
(:require [clojure.string :as string] (:require [clojure.string :as string]
[react-native.config :as react-native-config] ;; TODO (14/11/22 flexsurfer move to status-im2 [react-native.config :as react-native-config]
;; namespace
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.ens :as ens])) [status-im.ethereum.ens :as ens]))

View File

@ -1,6 +1,7 @@
(ns status-im2.setup.core (ns status-im2.setup.core
(:require (:require
[i18n.i18n :as i18n] [i18n.i18n :as i18n]
[status-im2.setup.i18n-resources :as i18n-resources]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[re-frame.interop :as interop] [re-frame.interop :as interop]
[react-native.core :as rn] [react-native.core :as rn]
@ -13,8 +14,7 @@
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.notifications.local :as notifications] [status-im.notifications.local :as notifications]
[status-im.utils.universal-links.core :as utils.universal-links] [status-im.utils.universal-links.core :as utils.universal-links]
[status-im2.contexts.shell.animation :as animation] ;; TODO (14/11/22 flexsurfer move to [status-im2.contexts.shell.animation :as animation]
;; status-im2 namespace
status-im2.contexts.syncing.events status-im2.contexts.syncing.events
status-im2.navigation.core status-im2.navigation.core
[status-im2.setup.config :as config] [status-im2.setup.config :as config]
@ -22,7 +22,6 @@
status-im2.setup.events status-im2.setup.events
status-im2.setup.datetime status-im2.setup.datetime
[status-im2.setup.global-error :as global-error] [status-im2.setup.global-error :as global-error]
[status-im2.setup.i18n-resources :as i18n-resources]
[status-im2.setup.log :as log] [status-im2.setup.log :as log]
status-im2.subs.root)) status-im2.subs.root))
@ -32,19 +31,15 @@
(defn init (defn init
[] []
(log/setup config/log-level) (log/setup config/log-level)
(global-error/register-handler) (global-error/register-handler)
(when platform/android? (when platform/android?
(status/set-soft-input-mode status/adjust-resize)) (status/set-soft-input-mode status/adjust-resize))
(notifications/listen-notifications) (notifications/listen-notifications)
(.addEventListener rn/app-state "change" #(re-frame/dispatch [:app-state-change %])) (.addEventListener rn/app-state "change" #(re-frame/dispatch [:app-state-change %]))
(i18n/init) (react-native-languages/add-change-listener #(fn [lang]
(i18n/set-language lang)
(react-native-languages/add-change-listener (i18n-resources/load-language lang)))
#(fn [lang]
(i18n/load-language lang i18n-resources/loaded-languages)
(i18n/set-language lang)))
(react-native-shake/add-shake-listener #(re-frame/dispatch [:shake-event])) (react-native-shake/add-shake-listener #(re-frame/dispatch [:shake-event]))
(utils.universal-links/initialize) (utils.universal-links/initialize)

View File

@ -1,5 +1,5 @@
(ns status-im2.setup.db (ns status-im2.setup.db
(:require [react-native.core :as rn] ;; TODO (14/11/22 flexsurfer move to status-im2 namespace (:require [react-native.core :as rn]
[status-im.fleet.core :as fleet] [status-im.fleet.core :as fleet]
[status-im.wallet.db :as wallet.db] [status-im.wallet.db :as wallet.db]
[status-im2.contexts.activity-center.events :as activity-center])) [status-im2.contexts.activity-center.events :as activity-center]))

View File

@ -2,7 +2,7 @@
(:require [clojure.string :as string] (:require [clojure.string :as string]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[quo2.theme :as quo2.theme] [quo2.theme :as quo2.theme]
[re-frame.core :as re-frame] ;; TODO (14/11/22 flexsurfer move to status-im2 namespace [re-frame.core :as re-frame]
[status-im.multiaccounts.login.core :as multiaccounts.login] [status-im.multiaccounts.login.core :as multiaccounts.login]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.utils.keychain.core :as keychain] [status-im.utils.keychain.core :as keychain]

View File

@ -1,6 +1,78 @@
(ns status-im2.setup.i18n-resources (ns status-im2.setup.i18n-resources
(:require [i18n.i18n :as i18n])) (:require [clojure.string :as string]
[i18n.i18n :as i18n]
[react-native.languages :as react-native-languages]))
(def default-device-language (react-native-languages/get-lang-keyword))
(def languages
#{:ar :bn :de :el :en :es :es_419 :es_AR :fil :fr :hi :id :in :it :ja :ko :ms :nl :pl :pt :pt_BR :ru
:tr :vi :zh :zh_Hant :zh_TW})
(defonce loaded-languages (defonce loaded-languages
(atom (atom
(conj #{:en} i18n/default-device-language))) (conj #{:en} default-device-language)))
(defn valid-language
[lang]
(if (contains? languages lang)
(keyword lang)
(let [parts (string/split (name lang) #"[\-\_]")
short-lang (keyword (str (first parts) "_" (second parts)))
shortest-lang (keyword (first parts))]
(if (and (> (count parts) 2) (contains? languages short-lang))
short-lang
(when (contains? languages shortest-lang)
shortest-lang)))))
(defn require-translation
[lang-key]
(when-let [lang (valid-language (keyword lang-key))]
(case lang
:ar (js/require "../translations/ar.json")
:bn (js/require "../translations/bn.json")
:de (js/require "../translations/de.json")
:el (js/require "../translations/el.json")
:en (js/require "../translations/en.json")
:es (js/require "../translations/es.json")
:es_419 (js/require "../translations/es_419.json")
:es_AR (js/require "../translations/es_AR.json")
:fil (js/require "../translations/fil.json")
:fr (js/require "../translations/fr.json")
:hi (js/require "../translations/hi.json")
:id (js/require "../translations/id.json")
:in (js/require "../translations/id.json")
:it (js/require "../translations/it.json")
:ja (js/require "../translations/ja.json")
:ko (js/require "../translations/ko.json")
:ms (js/require "../translations/ms.json")
:nl (js/require "../translations/nl.json")
:pl (js/require "../translations/pl.json")
:pt (js/require "../translations/pt.json")
:pt_BR (js/require "../translations/pt_BR.json")
:ru (js/require "../translations/ru.json")
:tr (js/require "../translations/tr.json")
:vi (js/require "../translations/vi.json")
:zh (js/require "../translations/zh.json")
:zh_Hant (js/require "../translations/zh_hant.json")
:zh_TW (js/require "../translations/zh_TW.json"))))
;; translations
(def translations-by-locale
(cond-> {:en (require-translation :en)}
(not= :en default-device-language)
(assoc default-device-language
(require-translation (-> (name default-device-language)
(string/replace "-" "_")
keyword)))))
(i18n/setup (name default-device-language) (clj->js translations-by-locale))
(defn load-language
[lang]
(when-let [lang-key (valid-language (keyword lang))]
(when-not (contains? @loaded-languages lang-key)
(aset (i18n/get-translations)
lang
(require-translation lang-key))
(swap! loaded-languages conj lang-key))))

View File

@ -3,11 +3,12 @@
[cljs.test :refer-macros [deftest is]] [cljs.test :refer-macros [deftest is]]
[clojure.set :as set] [clojure.set :as set]
[clojure.string :as string] [clojure.string :as string]
[i18n.i18n :as i18n])) [i18n.i18n :as i18n]
[status-im2.setup.i18n-resources :as i18n-resources]))
;; english as source of truth ;; english as source of truth
(def labels (def labels
(set (keys (js->clj (:en i18n/translations-by-locale) (set (keys (js->clj (:en i18n-resources/translations-by-locale)
:keywordize-keys :keywordize-keys
true)))) true))))
@ -16,7 +17,7 @@
(defn labels-for-all-locales (defn labels-for-all-locales
[] []
(->> i18n/translations-by-locale (->> i18n-resources/translations-by-locale
(mapcat #(-> % val (js->clj :keywordize-keys true) keys)) (mapcat #(-> % val (js->clj :keywordize-keys true) keys))
set)) set))
@ -1024,14 +1025,14 @@
;; locales ;; locales
(def locales (set (keys i18n/translations-by-locale))) (def locales (set (keys i18n-resources/translations-by-locale)))
(spec/def ::locale locales) (spec/def ::locale locales)
(spec/def ::locales (spec/coll-of ::locale :kind set? :into #{})) (spec/def ::locales (spec/coll-of ::locale :kind set? :into #{}))
(defn locale->labels (defn locale->labels
[locale] [locale]
(-> i18n/translations-by-locale (get locale) (js->clj :keywordize-keys true) keys set)) (-> i18n-resources/translations-by-locale (get locale) (js->clj :keywordize-keys true) keys set))
(defn locale->checkpoint (defn locale->checkpoint
[locale] [locale]

View File

@ -1,7 +1,7 @@
(ns status-im2.subs.activity-center-test (ns status-im2.subs.activity-center-test
(:require [cljs.test :refer [is testing]] (:require [cljs.test :refer [is testing]]
[re-frame.db :as rf-db] [re-frame.db :as rf-db]
[status-im.test-helpers :as h] [test-helpers.unit :as h]
status-im2.subs.activity-center status-im2.subs.activity-center
[utils.re-frame :as rf])) [utils.re-frame :as rf]))

View File

@ -145,6 +145,13 @@
(fn [[inputs public-key]] (fn [[inputs public-key]]
(get inputs (chat.models/profile-chat-topic public-key)))) (get inputs (chat.models/profile-chat-topic public-key))))
(re-frame/reg-sub
:chats/sending-image
:<- [:chats/current-chat-id]
:<- [:chat/inputs]
(fn [[chat-id inputs]]
(get-in inputs [chat-id :metadata :sending-image])))
(re-frame/reg-sub (re-frame/reg-sub
:chats/timeline-chat-input-text :chats/timeline-chat-input-text
:<- [:chats/timeline-chat-input] :<- [:chats/timeline-chat-input]
@ -283,12 +290,6 @@
(fn [{:keys [metadata]}] (fn [{:keys [metadata]}]
(:sending-contact-request metadata))) (:sending-contact-request metadata)))
(re-frame/reg-sub
:chats/sending-image
:<- [:chats/current-chat-inputs]
(fn [{:keys [metadata]}]
(:sending-image metadata)))
(re-frame/reg-sub (re-frame/reg-sub
:chats/timeline-sending-image :chats/timeline-sending-image
:<- [:chats/timeline-chat-input] :<- [:chats/timeline-chat-input]

View File

@ -102,6 +102,25 @@
(fn [messages] (fn [messages]
(empty? messages))) (empty? messages)))
(defn albumize-messages
[messages]
(get (reduce (fn [{:keys [messages albums]} message]
(let [album-id (when (:albumize? message) (:album-id message))
albums (cond-> albums album-id (update album-id conj message))
messages (if (and album-id (> (count (get albums album-id)) 3))
(conj (filterv #(not= album-id (:album-id %)) messages)
{:album (get albums album-id)
:album-id album-id
:message-id album-id
:content-type constants/content-type-album})
(conj messages message))]
{:messages messages
:albums albums}))
{:messages []
:albums {}}
messages)
:messages))
(re-frame/reg-sub (re-frame/reg-sub
:chats/raw-chat-messages-stream :chats/raw-chat-messages-stream
(fn [[_ chat-id] _] (fn [[_ chat-id] _]
@ -126,7 +145,8 @@
(datetime/timestamp) (datetime/timestamp)
chat-type chat-type
joined joined
loading-messages?)))))) loading-messages?)
(albumize-messages))))))
;;we want to keep data unchanged so react doesn't change component when we leave screen ;;we want to keep data unchanged so react doesn't change component when we leave screen
(def memo-profile-messages-stream (atom nil)) (def memo-profile-messages-stream (atom nil))

View File

@ -0,0 +1,25 @@
(ns status-im2.subs.chat.messages-test
(:require [cljs.test :refer [deftest is testing]]
[status-im2.subs.chat.messages :as messages]
[status-im2.common.constants :as constants]))
(def messages-state
[{:message-id "0x111" :album-id "abc" :albumize? true}
{:message-id "0x222" :album-id "abc" :albumize? true}
{:message-id "0x333" :album-id "abc" :albumize? true}
{:message-id "0x444" :album-id "abc" :albumize? true}
{:message-id "0x555" :album-id "efg" :albumize? true}])
(def messages-albumized-state
[{:album [{:message-id "0x444" :album-id "abc" :albumize? true}
{:message-id "0x333" :album-id "abc" :albumize? true}
{:message-id "0x222" :album-id "abc" :albumize? true}
{:message-id "0x111" :album-id "abc" :albumize? true}]
:album-id "abc"
:message-id "abc"
:content-type constants/content-type-album}
{:message-id "0x555" :album-id "efg" :albumize? true}])
(deftest albumize-messages
(testing "Finding albums in the messages list"
(is (= (messages/albumize-messages messages-state) messages-albumized-state))))

View File

@ -1,7 +1,7 @@
(ns status-im2.subs.communities-test (ns status-im2.subs.communities-test
(:require [cljs.test :refer [is testing use-fixtures]] (:require [cljs.test :refer [is testing use-fixtures]]
[re-frame.db :as rf-db] [re-frame.db :as rf-db]
[status-im.test-helpers :as h] [test-helpers.unit :as h]
status-im2.subs.communities status-im2.subs.communities
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -135,4 +135,3 @@
{:id "0x1" :name "Civilized monkeys"} {:id "0x1" :name "Civilized monkeys"}
{:id "0x2" :name "Civilized rats"}] {:id "0x2" :name "Civilized rats"}]
(rf/sub [sub-name]))))) (rf/sub [sub-name])))))

View File

@ -1,6 +1,6 @@
(ns status-im2.subs.multiaccount (ns status-im2.subs.multiaccount
(:require [cljs.spec.alpha :as spec] (:require [cljs.spec.alpha :as spec]
[clojure.set :as clojure.set] [clojure.set :as set]
[clojure.string :as string] [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
@ -23,7 +23,7 @@
(some-> (some->
current-account current-account
(select-keys [:name :preferred-name :public-key :identicon :image :images]) (select-keys [:name :preferred-name :public-key :identicon :image :images])
(clojure.set/rename-keys {:name :alias}) (set/rename-keys {:name :alias})
(multiaccounts/contact-with-names)))) (multiaccounts/contact-with-names))))
(re-frame/reg-sub (re-frame/reg-sub

View File

@ -1,21 +1,67 @@
(ns status-im2.subs.shell (ns status-im2.subs.shell
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.react-native.resources :as resources] [utils.datetime :as datetime]
[status-im2.common.constants :as status-constants])) [status-im2.common.constants :as constants]
[status-im.react-native.resources :as resources]))
(defn community-avatar
[community]
(let [images (:images community)]
(if (= (:id community) constants/status-community-id)
(resources/get-image :status-logo)
(when images
{:uri (:uri (or (:thumbnail images)
(:large images)
(first images)))}))))
(defn get-card-content (defn get-card-content
[chat] [chat communities]
(let [last-message (:last-message chat)] (let [last-message (:last-message chat)]
(case (:content-type last-message) (merge
status-constants/content-type-text (when last-message
{:content-type :text (case (:content-type last-message)
:data (get-in last-message [:content :text])} (constants/content-type-text
constants/content-type-emoji)
{:content-type constants/content-type-text
:data (get-in last-message [:content :text])}
{:content-type :text ;; Currently mock image is used as placeholder,
:data "Todo: Implement"}))) ;; as last-message don't have image
;; https://github.com/status-im/status-mobile/issues/14625
constants/content-type-image
{:content-type constants/content-type-image
:data [{:source (resources/get-mock-image :photo2)}]}
;; Same for sticker, mock image is used
constants/content-type-sticker
{:content-type constants/content-type-sticker
:data {:source (resources/get-mock-image :sticker)}}
;; Mock Image
constants/content-type-gif
{:content-type constants/content-type-gif
:data {:source (resources/get-mock-image :gif)}}
constants/content-type-audio
{:content-type constants/content-type-audio
:data (datetime/ms-to-duration (:audio-duration-ms last-message))}
constants/content-type-community
(let [community (get communities (:community-id last-message))]
{:content-type constants/content-type-community
:data {:avatar (community-avatar community)
:community-name (:name community)}})
{:content-type constants/content-type-text
:data "Todo: Implement"}))
{:new-notifications? (pos? (:unviewed-messages-count chat))
:notification-indicator (if (pos? (:unviewed-mentions-count chat))
:counter
:unread-dot)
:counter-label (:unviewed-mentions-count chat)})))
(defn one-to-one-chat-card (defn one-to-one-chat-card
[contact names chat id] [contact names chat id communities]
(let [images (:images contact) (let [images (:images contact)
profile-picture (:uri (or (:thumbnail images) (:large images) (first images)))] profile-picture (:uri (or (:thumbnail images) (:large images) (first images)))]
{:title (first names) {:title (first names)
@ -25,26 +71,20 @@
:customization-color (or (:customization-color contact) :primary) :customization-color (or (:customization-color contact) :primary)
:on-close #(re-frame/dispatch [:shell/close-switcher-card id]) :on-close #(re-frame/dispatch [:shell/close-switcher-card id])
:on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 id true]) :on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 id true])
:content (get-card-content chat)})) :content (get-card-content chat communities)}))
(defn private-group-chat-card (defn private-group-chat-card
[chat id] [chat id communities]
{:title (:chat-name chat) {:title (:chat-name chat)
:avatar-params {} :avatar-params {}
:customization-color (or (:customization-color chat) :primary) :customization-color (or (:customization-color chat) :primary)
:on-close #(re-frame/dispatch [:shell/close-switcher-card id]) :on-close #(re-frame/dispatch [:shell/close-switcher-card id])
:on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 id true]) :on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 id true])
:content (get-card-content chat)}) :content (get-card-content chat communities)})
(defn community-card (defn community-card
[community id content] [community id]
(let [images (:images community) (let [profile-picture (community-avatar community)]
profile-picture (if (= id status-constants/status-community-id)
(resources/get-image :status-logo)
(when images
{:uri (:uri (or (:thumbnail images)
(:large images)
(first images)))}))]
{:title (:name community) {:title (:name community)
:avatar-params (if profile-picture :avatar-params (if profile-picture
{:source profile-picture} {:source profile-picture}
@ -53,15 +93,15 @@
:on-close #(re-frame/dispatch [:shell/close-switcher-card id]) :on-close #(re-frame/dispatch [:shell/close-switcher-card id])
:on-press #(re-frame/dispatch [:navigate-to-nav2 :community :on-press #(re-frame/dispatch [:navigate-to-nav2 :community
{:community-id id} true]) {:community-id id} true])
:content (or content :content {:community-info {:type :permission}}}))
{:content-type :community-info
:data {:type :permission}})}))
(defn community-channel-card (defn community-channel-card
[community community-id _ channel-id content] [community community-id channel channel-id]
(merge (merge
(community-card community community-id content) (community-card community community-id)
{:on-press (fn [] {:content {:community-channel {:emoji (:emoji channel)
:channel-name (str "# " (:name channel))}}
:on-press (fn []
(re-frame/dispatch [:navigate-to :community {:community-id community-id}]) (re-frame/dispatch [:navigate-to :community {:community-id community-id}])
(js/setTimeout (js/setTimeout
#(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 channel-id true]) #(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 channel-id true])
@ -78,28 +118,32 @@
(fn [[_ id] _] (fn [[_ id] _]
[(re-frame/subscribe [:contacts/contact-by-identity id]) [(re-frame/subscribe [:contacts/contact-by-identity id])
(re-frame/subscribe [:contacts/contact-two-names-by-identity id]) (re-frame/subscribe [:contacts/contact-two-names-by-identity id])
(re-frame/subscribe [:chats/chat id])]) (re-frame/subscribe [:chats/chat id])
(fn [[contact names chat] [_ id]] (re-frame/subscribe [:communities])])
(one-to-one-chat-card contact names chat id))) (fn [[contact names chat communities] [_ id]]
(one-to-one-chat-card contact names chat id communities)))
(re-frame/reg-sub (re-frame/reg-sub
:shell/private-group-chat-card :shell/private-group-chat-card
(fn [[_ id] _] (fn [[_ id] _]
[(re-frame/subscribe [:chats/chat id])]) [(re-frame/subscribe [:chats/chat id])
(fn [[chat] [_ id]] (re-frame/subscribe [:communities])])
(private-group-chat-card chat id))) (fn [[chat communities] [_ id]]
(private-group-chat-card chat id communities)))
(re-frame/reg-sub (re-frame/reg-sub
:shell/community-card :shell/community-card
(fn [[_ id] _] (fn [[_ id] _]
[(re-frame/subscribe [:communities/community id])]) [(re-frame/subscribe [:communities/community id])])
(fn [[community] [_ id]] (fn [[community] [_ id]]
(community-card community id nil))) (community-card community id)))
(re-frame/reg-sub (re-frame/reg-sub
:shell/community-channel-card :shell/community-channel-card
(fn [[_ community-id channel-id _] _] (fn [[_ channel-id] _]
[(re-frame/subscribe [:communities/community community-id]) [(re-frame/subscribe [:chats/chat channel-id])
(re-frame/subscribe [:chats/chat channel-id])]) (re-frame/subscribe [:communities])])
(fn [[community channel] [_ community-id channel-id content]] (fn [[channel communities] [_ channel-id]]
(community-channel-card community community-id channel channel-id content))) (let [community-id (:community-id channel)
community (get communities (:community-id channel))]
(community-channel-card community community-id channel channel-id))))

View File

@ -1,7 +1,7 @@
(ns status-im2.subs.wallet.wallet-test (ns status-im2.subs.wallet.wallet-test
(:require [cljs.test :refer [deftest is testing]] (:require [cljs.test :refer [deftest is testing]]
[re-frame.db :as rf-db] [re-frame.db :as rf-db]
[status-im.test-helpers :as h] [test-helpers.unit :as h]
[status-im.utils.money :as money] [status-im.utils.money :as money]
[status-im2.subs.wallet.wallet :as wallet] [status-im2.subs.wallet.wallet :as wallet]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))

View File

@ -1,4 +1,4 @@
(ns status-im.test-helpers (ns test-helpers.unit
(:require [clojure.spec.alpha :as s] (:require [clojure.spec.alpha :as s]
[clojure.string :as string] [clojure.string :as string]
[clojure.walk :as walk])) [clojure.walk :as walk]))
@ -34,7 +34,7 @@
Example: Example:
```clojure ```clojure
(require '[status-im.test-helpers :as h]) (require '[test-helpers.unit :as h])
(h/deftest-sub :wallet/sorted-tokens (h/deftest-sub :wallet/sorted-tokens
[sub-name] [sub-name]
@ -60,7 +60,7 @@
"Register log fixture which allows inspecting all calls to `taoensso.timbre/log`. "Register log fixture which allows inspecting all calls to `taoensso.timbre/log`.
Usage: Simply call this macro once per test namespace, and use the Usage: Simply call this macro once per test namespace, and use the
`status-im.test-helpers/logs` atom to deref the collection of all logs for the `test-helpers.unit/logs` atom to deref the collection of all logs for the
test under execution. test under execution.
In Clojure(Script), we can rely on fixtures for each `cljs.deftest`, but not In Clojure(Script), we can rely on fixtures for each `cljs.deftest`, but not
@ -69,8 +69,8 @@
[] []
`(cljs.test/use-fixtures `(cljs.test/use-fixtures
:each :each
{:before status-im.test-helpers/log-fixture-before {:before test-helpers.unit/log-fixture-before
:after status-im.test-helpers/log-fixture-after})) :after test-helpers.unit/log-fixture-after}))
(defmacro run-test-sync (defmacro run-test-sync
"Wrap around `re-frame.test/run-test-sync` to make it work with our aliased "Wrap around `re-frame.test/run-test-sync` to make it work with our aliased

View File

@ -1,11 +1,11 @@
(ns status-im.test-helpers (ns test-helpers.unit
"Utilities for simplifying the process of writing tests and improving test "Utilities for simplifying the process of writing tests and improving test
readability. readability.
Avoid coupling this namespace with particularities of the Status' domain, thus Avoid coupling this namespace with particularities of the Status' domain, thus
prefer to use it for more general purpose concepts, such as the re-frame event prefer to use it for more general purpose concepts, such as the re-frame event
layer." layer."
(:require-macros status-im.test-helpers) (:require-macros test-helpers.unit)
(:require [re-frame.core :as rf] (:require [re-frame.core :as rf]
[re-frame.db :as rf-db] [re-frame.db :as rf-db]
[re-frame.events :as rf-events] [re-frame.events :as rf-events]

View File

@ -1,6 +1,7 @@
(ns utils.datetime (ns utils.datetime
(:require [cljs-time.coerce :as t.coerce] (:require [cljs-time.coerce :as t.coerce]
[cljs-time.core :as t] [cljs-time.core :as t]
[goog.string :as gstring]
[cljs-time.format :as t.format] [cljs-time.format :as t.format]
[clojure.string :as string] [clojure.string :as string]
[i18n.i18n :as i18n] [i18n.i18n :as i18n]
@ -56,6 +57,7 @@
;;;; Date formats ;;;; Date formats
(defn- short-date-format [_] "dd MMM") (defn- short-date-format [_] "dd MMM")
(defn- short-date-format-with-time [_] "dd MMM h:mm a")
(defn- datetime-within-one-week-format (defn- datetime-within-one-week-format
[^js locsym] [^js locsym]
@ -85,6 +87,7 @@
(def date-fmt (get-formatter-fn medium-date-format)) (def date-fmt (get-formatter-fn medium-date-format))
(def time-fmt (get-formatter-fn short-time-format)) (def time-fmt (get-formatter-fn short-time-format))
(def short-date-fmt (get-formatter-fn short-date-format)) (def short-date-fmt (get-formatter-fn short-date-format))
(def short-date-with-time-fmt (get-formatter-fn short-date-format-with-time))
(def datetime-within-one-week-fmt (get-formatter-fn datetime-within-one-week-format)) (def datetime-within-one-week-fmt (get-formatter-fn datetime-within-one-week-format))
;;;; Utilities ;;;; Utilities
@ -141,10 +144,14 @@
(defn timestamp->relative (defn timestamp->relative
[ms] [ms]
(let [datetime (t.coerce/from-long ms)] (let [datetime (-> ms
t.coerce/from-long
(t/plus time-zone-offset))]
(cond (cond
(today? datetime) (today? datetime)
(.format ^js (time-fmt) datetime) (str (string/capitalize (i18n/label :t/datetime-today))
" "
(.format ^js (time-fmt) datetime))
(within-last-n-days? datetime 1) (within-last-n-days? datetime 1)
(str (string/capitalize (i18n/label :t/datetime-yesterday)) (str (string/capitalize (i18n/label :t/datetime-yesterday))
@ -155,7 +162,7 @@
(.format ^js (datetime-within-one-week-fmt) datetime) (.format ^js (datetime-within-one-week-fmt) datetime)
(current-year? datetime) (current-year? datetime)
(.format ^js (short-date-fmt) datetime) (.format ^js (short-date-with-time-fmt) datetime)
(previous-years? datetime) (previous-years? datetime)
(.format ^js (date-fmt) datetime)))) (.format ^js (date-fmt) datetime))))
@ -250,3 +257,9 @@
(defn to-ms (defn to-ms
[sec] [sec]
(* 1000 sec)) (* 1000 sec))
(defn ms-to-duration
"milisecods to mm:ss format"
[ms]
(let [sec (quot ms 1000)]
(gstring/format "%02d:%02d" (quot sec 60) (mod sec 60))))

View File

@ -152,9 +152,9 @@
(is (= "Jan 1, 1973" (datetime/timestamp->relative 94694400000)))) (is (= "Jan 1, 1973" (datetime/timestamp->relative 94694400000))))
(testing "formats 7 days ago or older, but in the current year" (testing "formats 7 days ago or older, but in the current year"
(is (= "03 Mar" (datetime/timestamp->relative 163091745000))) (is (= "03 Mar 3:15 PM" (datetime/timestamp->relative 163091745000)))
(is (= "02 Mar" (datetime/timestamp->relative 163004400000))) (is (= "02 Mar 3:00 PM" (datetime/timestamp->relative 163004400000)))
(is (= "01 Jan" (datetime/timestamp->relative 157820400000)))) (is (= "01 Jan 3:00 PM" (datetime/timestamp->relative 157820400000))))
(testing "formats dates within the last 6 days" (testing "formats dates within the last 6 days"
(is (= "Sat 3:15 PM" (datetime/timestamp->relative 163523745000))) (is (= "Sat 3:15 PM" (datetime/timestamp->relative 163523745000)))
@ -168,9 +168,9 @@
(is (= "Yesterday 11:59 PM" (datetime/timestamp->relative 163641599000)))) (is (= "Yesterday 11:59 PM" (datetime/timestamp->relative 163641599000))))
(testing "formats today, at various timestamps" (testing "formats today, at various timestamps"
(is (= "3:15 PM" (datetime/timestamp->relative 163696545000))) (is (= "Today 3:15 PM" (datetime/timestamp->relative 163696545000)))
(is (= "12:00 PM" (datetime/timestamp->relative 163684800000))) (is (= "Today 12:00 PM" (datetime/timestamp->relative 163684800000)))
(is (= "12:00 AM" (datetime/timestamp->relative 163641600000)))))) (is (= "Today 12:00 AM" (datetime/timestamp->relative 163641600000))))))
#_((deftest day-relative-before-yesterday-force-24H-test #_((deftest day-relative-before-yesterday-force-24H-test
(with-redefs [t/*ms-fn* (constantly epoch-plus-3d) (with-redefs [t/*ms-fn* (constantly epoch-plus-3d)

View File

@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>", "_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im", "owner": "status-im",
"repo": "status-go", "repo": "status-go",
"version": "v0.117.1a", "version": "v0.117.3",
"commit-sha1": "b4bdfd3df6cf5fb91ab2d0e9f3b38e8d1b9703e5", "commit-sha1": "d40290a649133eb7b7f450b5c5b74eec28ba232d",
"src-sha256": "1s50q53p1j5ysnbj88c52zb2rh1ms21pnf6bjc7wja8gzcpih2wl" "src-sha256": "140j0gbp8nxzncya9mcpvlxnax5ajfl8c32ih20b29n8j525gw66"
} }

View File

@ -1264,27 +1264,20 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
self.chat_1.set_reaction(message_from_sender) self.chat_1.set_reaction(message_from_sender)
message_sender = self.chat_1.chat_element_by_text(message_from_sender) message_sender = self.chat_1.chat_element_by_text(message_from_sender)
if message_sender.emojis_below_message() != 1: message_sender.emojis_below_message().wait_for_element_text(1)
self.errors.append("Counter of reaction is not updated on your own message!")
self.device_2.just_fyi("Receiver sets own emoji and verifies counter on received message in 1-1 chat") self.device_2.just_fyi("Receiver sets own emoji and verifies counter on received message in 1-1 chat")
message_receiver = self.chat_2.chat_element_by_text(message_from_sender) message_receiver = self.chat_2.chat_element_by_text(message_from_sender)
if message_receiver.emojis_below_message() != 1: message_receiver.emojis_below_message().wait_for_element_text(1, 90)
self.errors.append("Counter of reaction is not updated on received message!")
self.chat_2.set_reaction(message_from_sender) self.chat_2.set_reaction(message_from_sender)
for counter in message_sender.emojis_below_message(), message_receiver.emojis_below_message():
if counter != 2:
self.errors.append('Counter is not updated after setting emoji from receiver!')
self.device_2.just_fyi("Receiver pick the same emoji and verify that counter will decrease for both users") self.device_2.just_fyi("Receiver pick the same emoji and verify that counter will decrease for both users")
self.chat_2.set_reaction(message_from_sender) self.chat_2.set_reaction(message_from_sender)
for counter in message_sender.emojis_below_message(), message_receiver.emojis_below_message(): message_sender.emojis_below_message().wait_for_element_text(1)
if counter != 1: message_receiver.emojis_below_message().wait_for_element_text(1, 90)
self.errors.append('Counter is not decreased after re-tapping emoji from receiver!')
self.errors.verify_no_errors() self.errors.verify_no_errors()
@marks.testrail_id(702731) @marks.testrail_id(702731)
@marks.xfail(reason="blocked by #14672")
def test_1_1_chat_pin_messages(self): def test_1_1_chat_pin_messages(self):
self.home_1.just_fyi("Check that Device1 can pin own message in 1-1 chat") self.home_1.just_fyi("Check that Device1 can pin own message in 1-1 chat")
self.chat_1.send_message(self.message_1) self.chat_1.send_message(self.message_1)
@ -1319,64 +1312,67 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
self.errors.append( self.errors.append(
"Message '%s' is missed on Pinned messages list for user %s" % (message, chat_number + 1) "Message '%s' is missed on Pinned messages list for user %s" % (message, chat_number + 1)
) )
chat.click_system_back_button() # workaround for 14672
chat.tap_by_coordinates(500, 100)
self.home_1.just_fyi("Check that Device1 can not pin more than 3 messages and 'Unpin' dialog appears") # Part of the test is blocked by #14637
self.chat_1.send_message(self.message_3) # chat.click_system_back_button()
self.chat_1.send_message(self.message_4) #
self.chat_1.pin_message(self.message_3, 'pin-to-chat') # self.home_1.just_fyi("Check that Device1 can not pin more than 3 messages and 'Unpin' dialog appears")
self.chat_1.pin_message(self.message_4, 'pin-to-chat') # self.chat_1.send_message(self.message_3)
if self.chat_1.pin_limit_popover.is_element_displayed(30): # self.chat_1.send_message(self.message_4)
self.chat_1.view_pinned_messages_button.click() # self.chat_1.pin_message(self.message_3, 'pin-to-chat')
self.chat_1.pinned_messages_list.message_element_by_text( # self.chat_1.pin_message(self.message_4, 'pin-to-chat')
self.message_2).click_inside_element_by_coordinate() # if self.chat_1.pin_limit_popover.is_element_displayed(30):
self.home_1.just_fyi("Unpin one message so that another could be pinned") # self.chat_1.view_pinned_messages_button.click()
self.chat_1.element_by_translation_id('unpin-from-chat').double_click() # self.chat_1.pinned_messages_list.message_element_by_text(
self.chat_1.chat_element_by_text(self.message_4).click() # self.message_2).click_inside_element_by_coordinate()
self.chat_1.pin_message(self.message_4, 'pin-to-chat') # self.home_1.just_fyi("Unpin one message so that another could be pinned")
if not (self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed(30) and # self.chat_1.element_by_translation_id('unpin-from-chat').double_click()
self.chat_2.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed(30)): # self.chat_1.chat_element_by_text(self.message_4).click()
self.errors.append("Message 4 is not pinned in chat after unpinning previous one") # self.chat_1.pin_message(self.message_4, 'pin-to-chat')
else: # if not (self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed(30) and
self.errors.append("Can pin more than 3 messages in chat") # self.chat_2.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed(30)):
# self.errors.append("Message 4 is not pinned in chat after unpinning previous one")
self.home_1.just_fyi("Check pinned messages are visible in Pinned panel for both users") # else:
for chat_number, chat in enumerate([self.chat_1, self.chat_2]): # self.errors.append("Can pin more than 3 messages in chat")
count = chat.pinned_messages_count.text #
if count != '3': # self.home_1.just_fyi("Check pinned messages are visible in Pinned panel for both users")
self.errors.append("Pinned messages count is not 3 for user %s" % (chat_number + 1)) # for chat_number, chat in enumerate([self.chat_1, self.chat_2]):
# count = chat.pinned_messages_count.text
self.home_1.just_fyi("Unpin one message and check it's unpinned for another user") # if count != '3':
self.chat_2.chat_element_by_text(self.message_4).long_press_element() # self.errors.append("Pinned messages count is not 3 for user %s" % (chat_number + 1))
self.chat_2.element_by_translation_id("unpin-from-chat").click() #
self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.wait_for_invisibility_of_element() # self.home_1.just_fyi("Unpin one message and check it's unpinned for another user")
if self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed(): # self.chat_2.chat_element_by_text(self.message_4).long_press_element()
self.errors.append("Message_4 is not unpinned!") # self.chat_2.element_by_translation_id("unpin-from-chat").click()
# self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.wait_for_invisibility_of_element()
for chat_number, chat in enumerate([self.chat_1, self.chat_2]): # if self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed():
count = chat.pinned_messages_count.text # self.errors.append("Message_4 is not unpinned!")
if count != '2': #
self.errors.append( # for chat_number, chat in enumerate([self.chat_1, self.chat_2]):
"Pinned messages count is not 2 after unpinning the last pinned message for user %s" % ( # count = chat.pinned_messages_count.text
chat_number + 1) # if count != '2':
) # self.errors.append(
# workaround for 14672 # "Pinned messages count is not 2 after unpinning the last pinned message for user %s" % (
self.chat_1.tap_by_coordinates(500, 100) # chat_number + 1)
# )
self.errors.verify_no_errors() self.errors.verify_no_errors()
@marks.testrail_id(702745) @marks.testrail_id(702745)
@marks.xfail(reason="On profile picture failed due to #14718")
def test_1_1_chat_non_latin_messages_stack_update_profile_photo(self): def test_1_1_chat_non_latin_messages_stack_update_profile_photo(self):
# self.home_1.click_system_back_button_until_element_is_shown() self.home_1.click_system_back_button_until_element_is_shown()
self.home_1.browser_tab.click() # temp, until profile is on browser tab self.home_1.browser_tab.click() # temp, until profile is on browser tab
self.profile_1.edit_profile_picture('sauce_logo.png') self.profile_1.edit_profile_picture('sauce_logo.png')
self.profile_1.chats_tab.click() self.profile_1.chats_tab.click()
self.chat_2.just_fyi("Send messages with non-latin symbols") self.chat_2.just_fyi("Send messages with non-latin symbols")
messages = ['hello', '¿Cómo estás tu año?', 'ё, доброго вечерочка', '® æ ç ♥']
[self.chat_2.send_message(message) for message in messages]
if not self.chat_1.chat_message_input.is_element_displayed(): if not self.chat_1.chat_message_input.is_element_displayed():
self.chat_1.click_system_back_button_until_element_is_shown() self.chat_1.click_system_back_button_until_element_is_shown()
self.home_1.get_chat(self.default_username_2).click() self.home_1.get_chat(self.default_username_2).click()
self.chat_1.send_message("workaround for 14637")
messages = ['hello', '¿Cómo estás tu año?', 'ё, доброго вечерочка', '® æ ç ♥']
[self.chat_2.send_message(message) for message in messages]
for message in messages: for message in messages:
if not self.chat_1.chat_element_by_text(message).is_element_displayed(): if not self.chat_1.chat_element_by_text(message).is_element_displayed():
self.errors.append("Message with test '%s' was not received" % message) self.errors.append("Message with test '%s' was not received" % message)

View File

@ -267,6 +267,7 @@ class TestGroupChatMultipleDeviceMergedNewUI(MultipleSharedDeviceTestCase):
if not self.homes[0].element_by_text(message).is_element_displayed(30): if not self.homes[0].element_by_text(message).is_element_displayed(30):
self.errors.append('%s PN was not fetched from offline' % message) self.errors.append('%s PN was not fetched from offline' % message)
self.homes[0].click_system_back_button() self.homes[0].click_system_back_button()
self.homes[0].chats_tab.click()
self.homes[0].get_chat(chat_name).click() self.homes[0].get_chat(chat_name).click()
self.homes[0].just_fyi("check that messages are shown for every member") self.homes[0].just_fyi("check that messages are shown for every member")
@ -277,7 +278,7 @@ class TestGroupChatMultipleDeviceMergedNewUI(MultipleSharedDeviceTestCase):
self.errors.verify_no_errors() self.errors.verify_no_errors()
@pytest.mark.xdist_group(name="three_2") @pytest.mark.xdist_group(name="two_2")
@marks.new_ui_critical @marks.new_ui_critical
class TestGroupChatMediumMultipleDeviceNewUI(MultipleSharedDeviceTestCase): class TestGroupChatMediumMultipleDeviceNewUI(MultipleSharedDeviceTestCase):

View File

@ -514,7 +514,7 @@ class TestPublicChatBrowserOneDeviceMerged(MultipleSharedDeviceTestCase):
self.errors.verify_no_errors() self.errors.verify_no_errors()
@pytest.mark.xdist_group(name="three_1") @pytest.mark.xdist_group(name="one_1")
@marks.new_ui_critical @marks.new_ui_critical
class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase): class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):

View File

@ -182,7 +182,7 @@ class TestActivityCenterMultipleDeviceMedium(MultipleSharedDeviceTestCase):
self.errors.verify_no_errors() self.errors.verify_no_errors()
@pytest.mark.xdist_group(name="four_2") @pytest.mark.xdist_group(name="two_2")
@marks.new_ui_critical @marks.new_ui_critical
class TestActivityCenterMultipleDevicePR(MultipleSharedDeviceTestCase): class TestActivityCenterMultipleDevicePR(MultipleSharedDeviceTestCase):

View File

@ -335,7 +335,7 @@ class TestDeeplinkChatProfileOneDevice(MultipleSharedDeviceTestCase):
self.errors.verify_no_errors() self.errors.verify_no_errors()
@pytest.mark.xdist_group(name="two_1") @pytest.mark.xdist_group(name="one_1")
@marks.new_ui_critical @marks.new_ui_critical
class TestDeeplinkOneDeviceNewUI(MultipleSharedDeviceTestCase): class TestDeeplinkOneDeviceNewUI(MultipleSharedDeviceTestCase):

View File

@ -148,10 +148,13 @@ class BaseElement(object):
counter = 0 counter = 0
self.driver.info("Wait for text element `%s` to be equal to `%s`" % (self.name, text)) self.driver.info("Wait for text element `%s` to be equal to `%s`" % (self.name, text))
while True: while True:
text_element = self.find_element().text
if isinstance(text, int):
text_element = int(text_element.strip())
if counter >= wait_time: if counter >= wait_time:
self.driver.fail(message if message else "`%s` is not equal to expected `%s` in %s sec" % ( self.driver.fail(message if message else "`%s` is not equal to expected `%s` in %s sec" % (
self.find_element().text, text, wait_time)) text_element, text, wait_time))
elif self.find_element().text != text: elif text_element != text:
counter += 10 counter += 10
time.sleep(10) time.sleep(10)
else: else:

View File

@ -748,7 +748,7 @@ class BaseView(object):
def tap_by_coordinates(self, x, y): def tap_by_coordinates(self, x, y):
action = TouchAction(self.driver) action = TouchAction(self.driver)
action.press(None, x, y).perform() action.press(None, x, y).release().perform()
# Method-helper # Method-helper
def write_page_source_to_file(self, full_path_to_file): def write_page_source_to_file(self, full_path_to_file):

Some files were not shown because too many files have changed in this diff Show More