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
status-im.utils.views/letsubs clojure.core/let
reagent.core/with-let clojure.core/let
status-im.utils.fx/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.previews.preview/list-comp clojure.core/for
status-im.utils.styles/def clojure.core/def
status-im.utils.styles/defn clojure.core/defn
status-im.test-helpers/deftest-sub clojure.core/defn
taoensso.tufte/defnp clojure.core/defn}
{:lint-as {status-im.utils.views/defview clojure.core/defn
status-im.utils.views/letsubs clojure.core/let
reagent.core/with-let clojure.core/let
status-im.utils.fx/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.previews.preview/list-comp clojure.core/for
status-im.utils.styles/def clojure.core/def
status-im.utils.styles/defn clojure.core/defn
test-helpers.unit/deftest-sub clojure.core/defn
taoensso.tufte/defnp clojure.core/defn}
:linters {:consistent-alias {:level :error
:aliases {clojure.string string
clojure.set set
clojure.walk walk
taoensso.timbre log}}
:invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]}
;; 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]))))))
;; good
(require '[status-im.test-helpers :as h])
(require '[test-helpers.unit :as h])
(re-frame/reg-sub
:user/recipes

View File

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

View File

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

View File

@ -339,6 +339,38 @@ RCT_EXPORT_METHOD(inputConnectionStringForBootstrapping:(NSString *)cs
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
callback:(RCTResponseSenderBlock)callback) {
#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
:target :node-test
;; 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
status-im.test-runner/main
;; set :ui-driven to true to let shadow-cljs inject node-repl

View File

@ -1,78 +1,14 @@
(ns i18n.i18n
(:require ["i18n-js" :as i18n]
[clojure.string :as string]
[status-im.goog.i18n :as goog.i18n]
[react-native.languages :as react-native-languages]))
[status-im.goog.i18n :as goog.i18n]))
(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})
(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
[]
(defn setup
[default-device-language translations-by-locale]
(set! (.-fallbacks i18n) true)
(set! (.-defaultSeparator i18n) "/")
(set! (.-locale i18n) (name default-device-language))
(set! (.-translations i18n) (clj->js translations-by-locale)))
(set! (.-locale i18n) default-device-language)
(set! (.-translations i18n) translations-by-locale))
(defn get-translations
[]
@ -134,12 +70,3 @@
(.-locale i18n))
(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
;; display image
[fast-image/fast-image
{:source {:uri profile-picture}
{:source profile-picture
:style (container-styling inner-dimensions outer-dimensions)}]
;; else display initials
[container inner-dimensions outer-dimensions

View File

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

View File

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

View File

@ -52,6 +52,7 @@
quo2.components.tabs.tabs
quo2.components.tags.context-tags
quo2.components.tags.status-tags
quo2.components.profile.profile-card.view
quo2.components.tags.tags))
(def toast quo2.components.notifications.toast/toast)
@ -74,6 +75,8 @@
(def user-avatar-tag quo2.components.tags.context-tags/user-avatar-tag)
(def context-tag quo2.components.tags.context-tags/context-tag)
(def group-avatar-tag quo2.components.tags.context-tags/group-avatar-tag)
(def audio-tag quo2.components.tags.context-tags/audio-tag)
(def community-tag quo2.components.tags.context-tags/community-tag)
(def tabs quo2.components.tabs.tabs/tabs)
(def scrollable-tabs quo2.components.tabs.tabs/scrollable-tabs)
(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 count-down-circle quo2.components.notifications.count-down-circle/circle-timer)
;;;; PROFILE
(def profile-card quo2.components.profile.profile-card.view/profile-card)
;;;; SETTINGS
(def privacy-option quo2.components.settings.privacy-option/card)
(def account quo2.components.settings.accounts.view/account)

View File

@ -184,9 +184,11 @@
(defn build-image-messages
[{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]}]]
{:chat-id chat-id
:album-id album-id
:content-type constants/content-type-image
:image-path (utils/safe-replace uri #"file://" "")
:text (i18n/label :t/update-to-see-image {"locale" "en"})})

View File

@ -4,9 +4,9 @@
[status-im.constants :as constants]
[status-im.data-store.chats :as data-store.chats]
[status-im.data-store.messages :as data-store.messages]
[utils.re-frame :as rf]
[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
[^js cursor]
@ -101,6 +101,17 @@
:on-success #(re-frame/dispatch
[::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
"Loads more messages for current chat"
{:events [::messages-loaded]}
@ -131,8 +142,8 @@
current-clock-value (get-in db
[:pagination-info chat-id
:cursor-clock-value])
clock-value (when cursor
(cursor->clock-value cursor))]
clock-value (when cursor (cursor->clock-value cursor))
new-messages (map mark-album new-messages)]
{:dispatch [:chat/add-senders-to-chat-users (vals senders)]
:db (-> db
(update-in [:pagination-info chat-id :cursor-clock-value]

View File

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

View File

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

View File

@ -205,9 +205,26 @@
(def ^:const community-member-role-manage-users 2)
(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.
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 "
"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
(:require [clojure.set :as clojure.set]
(:require [clojure.set :as set]
[clojure.string :as string]
[status-im.constants :as constants]
[status-im.ethereum.core :as ethereum]
@ -68,8 +68,8 @@
(let [current-contact (some->
current-account
(select-keys [:name :preferred-name :public-key :identicon :images])
(clojure.set/rename-keys {:name :alias
:preferred-name :name}))
(set/rename-keys {:name :alias
:preferred-name :name}))
all-contacts (cond-> contacts
current-contact
(assoc public-key current-contact))]

View File

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

View File

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

View File

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

View File

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

View File

@ -1,24 +1,24 @@
(ns status-im.data-store.reactions
(:require [clojure.set :as clojure.set]))
(:require [clojure.set :as set]))
(defn ->rpc
[message]
(-> message
(clojure.set/rename-keys {:message-id :messageId
:emoji-id :emojiId
:chat-id :localChatId
:message-type :messageType
:emoji-reaction-id :id})))
(set/rename-keys {:message-id :messageId
:emoji-id :emojiId
:chat-id :localChatId
:message-type :messageType
:emoji-reaction-id :id})))
(defn <-rpc
[message]
(-> message
(dissoc :chat_id)
(clojure.set/rename-keys {:messageId :message-id
:localChatId :chat-id
:emojiId :emoji-id
:messageType :message-type
:id :emoji-reaction-id})))
(set/rename-keys {:messageId :message-id
:localChatId :chat-id
:emojiId :emoji-id
:messageType :message-type
:id :emoji-reaction-id})))
(defn reactions-by-chat-id-rpc
[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
(:require [clojure.set :as clojure.set]
(:require [clojure.set :as set]
[re-frame.core :as re-frame]
[utils.re-frame :as rf]
[taoensso.timbre :as log]))
(defn <-rpc
[visibility-status-update]
(clojure.set/rename-keys visibility-status-update
{:publicKey :public-key
:statusType :status-type}))
(set/rename-keys visibility-status-update
{:publicKey :public-key
:statusType :status-type}))
(defn <-rpc-settings
[settings]
(-> settings
(clojure.set/rename-keys
(set/rename-keys
{:current-user-status :current-user-visibility-status})
(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.invitations :as data-store.invitations]
[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.ethereum.core :as ethereum]
[status-im.ethereum.eip55 :as eip55]
@ -473,7 +474,8 @@
(multiaccounts/get-profile-picture)
(multiaccounts/switch-preview-privacy-mode-flag)
(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
[auth-method save-password?]

View File

@ -5,7 +5,8 @@
[status-im.utils.platform :as platform]
[status-im.utils.react-native :as react-native-utils]
[status-im.utils.types :as types]
[taoensso.timbre :as log]))
[taoensso.timbre :as log]
[status-im.constants :as constants]))
(defn status
[]
@ -278,6 +279,55 @@
:connection-string connection-string})
(.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
"used for keycard"
[data callback]

View File

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

View File

@ -63,7 +63,8 @@
:sticker (js/require "../resources/images/mock/sticker.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-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
[k]

View File

@ -199,6 +199,12 @@
{:type :wallet-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
[chain chats uri cb]
(let [{:keys [handler route-params query-params]} (match-uri uri)]
@ -229,7 +235,8 @@
(cb {:type handler :community-id (:community-id route-params)})
(= 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)
(cb {:type handler :chat-id (:chat-id route-params)})

View File

@ -1,5 +1,5 @@
(ns status-im.signing.core
(:require [clojure.set :as clojure.set]
(:require [clojure.set :as set]
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
@ -596,4 +596,4 @@
(sign cofx
{:tx-obj (-> tx
(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.env :as env]
[utils.re-frame :as rf]
[i18n.i18n :as i18n]))
(i18n/init)
status-im2.setup.i18n-resources))
(defonce repl? (atom false))

View File

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

View File

@ -4,10 +4,11 @@
[re-frame.core :as re-frame]
[status-im.add-new.db :as db]
[status-im.chat.models :as chat.models]
[i18n.i18n :as i18n]
[status-im.react-native.resources :as resources]
[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]))
(defn- start-chat
@ -73,8 +74,8 @@
(defn get-language-topic
[]
(let [lang (subs (name i18n/default-device-language) 0 2)
lang3 (subs (name i18n/default-device-language) 0 3)
(let [lang (subs (name i18n-resources/default-device-language) 0 2)
lang3 (subs (name i18n-resources/default-device-language) 0 3)
lang-name (or (get lang-names lang3) (get lang-names lang))]
(when-not (= lang "en")
(or lang-name (str "status-" lang)))))

View File

@ -1,5 +1,5 @@
(ns status-im.ui.screens.communities.reorder-categories
(:require [clojure.set :as clojure.set]
(:require [clojure.set :as set]
[clojure.string :as string]
[clojure.walk :as walk]
[quo.core :as quo]
@ -48,7 +48,7 @@
[{:keys [id community-id] :as home-item} is-active? drag]
(let [chat-id (string/replace id community-id "")
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
{:accessibility-label :chat-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.transactions.views :as wallet-transactions]
[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]))
(defn right-button-options
@ -207,10 +206,6 @@
:options {:topBar {:visible false}}
:component pin-messages/pinned-messages}
{:name :photo-selector
:options {:topBar {:visible false}}
:component photo-selector/photo-selector}
{:name :group-chat-profile
;;TODO animated-header
:options {:topBar {:visible false}}

View File

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

View File

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

View File

@ -5,7 +5,7 @@
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[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]))
;; TODO (flexsurfer) this should be an in-app notification component in quo2
@ -56,6 +56,6 @@
:right 16}}
[quo/icon :i/close
{:color (colors/theme-colors colors/white colors/neutral-100)
:size 8}]]]))])
:size 12}]]]))])

View File

@ -14,7 +14,8 @@
[utils.re-frame :as rf]
[status-im.wallet.choose-recipient.core :as choose-recipient]
[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
;; dispatched 7 times for the same link
@ -76,7 +77,21 @@
(rf/defn handle-community
[cofx {:keys [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
[cofx {:keys [chat-id]}]
@ -145,6 +160,7 @@
:private-chat (handle-private-chat cofx data)
:community-requests (handle-community-requests cofx data)
:community (handle-community cofx data)
:desktop-community (handle-desktop-community cofx data)
:community-chat (handle-community-chat cofx data)
:contact (handle-view-profile cofx data)
:browser (handle-browse cofx data)

View File

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

View File

@ -1,6 +1,6 @@
(ns status-im.wallet.core
(:require
[clojure.set :as clojure.set]
[clojure.set :as set]
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.async-storage.core :as async-storage]
@ -360,7 +360,7 @@
(rf/merge cofx
(multiaccounts.update/multiaccount-update
: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)
(prices/update-prices))))

View File

@ -26,7 +26,7 @@
[quo/text {:style {:margin-left 10}} extra-text]]))
(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)]
(fn []
(let [{:keys [group-chat chat-id public-key color name]} context
@ -38,7 +38,9 @@
id]))
photo-path (when-not (empty? (:images contact))
(rf/sub [:chats/photo-path id]))]
[rn/view {:style {:margin-horizontal 20}}
[rn/view
{:style {:margin-horizontal 20}
:accessibility-label accessibility-label}
[quo/text
{:weight :semi-bold
:size :heading-1} title]

View File

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

View File

@ -2,7 +2,7 @@
(:require [cljs.test :refer [deftest is testing]]
[status-im.constants :as constants]
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.notification-types :as types]
[utils.re-frame :as rf]))

View File

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

View File

@ -9,12 +9,19 @@
[status-im2.contexts.activity-center.notification.mentions.style :as style]
[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
[message]
(let [parsed-text (get-in message [:content :parsed-text])
parsed-text-children (:children (first parsed-text))]
(into [quo/text
{:number-of-lines 2
{:number-of-lines 1
:style style/tag-text
:accessibility-label :activity-message-body
:size :paragraph-1}]
@ -29,25 +36,25 @@
parsed-text-children))))
(defn view
[{:keys [author chat-name chat-id message] :as notification}]
[rn/touchable-without-feedback
{:on-press (fn []
(rf/dispatch [:hide-popover])
(rf/dispatch [:chat.ui/navigate-to-chat chat-id]))}
[quo/activity-log
{:title (i18n/label :t/mention)
:icon :i/mention
:timestamp (datetime/timestamp->relative (:timestamp notification))
:unread? (not (:read notification))
:context [[common/user-avatar-tag author]
[quo/text {:style style/tag-text} (string/lower-case (i18n/label :t/on))]
;; TODO (@smohamedjavid): The `group-avatar-tag` component
;; does NOT support displaying channel name along with community/chat name.
;; Need to update the component to support it.
[quo/group-avatar-tag chat-name
{:size :small
:override-theme :dark
:color colors/primary-50
:style style/tag
:text-style style/tag-text}]]
:message {:body (message-body message)}}]])
[{: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/mention)
:icon :i/mention
:timestamp (datetime/timestamp->relative timestamp)
:unread? (not read)
:context [[common/user-avatar-tag author]
[quo/text {:style style/tag-text} (string/lower-case (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 (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
contact-verification]
[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]
[utils.re-frame :as rf]))
@ -107,6 +108,9 @@
types/mention
[mentions/view notification]
types/reply
[reply/view notification]
nil)])
(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.status.view :as status]
[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]
[utils.re-frame :as rf]
[status-im.ui2.screens.chat.messages.message :as old-message]
@ -94,6 +95,8 @@
constants/content-type-audio [not-implemented/not-implemented
[old-message/audio message-data]]
constants/content-type-album [album/album-message message-data]
[not-implemented/not-implemented [content.unknown/unknown-content message-data]])
[status/status message-data]]]]]))

View File

@ -179,7 +179,9 @@
:on-layout on-messages-view-layout})]
[quo/floating-shell-button
(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)}}
(when @show-floating-scroll-down-button
{: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]
[react-native.platform :as platform]))
@ -37,6 +37,11 @@
:margin-left 20
:margin-bottom 24})
(def title-container
{:flex-direction :row
:position :absolute
:align-self :center})
(defn chevron-container
[]
{:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
@ -68,3 +73,4 @@
:border-radius 8
:top 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]
[quo.components.safe-area :as safe-area]
[quo2.components.notifications.info-count :as info-count]
[quo2.core :as quo2]
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]
[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]
[quo.react]
[utils.re-frame :as rf]))
(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
[chat-id selected-images]
[:f>
(fn []
(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
{:colors [:black :transparent]
:start {:x 0 :y 1}
:end {:x 0 :y 0}
:style (style/gradient-container safe-area)}
[quo2/button
{:style {:align-self :stretch
:margin-horizontal 20}
:on-press #(do
(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]))}
[quo/button
{:style {:align-self :stretch
:margin-horizontal 20}
:on-press #(on-press-confirm-selection chat-id)
:accessibility-label :confirm-selection}
(i18n/label :t/confirm-selection)]])))])
(defn clear-button
[]
(when (pos? (count @selected))
(when (seq @selected)
[rn/touchable-opacity
{:on-press #(reset! selected [])
:style (style/clear-container)}
[quo2/text {:weight :medium} (i18n/label :t/clear)]]))
{:on-press #(reset! selected [])
:style (style/clear-container)
:accessibility-label :clear}
[quo/text {:weight :medium} (i18n/label :t/clear)]]))
(defn remove-selected
[coll item]
(vec (remove #(= % item) coll)))
(defn image
[item index _ {:keys [window-width]}]
[rn/touchable-opacity
{:active-opacity 1
:on-press (fn []
(if (some #{item} @selected)
(reset! selected (vec (remove #(= % item) @selected)))
(swap! selected conj item)))}
{:active-opacity 1
:on-press (fn []
(if (some #{item} @selected)
(swap! selected remove-selected item)
(swap! selected conj item)))
:accessibility-label (str "image-" index)}
[rn/image
{:source {:uri item}
:style (style/image window-width index)}]
(when (some #{item} @selected)
[rn/view {:style (style/overlay window-width)}])
(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))])])
(defn photo-selector
[chat-id]
(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
(reset! selected (vec selected-images)))
[:f>
@ -78,17 +91,15 @@
[rn/touchable-opacity
{:on-press #(js/alert "Camera: not implemented")
: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
{:style {:flex-direction :row
:position :absolute
:align-self :center}}
[quo2/text {:weight :medium} (i18n/label :t/recent)]
{:style style/title-container}
[quo/text {:weight :medium} (i18n/label :t/recent)]
[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]
[rn/flat-list
{:key-fn (fn [item] item)
{:key-fn identity
:render-fn image
:render-data {:window-width window-width}
:data camera-roll-photos
@ -100,3 +111,4 @@
has-next-page?])}]
[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.toast :as toast]
[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.record-audio.record-audio :as record-audio]
[status-im2.contexts.quo-preview.selectors.disclaimer :as disclaimer]
[status-im2.contexts.quo-preview.selectors.filter :as filter]
[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.switcher.switcher-cards :as switcher-cards]
[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.network-amount :as network-amount]
[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]))
(def screens-categories
@ -175,6 +176,9 @@
:posts-and-attachments [{:name :messages-skeleton
:insets {:top false}
:component messages-skeleton/preview-messages-skeleton}]
:profile [{:name :profile-card
:insets {:top false}
:component profile-card/preview-profile-card}]
:reactions [{:name :react
:insets {:top false}
:component react/preview-react}]

View File

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

View File

@ -3,6 +3,7 @@
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.react-native.resources :as resources]
[status-im.multiaccounts.core :as multiaccounts]
[status-im2.contexts.quo-preview.preview :as preview]))
@ -19,6 +20,8 @@
(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=")
(def coinbase-community (resources/get-mock-image :coinbase))
(def main-descriptor
[{:label "Type"
:key :type
@ -30,7 +33,11 @@
{:key :group-avatar
:value "Group avatar"}
{:key :context-tag
:value "Context tag"}]}])
:value "Context tag"}
{:key :audio
:value "Audio"}
{:key :community
:value "Community"}]}])
(def context-tag-descriptor
[{:label "Label"
@ -100,13 +107,17 @@
:public-key
[quo2/public-key-tag {} example-pk]
: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
[]
[rn/view
{:background-color (colors/theme-colors colors/white
colors/neutral-90)
colors/neutral-95)
:flex 1}
[rn/flat-list
{:flex 1

View File

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

View File

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

View File

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

View File

@ -51,9 +51,13 @@
:accessibility-label :shell-placeholder-view})
(def placeholder-image
{:margin-top 186
:width 120
:height 120})
{:margin-top 186
:width 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
{:margin-top 20

View File

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

View File

@ -10,8 +10,7 @@
[status-im2.navigation.state :as state]
[status-im2.navigation.view :as views]
[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)
(defn reg-comp

View File

@ -12,8 +12,7 @@
[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.send.views :as wallet.send.views]
[status-im2.common.toasts.view :as toasts] ;; TODO (14/11/22 flexsurfer) move to status-im2
;; namespace
[status-im2.common.toasts.view :as toasts]
[status-im2.navigation.screens :as screens]
[status-im2.setup.config :as config]
[status-im2.setup.hot-reload :as reloader]))

View File

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

View File

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

View File

@ -1,5 +1,5 @@
(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.wallet.db :as wallet.db]
[status-im2.contexts.activity-center.events :as activity-center]))

View File

@ -2,7 +2,7 @@
(:require [clojure.string :as string]
[quo.theme :as quo.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.native-module.core :as status]
[status-im.utils.keychain.core :as keychain]

View File

@ -1,6 +1,78 @@
(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
(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]]
[clojure.set :as set]
[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
(def labels
(set (keys (js->clj (:en i18n/translations-by-locale)
(set (keys (js->clj (:en i18n-resources/translations-by-locale)
:keywordize-keys
true))))
@ -16,7 +17,7 @@
(defn labels-for-all-locales
[]
(->> i18n/translations-by-locale
(->> i18n-resources/translations-by-locale
(mapcat #(-> % val (js->clj :keywordize-keys true) keys))
set))
@ -1024,14 +1025,14 @@
;; locales
(def locales (set (keys i18n/translations-by-locale)))
(def locales (set (keys i18n-resources/translations-by-locale)))
(spec/def ::locale locales)
(spec/def ::locales (spec/coll-of ::locale :kind set? :into #{}))
(defn locale->labels
[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
[locale]

View File

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

View File

@ -145,6 +145,13 @@
(fn [[inputs 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
:chats/timeline-chat-input-text
:<- [:chats/timeline-chat-input]
@ -283,12 +290,6 @@
(fn [{:keys [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
:chats/timeline-sending-image
:<- [:chats/timeline-chat-input]

View File

@ -102,6 +102,25 @@
(fn [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
:chats/raw-chat-messages-stream
(fn [[_ chat-id] _]
@ -126,7 +145,8 @@
(datetime/timestamp)
chat-type
joined
loading-messages?))))))
loading-messages?)
(albumize-messages))))))
;;we want to keep data unchanged so react doesn't change component when we leave screen
(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
(:require [cljs.test :refer [is testing use-fixtures]]
[re-frame.db :as rf-db]
[status-im.test-helpers :as h]
[test-helpers.unit :as h]
status-im2.subs.communities
[utils.re-frame :as rf]))
@ -135,4 +135,3 @@
{:id "0x1" :name "Civilized monkeys"}
{:id "0x2" :name "Civilized rats"}]
(rf/sub [sub-name])))))

View File

@ -1,6 +1,6 @@
(ns status-im2.subs.multiaccount
(:require [cljs.spec.alpha :as spec]
[clojure.set :as clojure.set]
[clojure.set :as set]
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum]
@ -23,7 +23,7 @@
(some->
current-account
(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))))
(re-frame/reg-sub

View File

@ -1,21 +1,67 @@
(ns status-im2.subs.shell
(:require [re-frame.core :as re-frame]
[status-im.react-native.resources :as resources]
[status-im2.common.constants :as status-constants]))
[utils.datetime :as datetime]
[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
[chat]
[chat communities]
(let [last-message (:last-message chat)]
(case (:content-type last-message)
status-constants/content-type-text
{:content-type :text
:data (get-in last-message [:content :text])}
(merge
(when last-message
(case (:content-type last-message)
(constants/content-type-text
constants/content-type-emoji)
{:content-type constants/content-type-text
:data (get-in last-message [:content :text])}
{:content-type :text
:data "Todo: Implement"})))
;; Currently mock image is used as placeholder,
;; 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
[contact names chat id]
[contact names chat id communities]
(let [images (:images contact)
profile-picture (:uri (or (:thumbnail images) (:large images) (first images)))]
{:title (first names)
@ -25,26 +71,20 @@
:customization-color (or (:customization-color contact) :primary)
:on-close #(re-frame/dispatch [:shell/close-switcher-card id])
: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
[chat id]
[chat id communities]
{:title (:chat-name chat)
:avatar-params {}
:customization-color (or (:customization-color chat) :primary)
:on-close #(re-frame/dispatch [:shell/close-switcher-card id])
: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
[community id content]
(let [images (:images 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)))}))]
[community id]
(let [profile-picture (community-avatar community)]
{:title (:name community)
:avatar-params (if profile-picture
{:source profile-picture}
@ -53,15 +93,15 @@
:on-close #(re-frame/dispatch [:shell/close-switcher-card id])
:on-press #(re-frame/dispatch [:navigate-to-nav2 :community
{:community-id id} true])
:content (or content
{:content-type :community-info
:data {:type :permission}})}))
:content {:community-info {:type :permission}}}))
(defn community-channel-card
[community community-id _ channel-id content]
[community community-id channel channel-id]
(merge
(community-card community community-id content)
{:on-press (fn []
(community-card community community-id)
{:content {:community-channel {:emoji (:emoji channel)
:channel-name (str "# " (:name channel))}}
:on-press (fn []
(re-frame/dispatch [:navigate-to :community {:community-id community-id}])
(js/setTimeout
#(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 channel-id true])
@ -78,28 +118,32 @@
(fn [[_ id] _]
[(re-frame/subscribe [:contacts/contact-by-identity id])
(re-frame/subscribe [:contacts/contact-two-names-by-identity id])
(re-frame/subscribe [:chats/chat id])])
(fn [[contact names chat] [_ id]]
(one-to-one-chat-card contact names chat id)))
(re-frame/subscribe [:chats/chat id])
(re-frame/subscribe [:communities])])
(fn [[contact names chat communities] [_ id]]
(one-to-one-chat-card contact names chat id communities)))
(re-frame/reg-sub
:shell/private-group-chat-card
(fn [[_ id] _]
[(re-frame/subscribe [:chats/chat id])])
(fn [[chat] [_ id]]
(private-group-chat-card chat id)))
[(re-frame/subscribe [:chats/chat id])
(re-frame/subscribe [:communities])])
(fn [[chat communities] [_ id]]
(private-group-chat-card chat id communities)))
(re-frame/reg-sub
:shell/community-card
(fn [[_ id] _]
[(re-frame/subscribe [:communities/community id])])
(fn [[community] [_ id]]
(community-card community id nil)))
(community-card community id)))
(re-frame/reg-sub
:shell/community-channel-card
(fn [[_ community-id channel-id _] _]
[(re-frame/subscribe [:communities/community community-id])
(re-frame/subscribe [:chats/chat channel-id])])
(fn [[community channel] [_ community-id channel-id content]]
(community-channel-card community community-id channel channel-id content)))
(fn [[_ channel-id] _]
[(re-frame/subscribe [:chats/chat channel-id])
(re-frame/subscribe [:communities])])
(fn [[channel communities] [_ channel-id]]
(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
(:require [cljs.test :refer [deftest is testing]]
[re-frame.db :as rf-db]
[status-im.test-helpers :as h]
[test-helpers.unit :as h]
[status-im.utils.money :as money]
[status-im2.subs.wallet.wallet :as wallet]
[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]
[clojure.string :as string]
[clojure.walk :as walk]))
@ -34,7 +34,7 @@
Example:
```clojure
(require '[status-im.test-helpers :as h])
(require '[test-helpers.unit :as h])
(h/deftest-sub :wallet/sorted-tokens
[sub-name]
@ -60,7 +60,7 @@
"Register log fixture which allows inspecting all calls to `taoensso.timbre/log`.
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.
In Clojure(Script), we can rely on fixtures for each `cljs.deftest`, but not
@ -69,8 +69,8 @@
[]
`(cljs.test/use-fixtures
:each
{:before status-im.test-helpers/log-fixture-before
:after status-im.test-helpers/log-fixture-after}))
{:before test-helpers.unit/log-fixture-before
:after test-helpers.unit/log-fixture-after}))
(defmacro run-test-sync
"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
readability.
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
layer."
(:require-macros status-im.test-helpers)
(:require-macros test-helpers.unit)
(:require [re-frame.core :as rf]
[re-frame.db :as rf-db]
[re-frame.events :as rf-events]

View File

@ -1,6 +1,7 @@
(ns utils.datetime
(:require [cljs-time.coerce :as t.coerce]
[cljs-time.core :as t]
[goog.string :as gstring]
[cljs-time.format :as t.format]
[clojure.string :as string]
[i18n.i18n :as i18n]
@ -56,6 +57,7 @@
;;;; Date formats
(defn- short-date-format [_] "dd MMM")
(defn- short-date-format-with-time [_] "dd MMM h:mm a")
(defn- datetime-within-one-week-format
[^js locsym]
@ -85,6 +87,7 @@
(def date-fmt (get-formatter-fn medium-date-format))
(def time-fmt (get-formatter-fn short-time-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))
;;;; Utilities
@ -141,10 +144,14 @@
(defn timestamp->relative
[ms]
(let [datetime (t.coerce/from-long ms)]
(let [datetime (-> ms
t.coerce/from-long
(t/plus time-zone-offset))]
(cond
(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)
(str (string/capitalize (i18n/label :t/datetime-yesterday))
@ -155,7 +162,7 @@
(.format ^js (datetime-within-one-week-fmt) datetime)
(current-year? datetime)
(.format ^js (short-date-fmt) datetime)
(.format ^js (short-date-with-time-fmt) datetime)
(previous-years? datetime)
(.format ^js (date-fmt) datetime))))
@ -250,3 +257,9 @@
(defn to-ms
[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))))
(testing "formats 7 days ago or older, but in the current year"
(is (= "03 Mar" (datetime/timestamp->relative 163091745000)))
(is (= "02 Mar" (datetime/timestamp->relative 163004400000)))
(is (= "01 Jan" (datetime/timestamp->relative 157820400000))))
(is (= "03 Mar 3:15 PM" (datetime/timestamp->relative 163091745000)))
(is (= "02 Mar 3:00 PM" (datetime/timestamp->relative 163004400000)))
(is (= "01 Jan 3:00 PM" (datetime/timestamp->relative 157820400000))))
(testing "formats dates within the last 6 days"
(is (= "Sat 3:15 PM" (datetime/timestamp->relative 163523745000)))
@ -168,9 +168,9 @@
(is (= "Yesterday 11:59 PM" (datetime/timestamp->relative 163641599000))))
(testing "formats today, at various timestamps"
(is (= "3:15 PM" (datetime/timestamp->relative 163696545000)))
(is (= "12:00 PM" (datetime/timestamp->relative 163684800000)))
(is (= "12:00 AM" (datetime/timestamp->relative 163641600000))))))
(is (= "Today 3:15 PM" (datetime/timestamp->relative 163696545000)))
(is (= "Today 12:00 PM" (datetime/timestamp->relative 163684800000)))
(is (= "Today 12:00 AM" (datetime/timestamp->relative 163641600000))))))
#_((deftest day-relative-before-yesterday-force-24H-test
(with-redefs [t/*ms-fn* (constantly epoch-plus-3d)

View File

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

View File

@ -1264,27 +1264,20 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
self.chat_1.set_reaction(message_from_sender)
message_sender = self.chat_1.chat_element_by_text(message_from_sender)
if message_sender.emojis_below_message() != 1:
self.errors.append("Counter of reaction is not updated on your own message!")
message_sender.emojis_below_message().wait_for_element_text(1)
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)
if message_receiver.emojis_below_message() != 1:
self.errors.append("Counter of reaction is not updated on received message!")
message_receiver.emojis_below_message().wait_for_element_text(1, 90)
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.chat_2.set_reaction(message_from_sender)
for counter in message_sender.emojis_below_message(), message_receiver.emojis_below_message():
if counter != 1:
self.errors.append('Counter is not decreased after re-tapping emoji from receiver!')
message_sender.emojis_below_message().wait_for_element_text(1)
message_receiver.emojis_below_message().wait_for_element_text(1, 90)
self.errors.verify_no_errors()
@marks.testrail_id(702731)
@marks.xfail(reason="blocked by #14672")
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.chat_1.send_message(self.message_1)
@ -1319,64 +1312,67 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
self.errors.append(
"Message '%s' is missed on Pinned messages list for user %s" % (message, chat_number + 1)
)
chat.click_system_back_button()
self.home_1.just_fyi("Check that Device1 can not pin more than 3 messages and 'Unpin' dialog appears")
self.chat_1.send_message(self.message_3)
self.chat_1.send_message(self.message_4)
self.chat_1.pin_message(self.message_3, 'pin-to-chat')
self.chat_1.pin_message(self.message_4, 'pin-to-chat')
if self.chat_1.pin_limit_popover.is_element_displayed(30):
self.chat_1.view_pinned_messages_button.click()
self.chat_1.pinned_messages_list.message_element_by_text(
self.message_2).click_inside_element_by_coordinate()
self.home_1.just_fyi("Unpin one message so that another could be pinned")
self.chat_1.element_by_translation_id('unpin-from-chat').double_click()
self.chat_1.chat_element_by_text(self.message_4).click()
self.chat_1.pin_message(self.message_4, 'pin-to-chat')
if not (self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed(30) and
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")
else:
self.errors.append("Can pin more than 3 messages in chat")
self.home_1.just_fyi("Check pinned messages are visible in Pinned panel for both users")
for chat_number, chat in enumerate([self.chat_1, self.chat_2]):
count = chat.pinned_messages_count.text
if count != '3':
self.errors.append("Pinned messages count is not 3 for user %s" % (chat_number + 1))
self.home_1.just_fyi("Unpin one message and check it's unpinned for another user")
self.chat_2.chat_element_by_text(self.message_4).long_press_element()
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()
if self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed():
self.errors.append("Message_4 is not unpinned!")
for chat_number, chat in enumerate([self.chat_1, self.chat_2]):
count = chat.pinned_messages_count.text
if count != '2':
self.errors.append(
"Pinned messages count is not 2 after unpinning the last pinned message for user %s" % (
chat_number + 1)
)
# workaround for 14672
self.chat_1.tap_by_coordinates(500, 100)
# workaround for 14672
chat.tap_by_coordinates(500, 100)
# Part of the test is blocked by #14637
# chat.click_system_back_button()
#
# self.home_1.just_fyi("Check that Device1 can not pin more than 3 messages and 'Unpin' dialog appears")
# self.chat_1.send_message(self.message_3)
# self.chat_1.send_message(self.message_4)
# self.chat_1.pin_message(self.message_3, 'pin-to-chat')
# self.chat_1.pin_message(self.message_4, 'pin-to-chat')
# if self.chat_1.pin_limit_popover.is_element_displayed(30):
# self.chat_1.view_pinned_messages_button.click()
# self.chat_1.pinned_messages_list.message_element_by_text(
# self.message_2).click_inside_element_by_coordinate()
# self.home_1.just_fyi("Unpin one message so that another could be pinned")
# self.chat_1.element_by_translation_id('unpin-from-chat').double_click()
# self.chat_1.chat_element_by_text(self.message_4).click()
# self.chat_1.pin_message(self.message_4, 'pin-to-chat')
# if not (self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed(30) and
# 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")
# else:
# self.errors.append("Can pin more than 3 messages in chat")
#
# self.home_1.just_fyi("Check pinned messages are visible in Pinned panel for both users")
# for chat_number, chat in enumerate([self.chat_1, self.chat_2]):
# count = chat.pinned_messages_count.text
# if count != '3':
# self.errors.append("Pinned messages count is not 3 for user %s" % (chat_number + 1))
#
# self.home_1.just_fyi("Unpin one message and check it's unpinned for another user")
# self.chat_2.chat_element_by_text(self.message_4).long_press_element()
# 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()
# if self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed():
# self.errors.append("Message_4 is not unpinned!")
#
# for chat_number, chat in enumerate([self.chat_1, self.chat_2]):
# count = chat.pinned_messages_count.text
# if count != '2':
# self.errors.append(
# "Pinned messages count is not 2 after unpinning the last pinned message for user %s" % (
# chat_number + 1)
# )
self.errors.verify_no_errors()
@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):
# 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.profile_1.edit_profile_picture('sauce_logo.png')
self.profile_1.chats_tab.click()
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():
self.chat_1.click_system_back_button_until_element_is_shown()
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:
if not self.chat_1.chat_element_by_text(message).is_element_displayed():
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):
self.errors.append('%s PN was not fetched from offline' % message)
self.homes[0].click_system_back_button()
self.homes[0].chats_tab.click()
self.homes[0].get_chat(chat_name).click()
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()
@pytest.mark.xdist_group(name="three_2")
@pytest.mark.xdist_group(name="two_2")
@marks.new_ui_critical
class TestGroupChatMediumMultipleDeviceNewUI(MultipleSharedDeviceTestCase):

View File

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

View File

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

View File

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

View File

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

View File

@ -748,7 +748,7 @@ class BaseView(object):
def tap_by_coordinates(self, x, y):
action = TouchAction(self.driver)
action.press(None, x, y).perform()
action.press(None, x, y).release().perform()
# Method-helper
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