Compare commits

..
Author SHA1 Message Date
Volodymyr Kozieiev 0aede3510d status-im/utils/fx replaced with utils/re-frame to avoid unambiguities 2022-12-23 16:59:17 +00:00
yqrashawn 39e29cfb5a feat: replace clj-fmt with zprint (#14520) 2022-12-20 21:57:49 +08:00
yqrashawn 37909c2d81 feat: toast component (#14376) 2022-12-20 21:52:28 +08:00
yqrashawn 01660765b7 refactor: delete message related code (#14548) 2022-12-20 21:42:50 +08:00
Brian Sztamfater c0c0742687 feat: record audio button
Signed-off-by: Brian Sztamfater <brian@status.im>
2022-12-20 10:01:19 -03:00
Siddarth Kumar b074e9c58e moving security from status-im ns to root utils (#14567)
making a lint a fix

update old decision doc

fixing issues while poorly rebasing

fix incorrect ns imports
2022-12-20 17:56:21 +05:30
Icaro Motta 8cf95cdb30 New make target to run tests via REPL (#14576) 2022-12-20 09:07:00 -03:00
Omar Basem ba41d4b271 Photo Selector 2 (#14487)
* Photo Selector (2)
2022-12-20 07:36:06 +04:00
Guilherme Devincenzi 003b0ffb1e feat: add privacy options component (#14500) 2022-12-19 20:12:58 +01:00
Parvesh Monu dbd61a34a0 Update shell placeholder screen with new designs (#14572) 2022-12-19 14:51:53 +05:30
Yevheniia Berdnyk cbb6571954 e2e: Updated tests 702807 and 702808 2022-12-19 04:02:57 +02:00
Siddarth Kumar 7b72391e60 remove comment blocks which are not useful (#14563)
Adding a series of // characters (i.e., a "comment bar") above every method in an Objective-C file would not provide any real benefit, and would in fact be redundant, as the method list in Xcode already serves as a visual separator for methods in a class.
The #pragma mark directive,  is a more concise and effective way to add separators and group related methods in a class. We should use #pragma mark to create a separator line and add a heading to describe the methods in a particular section.
2022-12-16 22:39:39 +05:30
Icaro Motta 269d13e2f8 Move Activity Center to new app structure (#14554) 2022-12-16 10:45:00 -03:00
Siddarth Kumar fc07fbf787 UI for mobile to mobile local pairing - updated (#14514)
* ui for local pairing

lint-fix

removed un-necessary +

addressing some of the feedback on PR

more feedback + removing feature toggle from ui

getting rid of comments/log messages over here

tidy up logs

fix typos and more i18n stuff

swap % with a named parameter

getting rid of global state + lint-fix

get rid of un-used function

icon guidelines and more kebab case stuff :>

moving stuff to events and utils namespace

:main-icons -> :i :)

address feedback and adhere to guidelines etc

fixed the :t/ qualification

moree feedback :-D

referring status-im.utils.security for now

adding "cs" to constants

make tests pass

re-frame to rf

addressing feedback

moving icons to icons2 & renaming stuff

trying to make this file the way it was before

missed out on updating these references

getting rid of the icons moved to icons2

This reverts commit be8552c0d3daaf7a7333cfeaf304d97c86d50d3e.

fixing mistakes

getting rid of the s

* this rename makes sense to me

* adding an alias to the view

* fixed broken up namespaces
2022-12-16 18:40:56 +05:30
844 changed files with 26138 additions and 30310 deletions
+20 -20
View File
@@ -1,20 +1,20 @@
{: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}
:linters {:consistent-alias {:level :error
:aliases {clojure.string string
taoensso.timbre log}}
:invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]}
;; TODO remove number when this is fixed
;; https://github.com/borkdude/clj-kondo/issues/867
:unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY
number
status-im.test-helpers/restore-app-db]}}}
{: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
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}
:linters {:consistent-alias {:level :error
:aliases {clojure.string string
taoensso.timbre log}}
:invalid-arity {:skip-args [utils.re-frame/defn]}
;; TODO remove number when this is fixed
;; https://github.com/borkdude/clj-kondo/issues/867
:unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY
number
status-im.test-helpers/restore-app-db]}}
:config-in-ns {mocks.js-dependencies {:linters {:clojure-lsp/unused-public-var {:level :off}}}}}
+1
View File
@@ -32,3 +32,4 @@ COMMANDS_ENABLED=1
TWO_MINUTES_SYNCING=1
SWAP_ENABLED=1
STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1
+1
View File
@@ -32,3 +32,4 @@ COMMUNITIES_MANAGEMENT_ENABLED=1
DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1
+1
View File
@@ -34,3 +34,4 @@ DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
ENABLE_QUO_PREVIEW=1
STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1
+9 -1
View File
@@ -309,6 +309,14 @@ test-watch: ##@ Watch tests and re-run no changes to cljs files
yarn install
nodemon --exec 'yarn shadow-cljs compile mocks && yarn shadow-cljs compile test && node --require ./test-resources/override.js target/test/test.js' -e cljs
test-watch-for-repl: export TARGET := clojure
test-watch-for-repl: ##@ Watch tests and support REPL connections
yarn install
rm -f target/test/test.js
concurrently --kill-others --prefix-colors 'auto' --names 'build,repl' \
'yarn shadow-cljs compile mocks && yarn shadow-cljs watch test --verbose' \
'until [ -f ./target/test/test.js ] ; do sleep 1 ; done ; node --require ./test-resources/override.js ./target/test/test.js --repl'
test: export TARGET := clojure
test: ##@test Run tests once in NodeJS
# Here we create the gyp bindings for nodejs
@@ -323,7 +331,7 @@ run-visual-test-ios: XCODE_DERIVED_DATA := $(HOME)/Library/Developer/Xcode/Deriv
run-visual-test-ios: APPLICATION_NAME := StatusIm-brfnruzfrkkycpbndmdoeyrigthc
run-visual-test-ios: export TEST_BINARY_PATH := $(XCODE_DERIVED_DATA)/$(APPLICATION_NAME)/Build/Products/Debug-iphonesimulator/StatusIm.app
run-visual-test-ios: ##@test Run tests once in NodeJS
detox test --configuration ios.sim.debug
detox test --configuration ios.sim.debug
component-test-watch: export TARGET := clojure
component-test-watch: export COMPONENT_TEST := true
@@ -20,6 +20,8 @@ printed out by mistake in a log entry (see https://github.com/status-im/status-m
To minimize the risk of leaking passwords through logs, we should not pass
passwords as strings in our codebase. We introduced a new type `MaskedData` in
`status-im.utils.security`.
update (16-Dec-2022) `status-im.utils.security` is now moved over to `utils.security.core`
We use `(security/mask-data <data to hide>` to wrap sensitive data into this
type and then use `(security/unmask <masked-data>)` to get the plaintext back.
@@ -940,6 +940,52 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void getConnectionStringForBootstrappingAnotherDevice(final String configJSON, final Callback callback) throws JSONException {
final JSONObject jsonConfig = new JSONObject(configJSON);
final String keyUID = jsonConfig.getString("keyUID");
final 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);
}
@ReactMethod
public void inputConnectionStringForBootstrapping(final String connectionString, final String configJSON, final Callback callback) throws JSONException {
final JSONObject jsonConfig = new JSONObject(configJSON);
final String keyStorePath = pathCombine(this.getNoBackupDirectory(), "/keystore");
jsonConfig.put("keystorePath", keyStorePath);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable runnableTask = new Runnable() {
@Override
public void run() {
String res = Statusgo.inputConnectionStringForBootstrapping(connectionString,jsonConfig.toString());
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
}
@ReactMethod
public void hashTypedData(final String data, final Callback callback) {
Log.d(TAG, "hashTypedData");
@@ -90,26 +90,25 @@ static RCTBridge *bridge;
RCT_EXPORT_MODULE();
////////////////////////////////////////////////////////////////////
#pragma mark - shouldMoveToInternalStorage
//////////////////////////////////////////////////////////////////// shouldMoveToInternalStorage
RCT_EXPORT_METHOD(shouldMoveToInternalStorage:(RCTResponseSenderBlock)onResultCallback) {
// Android only
onResultCallback(@[[NSNull null]]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - moveToInternalStorage
//////////////////////////////////////////////////////////////////// moveToInternalStorage
RCT_EXPORT_METHOD(moveToInternalStorage:(RCTResponseSenderBlock)onResultCallback) {
// Android only
onResultCallback(@[[NSNull null]]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - InitKeystore method
//////////////////////////////////////////////////////////////////// StopNode
RCT_EXPORT_METHOD(initKeystore:(NSString *)keyUID
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -132,9 +131,9 @@ RCT_EXPORT_METHOD(initKeystore:(NSString *)keyUID
});
}
////////////////////////////////////////////////////////////////////
#pragma mark - SendLogs method
//////////////////////////////////////////////////////////////////// sendLogs
RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
jsLogs:(NSString *)jsLogs
callback:(RCTResponseSenderBlock)callback) {
@@ -196,7 +195,6 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
callback(@[zipFile.absoluteString]);
}
//////////////////////////////////////////////////////////////////// addPeer
RCT_EXPORT_METHOD(exportLogs:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"exportLogs() method called");
@@ -205,7 +203,6 @@ RCT_EXPORT_METHOD(exportLogs:(RCTResponseSenderBlock)callback) {
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// addPeer
RCT_EXPORT_METHOD(addPeer:(NSString *)enode
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoAddPeer(enode);
@@ -215,7 +212,6 @@ RCT_EXPORT_METHOD(addPeer:(NSString *)enode
#endif
}
//////////////////////////////////////////////////////////////////// multiAccountImportPrivateKey
RCT_EXPORT_METHOD(deleteMultiaccount:(NSString *)keyUID
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -226,7 +222,6 @@ RCT_EXPORT_METHOD(deleteMultiaccount:(NSString *)keyUID
callback(@[result]);
}
////////////////////////////////////verify//////////////////////////////// multiAccountImportPrivateKey
RCT_EXPORT_METHOD(deleteImportedKey:(NSString *)keyUID
address:(NSString *)address
password:(NSString *)password
@@ -239,7 +234,6 @@ RCT_EXPORT_METHOD(deleteImportedKey:(NSString *)keyUID
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountGenerateAndDeriveAddresses
RCT_EXPORT_METHOD(multiAccountGenerateAndDeriveAddresses:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -249,7 +243,6 @@ RCT_EXPORT_METHOD(multiAccountGenerateAndDeriveAddresses:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// MultiAccountStoreAccount
RCT_EXPORT_METHOD(multiAccountStoreAccount:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -259,7 +252,6 @@ RCT_EXPORT_METHOD(multiAccountStoreAccount:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// MultiAccountLoadAccount
RCT_EXPORT_METHOD(multiAccountLoadAccount:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -269,7 +261,6 @@ RCT_EXPORT_METHOD(multiAccountLoadAccount:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// MultiAccountReset
RCT_EXPORT_METHOD(multiAccountReset:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"MultiAccountReset() method called");
@@ -278,7 +269,6 @@ RCT_EXPORT_METHOD(multiAccountReset:(RCTResponseSenderBlock)callback) {
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountStoreDerived
RCT_EXPORT_METHOD(multiAccountStoreDerived:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -288,7 +278,6 @@ RCT_EXPORT_METHOD(multiAccountStoreDerived:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountImportPrivateKey
RCT_EXPORT_METHOD(multiAccountImportPrivateKey:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -298,7 +287,6 @@ RCT_EXPORT_METHOD(multiAccountImportPrivateKey:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// hashTransaction
RCT_EXPORT_METHOD(hashTransaction:(NSString *)txArgsJSON
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -308,7 +296,6 @@ RCT_EXPORT_METHOD(hashTransaction:(NSString *)txArgsJSON
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// hashMessage
RCT_EXPORT_METHOD(hashMessage:(NSString *)message
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -318,7 +305,40 @@ RCT_EXPORT_METHOD(hashMessage:(NSString *)message
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// hashTypedData
RCT_EXPORT_METHOD(getConnectionStringForBootstrappingAnotherDevice:(NSString *)configJSON
callback:(RCTResponseSenderBlock)callback) {
NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
NSString *keyUID = [configDict objectForKey:@"keyUID"];
NSURL *multiaccountKeystoreDir = [self getKeyStoreDir:keyUID];
NSString *keystoreDir = multiaccountKeystoreDir.path;
[configDict setValue:keystoreDir forKey:@"keystorePath"];
NSString *modifiedConfigJSON = [configDict bv_jsonStringWithPrettyPrint:NO];
NSString *result = StatusgoGetConnectionStringForBootstrappingAnotherDevice(modifiedConfigJSON);
callback(@[result]);
}
RCT_EXPORT_METHOD(inputConnectionStringForBootstrapping:(NSString *)cs
configJSON:(NSString *)configJSON
callback:(RCTResponseSenderBlock)callback) {
NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *rootUrl =[[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *multiaccountKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
NSString *keystoreDir = multiaccountKeystoreDir.path;
[configDict setValue:keystoreDir forKey:@"keystorePath"];
NSString *modifiedConfigJSON = [configDict bv_jsonStringWithPrettyPrint:NO];
NSString *result = StatusgoInputConnectionStringForBootstrapping(cs,modifiedConfigJSON);
callback(@[result]);
}
RCT_EXPORT_METHOD(hashTypedData:(NSString *)data
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -328,7 +348,6 @@ RCT_EXPORT_METHOD(hashTypedData:(NSString *)data
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// hashTypedDataV4
RCT_EXPORT_METHOD(hashTypedDataV4:(NSString *)data
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -338,7 +357,6 @@ RCT_EXPORT_METHOD(hashTypedDataV4:(NSString *)data
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// sendTransactionWithSignature
RCT_EXPORT_METHOD(sendTransactionWithSignature:(NSString *)txArgsJSON
signature:(NSString *)signature
callback:(RCTResponseSenderBlock)callback) {
@@ -349,7 +367,6 @@ RCT_EXPORT_METHOD(sendTransactionWithSignature:(NSString *)txArgsJSON
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountImportMnemonic
RCT_EXPORT_METHOD(multiAccountImportMnemonic:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -359,7 +376,6 @@ RCT_EXPORT_METHOD(multiAccountImportMnemonic:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountDeriveAddresses
RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -379,7 +395,6 @@ RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
return [json valueForKey:@"key-uid"];
}
//////////////////////////////////////////////////////////////////// prepareDirAndUpdateConfig
-(NSString *) prepareDirAndUpdateConfig:(NSString *)config
withKeyUID:(NSString *)keyUID {
NSFileManager *fileManager = [NSFileManager defaultManager];
@@ -452,7 +467,6 @@ RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
}
//////////////////////////////////////////////////////////////////// prepareDirAndUpdateConfig
RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
config:(NSString *)config
callback:(RCTResponseSenderBlock)callback) {
@@ -464,7 +478,6 @@ RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
callback(@[updatedConfig]);
}
//////////////////////////////////////////////////////////////////// saveAccountAndLogin
RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)multiaccountData
password:(NSString *)password
settings:(NSString *)settings
@@ -481,7 +494,6 @@ RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)multiaccountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// saveAccountAndLoginWithKeycard
RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
password:(NSString *)password
settings:(NSString *)settings
@@ -502,11 +514,11 @@ RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
- (NSString *) getExportDbFilePath {
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"export.db"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
[fileManager removeItemAtPath:filePath error:nil];
}
return filePath;
}
@@ -543,7 +555,6 @@ RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
}
}
//////////////////////////////////////////////////////////////////// login
RCT_EXPORT_METHOD(login:(NSString *)accountData
password:(NSString *)password) {
#if DEBUG
@@ -555,7 +566,6 @@ RCT_EXPORT_METHOD(login:(NSString *)accountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// loginWithConfig
RCT_EXPORT_METHOD(loginWithConfig:(NSString *)accountData
password:(NSString *)password
configJSON:(NSString *)configJSON) {
@@ -568,7 +578,6 @@ RCT_EXPORT_METHOD(loginWithConfig:(NSString *)accountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// loginWithKeycard
RCT_EXPORT_METHOD(loginWithKeycard:(NSString *)accountData
password:(NSString *)password
chatKey:(NSString *)chatKey) {
@@ -583,7 +592,6 @@ RCT_EXPORT_METHOD(loginWithKeycard:(NSString *)accountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// logout
RCT_EXPORT_METHOD(logout) {
#if DEBUG
NSLog(@"Logout() method called");
@@ -593,7 +601,6 @@ RCT_EXPORT_METHOD(logout) {
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// openAccounts
RCT_EXPORT_METHOD(openAccounts:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"OpenAccounts() method called");
@@ -607,7 +614,6 @@ RCT_EXPORT_METHOD(openAccounts:(RCTResponseSenderBlock)callback) {
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// verityAccountPassword
RCT_EXPORT_METHOD(verify:(NSString *)address
password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) {
@@ -624,7 +630,6 @@ RCT_EXPORT_METHOD(verify:(NSString *)address
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// verifyDatabasePassword
RCT_EXPORT_METHOD(verifyDatabasePassword:(NSString *)keyUID
password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) {
@@ -635,7 +640,6 @@ RCT_EXPORT_METHOD(verifyDatabasePassword:(NSString *)keyUID
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// changeDatabasePassword
RCT_EXPORT_METHOD(reEncryptDbAndKeystore:(NSString *)keyUID
currentPassword:(NSString *)currentPassword
newPassword:(NSString *)newPassword
@@ -648,7 +652,6 @@ RCT_EXPORT_METHOD(reEncryptDbAndKeystore:(NSString *)keyUID
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// convertToKeycardAccount
RCT_EXPORT_METHOD(convertToKeycardAccount:(NSString *)keyUID
accountData:(NSString *)accountData
settings:(NSString *)settings
@@ -663,9 +666,9 @@ RCT_EXPORT_METHOD(convertToKeycardAccount:(NSString *)keyUID
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - SendTransaction
//////////////////////////////////////////////////////////////////// sendTransaction
RCT_EXPORT_METHOD(sendTransaction:(NSString *)txArgsJSON
password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) {
@@ -676,9 +679,9 @@ RCT_EXPORT_METHOD(sendTransaction:(NSString *)txArgsJSON
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - SignMessage
//////////////////////////////////////////////////////////////////// signMessage
RCT_EXPORT_METHOD(signMessage:(NSString *)message
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -688,9 +691,9 @@ RCT_EXPORT_METHOD(signMessage:(NSString *)message
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - Recover
//////////////////////////////////////////////////////////////////// recover
RCT_EXPORT_METHOD(recover:(NSString *)message
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -700,9 +703,9 @@ RCT_EXPORT_METHOD(recover:(NSString *)message
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - SignTypedData
//////////////////////////////////////////////////////////////////// signTypedData
RCT_EXPORT_METHOD(signTypedData:(NSString *)data
account:(NSString *)account
password:(NSString *)password
@@ -714,9 +717,9 @@ RCT_EXPORT_METHOD(signTypedData:(NSString *)data
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - SignTypedDataV4
//////////////////////////////////////////////////////////////////// signTypedDataV4
RCT_EXPORT_METHOD(signTypedDataV4:(NSString *)data
account:(NSString *)account
password:(NSString *)password
@@ -728,9 +731,9 @@ RCT_EXPORT_METHOD(signTypedDataV4:(NSString *)data
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - SignGroupMembership
//////////////////////////////////////////////////////////////////// signGroupMembership
RCT_EXPORT_METHOD(signGroupMembership:(NSString *)content
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -740,9 +743,9 @@ RCT_EXPORT_METHOD(signGroupMembership:(NSString *)content
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - ExtractGroupMembershipSignatures
//////////////////////////////////////////////////////////////////// extractGroupMembershipSignatures
RCT_EXPORT_METHOD(extractGroupMembershipSignatures:(NSString *)content
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -752,9 +755,9 @@ RCT_EXPORT_METHOD(extractGroupMembershipSignatures:(NSString *)content
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - GetNodeConfig
//////////////////////////////////////////////////////////////////// getNodeConfig
RCT_EXPORT_METHOD(getNodeConfig:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"GetNodeConfig() method called");
@@ -764,9 +767,9 @@ RCT_EXPORT_METHOD(getNodeConfig:(RCTResponseSenderBlock)callback) {
}
////////////////////////////////////////////////////////////////////
#pragma mark - only android methods
////////////////////////////////////////////////////////////////////
RCT_EXPORT_METHOD(setAdjustResize) {
#if DEBUG
NSLog(@"setAdjustResize() works only on Android");
@@ -957,10 +960,10 @@ RCT_EXPORT_METHOD(exportUnencryptedDatabase:(NSString *)accountData
#if DEBUG
NSLog(@"exportUnencryptedDatabase() method called");
#endif
NSString *filePath = [self getExportDbFilePath];
StatusgoExportUnencryptedDatabase(accountData, password, filePath);
callback(@[filePath]);
}
@@ -995,7 +998,7 @@ RCT_EXPORT_METHOD(deactivateKeepAwake)
});
}
//// deviceinfo
#pragma mark - deviceinfo
- (bool) is24Hour
{
+1
View File
@@ -89,6 +89,7 @@
"@testing-library/jest-native": "^5.0.0",
"@testing-library/react-native": "^11.2.0",
"@types/jest": "^28.1.6",
"concurrently": "^7.6.0",
"detox": "^19.9.1",
"jest": "^25.1.0",
"jest-circus": "^26.0.0",
Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 KiB

+15 -10
View File
@@ -81,20 +81,25 @@
:output-dir "target/test"
:optimizations :simple
:target :node-test
:main status-im.test-runner/main
;; 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$"
:main
status-im.test-runner/main
;; set :ui-driven to true to let shadow-cljs inject node-repl
:ui-driven true
:ui-driven
true
:closure-defines
{status-im.utils.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
status-im.utils.config/OPENSEA_API_KEY #shadow/env "OPENSEA_API_KEY"}
:compiler-options {;; needed because we override require and it
;; messes with source-map which reports callstack
;; exceeded exceptions instead of real issues
:source-map false
;; needed because we use deref in tests
:static-fns false
:optimizations :simple
:infer-externs true}}
:compiler-options
{;; needed because we override require and it
;; messes with source-map which reports callstack
;; exceeded exceptions instead of real issues
:source-map false
;; needed because we use deref in tests
:static-fns false
:optimizations :simple
:infer-externs true}}
;; mock.js-dependencies is mocking the react-native libraries
;; we build it as a node library so that it can be required by
+11 -17
View File
@@ -1,25 +1,22 @@
(ns i18n.i18n
(:require ["i18n-js" :as i18n]
[clojure.string :as string]
[status-im.goog.i18n :as goog.i18n]))
(:require
["i18n-js" :as i18n]
[clojure.string :as string]
[status-im.goog.i18n :as goog.i18n]))
(defn init
[default-device-language translations-by-locale]
(defn init [default-device-language translations-by-locale]
(set! (.-fallbacks i18n) true)
(set! (.-defaultSeparator i18n) "/")
(set! (.-locale i18n) default-device-language)
(set! (.-translations i18n) translations-by-locale))
(defn get-translations
[]
(defn get-translations []
(.-translations i18n))
(defn set-language
[lang]
(defn set-language [lang]
(set! (.-locale i18n) lang))
;;:zh, :zh-hans-xx, :zh-hant-xx have been added until this bug will be fixed
;;https://github.com/fnando/i18n-js/issues/460
;;:zh, :zh-hans-xx, :zh-hant-xx have been added until this bug will be fixed https://github.com/fnando/i18n-js/issues/460
(def delimeters
"This function is a hack: mobile Safari doesn't support toLocaleString(), so we need to pass
@@ -32,8 +29,7 @@
{:delimiter ""
:separator (subs n 4 5)})))
(defn label-number
[number]
(defn label-number [number]
(when number
(let [{:keys [delimiter separator]} delimeters]
(.toNumber i18n
@@ -45,8 +41,7 @@
(def default-option-value "<no value>")
(defn label-options
[options]
(defn label-options [options]
;; i18n ignores nil value, leading to misleading messages
(into {} (for [[k v] options] [k (or v default-option-value)])))
@@ -60,8 +55,7 @@
(def label (memoize label-fn))
(defn label-pluralize
[count path & options]
(defn label-pluralize [count path & options]
(if (exists? (.t i18n))
(.p i18n count (name path) (clj->js options))
(name path)))
+18
View File
@@ -0,0 +1,18 @@
import { useDerivedValue } from 'react-native-reanimated';
const MAX_SCALE = 1.8;
export function ringScale(scale, subtract) {
return useDerivedValue(
function () {
'worklet'
const value = scale.value;
const maxDelta = MAX_SCALE - 1;
const maxDeltaDiff = 1 - maxDelta;
const maxVirtualScale = MAX_SCALE + maxDelta;
const decimals = value - Math.floor(value);
const normalizedValue = value >= maxVirtualScale ? (decimals + ((parseInt(value) - 1) * maxDeltaDiff) + 1) : value;
return (((normalizedValue - subtract) > MAX_SCALE ? normalizedValue - maxDelta : normalizedValue) - subtract);
}
);
}
+11
View File
@@ -1,3 +1,5 @@
import { useDerivedValue, interpolate } from 'react-native-reanimated';
// Generic Worklets
export function applyAnimationsToStyle(animations, style) {
@@ -27,3 +29,12 @@ export function applyAnimationsToStyle(animations, style) {
return Object.assign(animatedStyle, style);
};
};
export function interpolateValue(sharedValue, inputRange, outputRange) {
return useDerivedValue(
function () {
'worklet'
return interpolate(sharedValue.value, inputRange, outputRange);
}
);
}
+246 -262
View File
@@ -4,24 +4,38 @@
(:require [status-im.utils.test :as utils.test])
(:require [status-im.chat.default-chats :refer (default-chats)]))
(def action-button #js {:default #js {:Item #js {}}})
(def config #js {:default #js {}})
(def camera #js {:RNCamera #js {:Constants #js {}}})
(def dialogs #js {})
(def dismiss-keyboard #js {})
(def emoji-picker #js {:default #js {}})
(def fs #js {})
(def i18n #js {:locale "en"})
(def image-crop-picker #js {})
(def image-resizer #js {})
(def qr-code #js {})
(def svg #js {})
;; to generate a js Proxy at js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ that accept any (.xxx) call and return itself
;; For the convenience to mock eg.
;; (-> reanimated/slide-out-up-animation .springify (.damping 20) (.stiffness 300))
;; (-> reanimated/slide-out-up-animation (.damping 20) .springify (.stiffness 300))
(js/eval "
var globalThis
if (typeof window === \"undefined\") {
globalThis = global
} else {
globalThis = window
}
globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return () => globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__}})
")
(def action-button #js {:default #js {:Item #js {}}})
(def config #js {:default #js {}})
(def camera #js {:RNCamera #js {:Constants #js {}}})
(def dialogs #js {})
(def dismiss-keyboard #js {})
(def emoji-picker #js {:default #js {}})
(def fs #js {})
(def i18n #js {:locale "en"})
(def image-crop-picker #js {})
(def image-resizer #js {})
(def qr-code #js {})
(def svg #js {})
(def react-native
(clj->js {:NativeModules {:RNGestureHandlerModule {:Direction (fn [])}
:PushNotifications {}
:Status utils.test/status
:ReanimatedModule {:configureProps (fn [])}}
(clj->js {:NativeModules {:RNGestureHandlerModule {:Direction (fn [])}
:PushNotifications {}
:Status utils.test/status
:ReanimatedModule {:configureProps (fn [])}}
:View {}
:RefreshControl {}
@@ -64,10 +78,9 @@
:Platform {:select (fn [])}
:I18nManager {:isRTL ""}
:NativeEventEmitter (fn [])
:LayoutAnimation {:Presets #js
{:easeInEaseOut nil
:linear nil
:spring nil}
:LayoutAnimation {:Presets #js {:easeInEaseOut nil
:linear nil
:spring nil}
:Types #js {}
:Properties #{}
:create (fn [])
@@ -78,194 +91,168 @@
(set! js/ReactNative react-native)
(def reanimated-bottom-sheet #js {:default #js {}})
(def reanimated-bottom-sheet #js {:default #js {}})
(def icons #js {:default #js {}})
(def webview #js {:WebView #js {}})
(def status-keycard
#js
{:default #js
{:nfcIsSupported (fn [] #js {:then identity})
:nfcIsEnabled (fn [] #js {:then identity})}})
(def icons #js {:default #js {}})
(def webview #js {:WebView #js {}})
(def status-keycard #js {:default #js {:nfcIsSupported (fn [] #js {:then identity})
:nfcIsEnabled (fn [] #js {:then identity})}})
(def snoopy #js {:default #js {}})
(def snoopy-filter #js {:default #js {}})
(def snoopy-bars #js {:default #js {}})
(def snoopy-buffer #js {:default #js {}})
(def fetch #js {})
(def snoopy #js {:default #js {}})
(def snoopy-filter #js {:default #js {}})
(def snoopy-bars #js {:default #js {}})
(def snoopy-buffer #js {:default #js {}})
(def fetch #js {})
(def async-storage-atom (atom {}))
(def async-storage
#js
{:default #js
{:setItem #(js/Promise.resolve)
:multiGet #(js/Promise.resolve)
:getItem #(js/Promise.resolve)}})
(def async-storage #js {:default #js {:setItem #(js/Promise.resolve)
:multiGet #(js/Promise.resolve)
:getItem #(js/Promise.resolve)}})
(def background-timer
(clj->js {:default {:setTimeout js/setTimeout
:setInterval js/setInterval
:clearTimeout js/clearTimeout
:clearInterval js/clearInterval}}))
(def background-timer (clj->js {:default {:setTimeout js/setTimeout
:setInterval js/setInterval
:clearTimeout js/clearTimeout
:clearInterval js/clearInterval}}))
(def keychain
#js
{:setGenericPassword (constantly (.resolve js/Promise true))
:setInternetCredentials #(js/Promise.resolve)
:resetInternetCredentials #(js/Promise.resolve)
"ACCESSIBLE" {}
"ACCESS_CONTROL" {}})
(def keychain #js {:setGenericPassword (constantly (.resolve js/Promise true))
:setInternetCredentials #(js/Promise.resolve)
:resetInternetCredentials #(js/Promise.resolve)
"ACCESSIBLE" {}
"ACCESS_CONTROL" {}})
(def react-native-mail #js {:mail #js {}})
(def react-native-screens #js {})
(def react-native-shake #js {})
(def react-native-screens #js {})
(def react-native-shake #js {})
(def react-native-share #js {:default {}})
(def react-native-svg
#js
{:SvgUri #js {:render identity}
:SvgXml #js {:render identity}})
(def react-native-svg #js {:SvgUri #js {:render identity}
:SvgXml #js {:render identity}
:default #js {:render identity}
:Path #js {:render identity}})
(def react-native-webview #js {:default {}})
(def react-native-audio-toolkit #js {:MediaStates {}})
(def net-info #js {})
(def touchid #js {})
(def net-info #js {})
(def touchid #js {})
(def react-native-image-viewing #js {:default {}})
(def safe-area-context
(clj->js {:SafeAreaProvider {:_reactNativeIphoneXHelper {:getStatusBarHeight (fn [])}}
:SafeAreaInsetsContext {:Consumer (fn [])}
:SafeAreaView {}}))
(def safe-area-context (clj->js {:SafeAreaProvider {:_reactNativeIphoneXHelper {:getStatusBarHeight (fn [])}}
:SafeAreaInsetsContext {:Consumer (fn [])}
:SafeAreaView {}}))
(def back-handler
#js
{:addEventListener identity
:removeEventListener identity})
(def react
#js
{:useCallback nil
:useEffect nil
:useRef nil
:createRef nil
:Fragment identity})
(def react-navigation-native
#js
{:NavigationContainer #js {}
:useFocusEffect identity
:CommonActions #js {}
:StackActions #js {}})
(def back-handler #js {:addEventListener identity
:removeEventListener identity})
(def react #js {:useCallback nil
:useEffect nil
:useRef nil
:createRef nil
:Fragment identity})
(def react-navigation-native #js {:NavigationContainer #js {}
:useFocusEffect identity
:CommonActions #js {}
:StackActions #js {}})
(def react-native-navigation
#js
{:Navigation #js
{:constants (fn [] #js {:then identity})
:setDefaultOptions identity
:setRoot identity
:dismissOverlay #(js/Promise.resolve)
:showOverlay identity
:setLazyComponentRegistrator identity
:pop identity
:push identity
:mergeOptions identity
:popToRoot identity
:showModal identity
:dismissModal identity
:registerComponent identity
:events
(fn []
#js
{:registerModalDismissedListener identity
:registerAppLaunchedListener identity
:registerBottomTabSelectedListener identity
:registerComponentDidDisappearListener identity
:registerComponentDidAppearListener identity
:registerNavigationButtonPressedListener identity})}})
(def react-native-navigation #js {:Navigation #js {:constants (fn [] #js {:then identity})
:setDefaultOptions identity
:setRoot identity
:dismissOverlay #(js/Promise.resolve)
:showOverlay identity
:setLazyComponentRegistrator identity
:pop identity
:push identity
:mergeOptions identity
:popToRoot identity
:showModal identity
:dismissModal identity
:registerComponent identity
:events
(fn []
#js {:registerModalDismissedListener identity
:registerAppLaunchedListener identity
:registerBottomTabSelectedListener identity
:registerComponentDidDisappearListener identity
:registerComponentDidAppearListener identity
:registerNavigationButtonPressedListener identity})}})
(def react-navigation-stack
#js
{:createStackNavigator identity
:TransitionPresets #js {:ModalPresentationIOS #js {}}})
(def react-navigation-stack #js {:createStackNavigator identity
:TransitionPresets #js {:ModalPresentationIOS #js {}}})
(def react-navigation-bottom-tabs #js {:createBottomTabNavigator identity})
(def react-native-haptic-feedback #js {:default #js {:trigger nil}})
(def react-native-reanimated
#js
{:default #js
{:createAnimatedComponent identity
:eq nil
:greaterOrEq nil
:greaterThan nil
:lessThan nil
:lessOrEq nil
:add nil
:diff nil
:divide nil
:sub nil
:multiply nil
:abs nil
:min nil
:max nil
:neq nil
:and nil
:or nil
:not nil
:set nil
:startClock nil
:stopClock nil
:Value nil
:Clock nil
:debug nil
:log nil
:event nil
:cond nil
:block nil
:interpolateNode nil
:call nil
:timing nil
:onChange nil
:View #js {}
:Image #js {}
:ScrollView #js {}
:Text #js {}
:Extrapolate #js {:CLAMP nil}
:Code #js {}}
:EasingNode #js
{:bezier identity
:linear identity}
:clockRunning nil
:useSharedValue (fn [])
:useAnimatedStyle (fn [])
:withTiming (fn [])
:withDelay (fn [])
:Easing #js {:bezier identity}
:Keyframe (fn [])})
(def react-native-gesture-handler
#js
{:default #js {}
:State #js
{:BEGAN nil
:ACTIVE nil
:CANCELLED nil
:END nil
:FAILED nil
:UNDETERMINED nil}
:PureNativeButton #js {}
:TapGestureHandler #js {}
:PanGestureHandler #js {}
:TouchableHighlight #js {}
:LongPressGestureHandler #js {}
:TouchableWithoutFeedback #js {}
:NativeViewGestureHandler #js {}
:FlatList #js {}
:ScrollView #js {}
:TouchableOpacity #js {}
:GestureDetector #js {}
:Gesture #js {:Pan nil}
:createNativeWrapper identity})
(def react-native-reanimated #js {:default #js {:createAnimatedComponent identity
:eq nil
:greaterOrEq nil
:greaterThan nil
:lessThan nil
:lessOrEq nil
:add nil
:diff nil
:divide nil
:sub nil
:multiply nil
:abs nil
:min nil
:max nil
:neq nil
:and nil
:or nil
:not nil
:set nil
:startClock nil
:stopClock nil
:Value nil
:Clock nil
:debug nil
:log nil
:event nil
:cond nil
:block nil
:interpolateNode nil
:call nil
:timing nil
:onChange nil
:View #js {}
:Image #js {}
:ScrollView #js {}
:Text #js {}
:Extrapolate #js {:CLAMP nil}
:Code #js {}}
:EasingNode #js {:bezier identity
:linear identity}
:clockRunning nil
:useSharedValue (fn [])
:useAnimatedStyle (fn [])
:withTiming (fn [])
:withDelay (fn [])
:Easing #js {:bezier identity}
:Keyframe (fn [])
:SlideOutUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__
:SlideInUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__
:LinearTransition js/__STATUS_MOBILE_JS_IDENTITY_PROXY__})
(def react-native-gesture-handler #js {:default #js {}
:State #js {:BEGAN nil
:ACTIVE nil
:CANCELLED nil
:END nil
:FAILED nil
:UNDETERMINED nil}
:PureNativeButton #js {}
:TapGestureHandler #js {}
:PanGestureHandler #js {}
:TouchableHighlight #js {}
:LongPressGestureHandler #js {}
:TouchableWithoutFeedback #js {}
:NativeViewGestureHandler #js {}
:FlatList #js {}
:ScrollView #js {}
:TouchableOpacity #js {}
:GestureDetector #js {}
:Gesture #js {:Pan nil}
:createNativeWrapper identity})
(def react-native-redash #js {:clamp nil})
(def react-native-languages
(clj->js {:default {:language "en"
:addEventListener (fn [])
(clj->js {:default {:language "en",
:addEventListener (fn []),
:removeEventListener (fn [])}}))
(def react-native-device-info
@@ -275,9 +262,8 @@
#js {:CameraKitCamera #js {}})
(def react-native-push-notification
#js
{:localNotification identity
:requestPermission identity})
#js {:localNotification identity
:requestPermission identity})
(def react-native-gradien #js {:default #js {}})
@@ -305,87 +291,85 @@
(def react-native-camera-roll
(clj->js {:default #js {}}))
(def wallet-connect-client
#js
{:default #js {}
:CLIENT_EVENTS #js
{:session #js
{:request nil
:created nil
:deleted nil
:proposal nil
:updated nil}}})
(def wallet-connect-client #js {:default #js {}
:CLIENT_EVENTS #js {:session #js {:request nil
:created nil
:deleted nil
:proposal nil
:updated nil}}})
(def worklet-factory
#js {:applyAnimationsToStyle (fn [])})
(def shell-worklets #js {})
(def record-audio-worklets #js {})
;; Update i18n_resources.cljs
(defn mock
[module]
(defn mock [module]
(case module
"react-native-languages" react-native-languages
"react-native-background-timer" background-timer
"react-native-image-crop-picker" image-crop-picker
"react-native-gesture-handler" react-native-gesture-handler
"react-native-safe-area-context" safe-area-context
"react-native-config" config
"react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn [])
:getBottomSpace (fn [])})
"react-native-screens" (clj->js {})
"react-native-reanimated" react-native-reanimated
"react-native-redash/lib/module/v1" react-native-redash
"react-native-fetch-polyfill" fetch
"react-native-status-keycard" status-keycard
"react-native-keychain" keychain
"react-native-touch-id" touchid
"@react-native-community/netinfo" net-info
"react-native-dialogs" dialogs
"react-native" react-native
"react-native-fs" fs
"react-native-mail" react-native-mail
"react-native-image-resizer" image-resizer
"react-native-haptic-feedback" react-native-haptic-feedback
"react-native-device-info" react-native-device-info
"react-native-push-notification" react-native-push-notification
"react-native-linear-gradient" react-native-gradien
"@react-native-community/masked-view" masked-view
"react-native-blob-util" react-native-blob-util
"react-native-navigation" react-native-navigation
"react-native-languages" react-native-languages
"react-native-background-timer" background-timer
"react-native-image-crop-picker" image-crop-picker
"react-native-gesture-handler" react-native-gesture-handler
"react-native-safe-area-context" safe-area-context
"react-native-config" config
"react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn [])
:getBottomSpace (fn [])})
"react-native-screens" (clj->js {})
"react-native-reanimated" react-native-reanimated
"react-native-redash/lib/module/v1" react-native-redash
"react-native-fetch-polyfill" fetch
"react-native-status-keycard" status-keycard
"react-native-keychain" keychain
"react-native-touch-id" touchid
"@react-native-community/netinfo" net-info
"react-native-dialogs" dialogs
"react-native" react-native
"react-native-fs" fs
"react-native-mail" react-native-mail
"react-native-image-resizer" image-resizer
"react-native-haptic-feedback" react-native-haptic-feedback
"react-native-device-info" react-native-device-info
"react-native-push-notification" react-native-push-notification
"react-native-linear-gradient" react-native-gradien
"@react-native-community/masked-view" masked-view
"react-native-blob-util" react-native-blob-util
"react-native-navigation" react-native-navigation
"@react-native-community/push-notification-ios" push-notification-ios
"@react-native-community/blur" react-native-blur
"@react-native-community/cameraroll" react-native-camera-roll
"react-native-camera-kit" react-native-camera-kit
"react-native-permissions" react-native-permissions
"rn-emoji-keyboard" rn-emoji-keyboard
"react-native-hole-view" react-native-hole-view
"react-native-draggable-flatlist" react-native-draggable-flatlist
"react-native-webview" react-native-webview
"@react-native-community/audio-toolkit" react-native-audio-toolkit
"react-native-image-viewing" react-native-image-viewing
"react-native-share" react-native-share
"@react-native-async-storage/async-storage" async-storage
"react-native-svg" react-native-svg
"../src/js/worklet_factory.js" worklet-factory
"../src/js/shell_worklets.js" shell-worklets
"./fleets.js" default-fleets
"./chats.js" default-chats
"@walletconnect/client" wallet-connect-client
"../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json"))
"../translations/de.json" (js/JSON.parse (slurp "./translations/de.json"))
"../translations/en.json" (js/JSON.parse (slurp "./translations/en.json"))
"../translations/es.json" (js/JSON.parse (slurp "./translations/es.json"))
"../translations/es_419.json" (js/JSON.parse (slurp "./translations/es_419.json"))
"../translations/fil.json" (js/JSON.parse (slurp "./translations/fil.json"))
"../translations/fr.json" (js/JSON.parse (slurp "./translations/fr.json"))
"../translations/id.json" (js/JSON.parse (slurp "./translations/id.json"))
"../translations/it.json" (js/JSON.parse (slurp "./translations/it.json"))
"../translations/ko.json" (js/JSON.parse (slurp "./translations/ko.json"))
"../translations/pt_BR.json" (js/JSON.parse (slurp "./translations/pt_BR.json"))
"../translations/ru.json" (js/JSON.parse (slurp "./translations/ru.json"))
"../translations/tr.json" (js/JSON.parse (slurp "./translations/tr.json"))
"../translations/zh.json" (js/JSON.parse (slurp "./translations/zh.json"))
"../translations/zh_hant.json" (js/JSON.parse (slurp "./translations/zh_hant.json"))
"../translations/zh_TW.json" (js/JSON.parse (slurp "./translations/zh_TW.json"))
"@react-native-community/blur" react-native-blur
"@react-native-community/cameraroll" react-native-camera-roll
"react-native-camera-kit" react-native-camera-kit
"react-native-permissions" react-native-permissions
"rn-emoji-keyboard" rn-emoji-keyboard
"react-native-hole-view" react-native-hole-view
"react-native-draggable-flatlist" react-native-draggable-flatlist
"react-native-webview" react-native-webview
"@react-native-community/audio-toolkit" react-native-audio-toolkit
"react-native-image-viewing" react-native-image-viewing
"react-native-share" react-native-share
"@react-native-async-storage/async-storage" async-storage
"react-native-svg" react-native-svg
"../src/js/worklet_factory.js" worklet-factory
"../src/js/shell_worklets.js" shell-worklets
"../src/js/record_audio_worklets.js" record-audio-worklets
"./fleets.js" default-fleets
"./chats.js" default-chats
"@walletconnect/client" wallet-connect-client
"../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json"))
"../translations/de.json" (js/JSON.parse (slurp "./translations/de.json"))
"../translations/en.json" (js/JSON.parse (slurp "./translations/en.json"))
"../translations/es.json" (js/JSON.parse (slurp "./translations/es.json"))
"../translations/es_419.json" (js/JSON.parse (slurp "./translations/es_419.json"))
"../translations/fil.json" (js/JSON.parse (slurp "./translations/fil.json"))
"../translations/fr.json" (js/JSON.parse (slurp "./translations/fr.json"))
"../translations/id.json" (js/JSON.parse (slurp "./translations/id.json"))
"../translations/it.json" (js/JSON.parse (slurp "./translations/it.json"))
"../translations/ko.json" (js/JSON.parse (slurp "./translations/ko.json"))
"../translations/pt_BR.json" (js/JSON.parse (slurp "./translations/pt_BR.json"))
"../translations/ru.json" (js/JSON.parse (slurp "./translations/ru.json"))
"../translations/tr.json" (js/JSON.parse (slurp "./translations/tr.json"))
"../translations/zh.json" (js/JSON.parse (slurp "./translations/zh.json"))
"../translations/zh_hant.json" (js/JSON.parse (slurp "./translations/zh_hant.json"))
"../translations/zh_TW.json" (js/JSON.parse (slurp "./translations/zh_TW.json"))
nil))
+58 -88
View File
@@ -1,12 +1,12 @@
(ns quo.animated
(:refer-clojure :exclude [abs set delay divide])
(:require ["react-native-reanimated" :default animated :refer (clockRunning EasingNode)]
["react-native-redash/lib/module/v1" :as redash]
[oops.core :refer [ocall oget]]
[quo.gesture-handler :as gh]
quo.react
(:require [reagent.core :as reagent]
[quo.react-native :as rn]
[reagent.core :as reagent])
[quo.gesture-handler :as gh]
[oops.core :refer [oget ocall]]
["react-native-reanimated" :default animated :refer (clockRunning EasingNode)]
["react-native-redash/lib/module/v1" :as redash]
quo.react)
(:require-macros [quo.react :refer [maybe-js-deps]]))
(def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent animated)))
@@ -15,10 +15,9 @@
(def text (reagent/adapt-react-class (.-Text animated)))
(def scroll-view (reagent/adapt-react-class (.-ScrollView animated)))
(def code (reagent/adapt-react-class (.-Code animated)))
(def animated-flat-list (create-animated-component gh/flat-list-raw))
(def animated-flat-list (create-animated-component gh/flat-list-raw))
(defn flat-list
[props]
(defn flat-list [props]
[animated-flat-list (rn/base-list-props props)])
(def useCode (.-useCode animated))
@@ -59,42 +58,37 @@
(def bezier (.-bezier ^js EasingNode))
(def linear (.-linear ^js EasingNode))
(def easings
{:linear linear
:ease-in (bezier 0.42 0 1 1)
:ease-out (bezier 0 0 0.58 1)
:ease-in-out (bezier 0.42 0 0.58 1)
:cubic (bezier 0.55 0.055 0.675 0.19)
:keyboard (bezier 0.17 0.59 0.4 0.77)})
(def easings {:linear linear
:ease-in (bezier 0.42 0 1 1)
:ease-out (bezier 0 0 0.58 1)
:ease-in-out (bezier 0.42 0 0.58 1)
:cubic (bezier 0.55 0.055 0.675 0.19)
:keyboard (bezier 0.17 0.59 0.4 0.77)})
(def springs
{:lazy {:damping 50
:mass 0.3
:stiffness 120
:overshootClamping true
:bouncyFactor 1}
:jump {:damping 13
:mass 0.5
:stiffness 170
:overshootClamping false
:bouncyFactor 1
:restSpeedThreshold 0.001
:restDisplacementThreshold 0.001}})
(def springs {:lazy {:damping 50
:mass 0.3
:stiffness 120
:overshootClamping true
:bouncyFactor 1}
:jump {:damping 13
:mass 0.5
:stiffness 170
:overshootClamping false
:bouncyFactor 1
:restSpeedThreshold 0.001
:restDisplacementThreshold 0.001}})
(defn set-value
[anim val]
(defn set-value [anim val]
(ocall anim "setValue" val))
(def Value (oget animated "Value"))
(defn value
[x]
(defn value [x]
(new Value x))
(def Clock (oget animated "Clock"))
(defn clock
[]
(defn clock []
(new Clock))
(def debug (oget animated "debug"))
@@ -106,10 +100,8 @@
([config options]
(ocall animated "event" (clj->js config) (clj->js options))))
(defn on-change
[state node]
(ocall animated
"onChange"
(defn on-change [state node]
(ocall animated "onChange"
state
(if (vector? node)
(clj->js node)
@@ -132,31 +124,24 @@
(clj->js else-node)
else-node))))
(defn block
[opts]
(defn block [opts]
(.block ^js animated (to-array opts)))
(defn interpolate
[anim-value config]
(defn interpolate [anim-value config]
(.interpolateNode ^js animated anim-value (clj->js config)))
(defn call*
[args callback]
(defn call* [args callback]
(.call ^js animated (to-array args) callback))
(defn timing
[clock-value opts config]
(defn timing [clock-value opts config]
(.timing ^js animated
clock-value
(clj->js opts)
(clj->js config)))
(defn spring
[clock-value opts config]
(.spring ^js animated
clock-value
(clj->js opts)
(clj->js config)))
(defn spring [clock-value opts config]
(.spring ^js animated clock-value
(clj->js opts) (clj->js config)))
(def extrapolate {:clamp (oget animated "Extrapolate" "CLAMP")})
@@ -165,48 +150,37 @@
(def clamp (oget redash "clamp"))
(def diff-clamp (.-diffClamp ^js redash))
(defn with-spring
[config]
(defn with-spring [config]
(ocall redash "withSpring" (clj->js config)))
(defn with-decay
[config]
(defn with-decay [config]
(.withDecay ^js redash (clj->js config)))
(defn with-offset
[config]
(defn with-offset [config]
(.withOffset ^js redash (clj->js config)))
(defn with-spring-transition
[val config]
(defn with-spring-transition [val config]
(.withSpringTransition ^js redash val (clj->js config)))
(defn with-timing-transition
[val config]
(defn with-timing-transition [val config]
(.withTimingTransition ^js redash val (clj->js config)))
(defn use-spring-transition
[val config]
(defn use-spring-transition [val config]
(.useSpringTransition ^js redash val (clj->js config)))
(defn use-timing-transition
[val config]
(defn use-timing-transition [val config]
(.useTimingTransition ^js redash val (clj->js config)))
(defn re-timing
[config]
(defn re-timing [config]
(.timing ^js redash (clj->js config)))
(defn re-spring
[config]
(defn re-spring [config]
(.spring ^js redash (clj->js config)))
(defn on-scroll
[opts]
(defn on-scroll [opts]
(ocall redash "onScrollEvent" (clj->js opts)))
(defn on-gesture
[opts]
(defn on-gesture [opts]
(let [gesture-event (event #js [#js {:nativeEvent (clj->js opts)}])]
{:onHandlerStateChange gesture-event
:onGestureEvent gesture-event}))
@@ -217,22 +191,19 @@
(def delay (.-delay ^js redash))
(defn loop*
[opts]
(defn loop* [opts]
(ocall redash "loop" (clj->js opts)))
(def use-value (.-useValue ^js redash))
(def use-clock (.-useClock ^js redash))
(defn use-gesture
[opts]
(defn use-gesture [opts]
(let [gesture (.useGestureHandler ^js redash (clj->js opts))]
{:onHandlerStateChange (.-onHandlerStateChange ^js gesture)
:onGestureEvent (.-onGestureEvent ^js gesture)}))
(defn snap-point
[value velocity snap-points]
(defn snap-point [value velocity snap-points]
(.snapPoint ^js redash value velocity (to-array snap-points)))
(defn cancelable-loop
@@ -285,13 +256,12 @@
(set position (add offset val))])
(cond* (and* (eq state (:end gh/states))
(not* animation-over))
[(set position
(re-timing
{:clock c
:easing easing
:duration duration
:from position
:to to}))
[(set position (re-timing
{:clock c
:easing easing
:duration duration
:from position
:to to}))
(cond* (not* (clock-running c))
finish-animation)])
position])))
+29 -33
View File
@@ -1,9 +1,9 @@
(ns quo.components.animated.pressable
(:require [cljs-bean.core :as bean]
[quo.animated :as animated]
[quo.gesture-handler :as gesture-handler]
(:require [quo.animated :as animated]
[quo.react :as react]
[reagent.core :as reagent]))
[reagent.core :as reagent]
[cljs-bean.core :as bean]
[quo.gesture-handler :as gesture-handler]))
(def long-press-duration 500)
(def scale-down-small 0.95)
@@ -54,8 +54,7 @@
:right 0
:position :absolute})
(defn pressable-hooks
[props]
(defn pressable-hooks [props]
(let [{background-color :bgColor
border-radius :borderRadius
border-color :borderColor
@@ -70,24 +69,23 @@
:or {border-radius 0
type "primary"}}
(bean/bean props)
long-press-ref (react/create-ref)
state (animated/use-value (:undetermined gesture-handler/states))
active (animated/eq state (:began gesture-handler/states))
gesture-handler (animated/use-gesture {:state state})
animation (react/use-memo
(fn []
(animated/with-timing-transition active
{:duration (animated/cond* active time-in time-out)
:easing (:ease-in animated/easings)}))
[])
long-press-ref (react/create-ref)
state (animated/use-value (:undetermined gesture-handler/states))
active (animated/eq state (:began gesture-handler/states))
gesture-handler (animated/use-gesture {:state state})
animation (react/use-memo
(fn []
(animated/with-timing-transition active
{:duration (animated/cond* active time-in time-out)
:easing (:ease-in animated/easings)}))
[])
{:keys [background
foreground]}
(react/use-memo
(fn []
(type->animation {:type (keyword type)
:animation animation}))
[type])
handle-press (fn [] (when on-press (on-press)))
foreground]} (react/use-memo
(fn []
(type->animation {:type (keyword type)
:animation animation}))
[type])
handle-press (fn [] (when on-press (on-press)))
long-gesture-handler (react/callback
(fn [^js evt]
(let [gesture-state (-> evt .-nativeEvent .-state)]
@@ -113,9 +111,8 @@
:min-duration-ms long-press-duration
:max-dist 22
:ref long-press-ref}
[animated/view
{:accessible true
:accessibility-label accessibility-label}
[animated/view {:accessible true
:accessibility-label accessibility-label}
[gesture-handler/tap-gesture-handler
(merge gesture-handler
{:shouldCancelWhenOutside true
@@ -123,13 +120,12 @@
:enabled (boolean (and (or on-press on-long-press on-press-start)
(not disabled)))})
[animated/view
[animated/view
{:style (merge absolute-fill
background
{:background-color background-color
:border-radius border-radius
:border-color border-color
:border-width border-width})}]
[animated/view {:style (merge absolute-fill
background
{:background-color background-color
:border-radius border-radius
:border-color border-color
:border-width border-width})}]
(into [animated/view {:style foreground}]
(react/get-children children))]]]])))
+45 -58
View File
@@ -7,8 +7,7 @@
[quo.platform :as platform]
[reagent.core :as reagent]))
(defn header-wrapper-style
[{:keys [value offset]}]
(defn header-wrapper-style [{:keys [value offset]}]
(merge
{:background-color (:ui-background @colors/theme)}
(when (and offset platform/android?)
@@ -28,78 +27,66 @@
:shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}})))
(defn title-style
[layout]
{:flex 1
(defn title-style [layout]
{:flex 1
:justify-content :center
:padding-right (get-in layout [:right :width])})
:padding-right (get-in layout [:right :width])})
(defn header-container
[]
(defn header-container []
(let [y (animated/value 0)
animation-value (animated/value 0)
animation (animated/with-timing-transition
animation-value
{:duration 250
:easing (:ease-in animated/easings)})
animation-value
{:duration 250
:easing (:ease-in animated/easings)})
on-scroll (animated/on-scroll {:y y})
layout (reagent/atom {})
offset (reagent/atom 0)
on-layout (fn [evt]
(reset! offset (oget evt "nativeEvent" "layout" "height")))]
(fn [{:keys [extended-header refresh-control refreshing-sub refreshing-counter] :as props} children]
[animated/view
{:flex 1
:pointer-events :box-none}
[animated/code
{:key (str @offset)
:exec (animated/cond*
(animated/and* (animated/greater-or-eq y @offset)
(animated/greater-or-eq y 1))
(animated/set animation-value 1)
(animated/set animation-value 0))}]
[animated/view
{:pointer-events :box-none
:style (header-wrapper-style {:value y
:offset @offset})}
[header/header
(merge
{:get-layout (fn [el l] (swap! layout assoc el l))
:border-bottom false
:title-component [animated/view {:style (title-style @layout)}
[extended-header
{:value y
:animation animation
:minimized true
:offset @offset}]]
:title-align :left}
(dissoc props :extended-header))]]
(into [animated/scroll-view
{:on-scroll on-scroll
:refreshControl (when refresh-control
(refresh-control
(and @refreshing-sub
@refreshing-counter)))
:style {:z-index 1}
:scrollEventThrottle 16}
[animated/view {:flex 1
:pointer-events :box-none}
[animated/code {:key (str @offset)
:exec (animated/cond*
(animated/and* (animated/greater-or-eq y @offset)
(animated/greater-or-eq y 1))
(animated/set animation-value 1)
(animated/set animation-value 0))}]
[animated/view {:pointer-events :box-none
:style (header-wrapper-style {:value y
:offset @offset})}
[header/header (merge
{:get-layout (fn [el l] (swap! layout assoc el l))
:border-bottom false
:title-component [animated/view {:style (title-style @layout)}
[extended-header {:value y
:animation animation
:minimized true
:offset @offset}]]
:title-align :left}
(dissoc props :extended-header))]]
(into [animated/scroll-view {:on-scroll on-scroll
:refreshControl (when refresh-control
(refresh-control
(and @refreshing-sub
@refreshing-counter)))
:style {:z-index 1}
:scrollEventThrottle 16}
[animated/view {:pointer-events :box-none}
[animated/view
{:pointer-events :box-none
:on-layout on-layout}
[extended-header
{:value y
:animation animation
:offset @offset}]]]]
[animated/view {:pointer-events :box-none
:on-layout on-layout}
[extended-header {:value y
:animation animation
:offset @offset}]]]]
children)])))
(defn header
[{:keys [use-insets] :as props} & children]
(defn header [{:keys [use-insets] :as props} & children]
(if use-insets
[safe-area/consumer
(fn [insets]
[header-container
(-> props
(dissoc :use-insets)
(assoc :insets insets))
[header-container (-> props
(dissoc :use-insets)
(assoc :insets insets))
children])]
[header-container props children]))
+7 -8
View File
@@ -15,14 +15,13 @@
:flex 1
:justify-content :flex-end})
(defn backdrop
[]
{:flex 1
:position :absolute
:left 0
:top 0
:right 0
:bottom 0})
(defn backdrop []
{:flex 1
:position :absolute
:left 0
:top 0
:right 0
:bottom 0})
(defn content-container
[window-height]
+110 -127
View File
@@ -1,27 +1,25 @@
(ns quo.components.bottom-sheet.view
(:require [cljs-bean.core :as bean]
(:require [reagent.core :as reagent]
[quo.animated :as animated]
[quo.components.bottom-sheet.style :as styles]
[quo.components.safe-area :as safe-area]
[quo.design-system.colors :as colors]
[quo.gesture-handler :as gesture-handler]
[quo.platform :as platform]
[quo.react :as react]
[quo.react-native :as rn]
[reagent.core :as reagent]))
[quo.react :as react]
[quo.platform :as platform]
[cljs-bean.core :as bean]
[quo.components.safe-area :as safe-area]
[quo.components.bottom-sheet.style :as styles]
[quo.gesture-handler :as gesture-handler]
[quo.design-system.colors :as colors]))
(def opacity-coeff 0.8)
(def close-duration 150)
(def spring-config
{:damping 15
:mass 0.7
:stiffness 150
:overshootClamping false
:restSpeedThreshold 0.1
:restDisplacementThreshold 0.1})
(def spring-config {:damping 15
:mass 0.7
:stiffness 150
:overshootClamping false
:restSpeedThreshold 0.1
:restDisplacementThreshold 0.1})
(defn bottom-sheet-hooks
[props]
(defn bottom-sheet-hooks [props]
(let [{on-cancel :onCancel
disable-drag? :disableDrag?
show-handle? :showHandle?
@@ -33,83 +31,77 @@
backdrop-dismiss? true
back-button-cancel true}}
(bean/bean props)
body-ref (react/create-ref)
body-ref (react/create-ref)
master-ref (react/create-ref)
height (react/state 0)
{window-height :height} (rn/use-window-dimensions)
height (react/state 0)
{window-height :height} (rn/use-window-dimensions)
{:keys [keyboard-shown
keyboard-height]}
(rn/use-keyboard)
keyboard-height-android-delta
(if (and platform/android? keyboard-shown) (+ keyboard-height 20) 0)
safe-area (safe-area/use-safe-area)
window-height (- window-height
(if platform/android?
(+ 50 keyboard-height-android-delta) ;; TODO : remove 50 when
;; react-native-navigation v8 will be
;; implemented
;; https://github.com/wix/react-native-navigation/issues/7225
0))
min-height (+ (* styles/vertical-padding 2) (:bottom safe-area))
max-height (- window-height (:top safe-area))
visible (react/state false)
keyboard-height]} (rn/use-keyboard)
keyboard-height-android-delta (if (and platform/android? keyboard-shown) (+ keyboard-height 20) 0)
safe-area (safe-area/use-safe-area)
window-height (- window-height (if platform/android?
(+ 50 keyboard-height-android-delta) ;; TODO : remove 50 when react-native-navigation v8 will be implemented https://github.com/wix/react-native-navigation/issues/7225
0))
min-height (+ (* styles/vertical-padding 2) (:bottom safe-area))
max-height (- window-height (:top safe-area))
visible (react/state false)
master-translation-y (animated/use-value 0)
master-velocity-y (animated/use-value (:undetermined gesture-handler/states))
master-state (animated/use-value (:undetermined gesture-handler/states))
tap-state (animated/use-value 0)
manual-open (animated/use-value 0)
manual-close (animated/use-value 0)
offset (animated/use-value 0)
drag-over (animated/use-value 1)
clock (animated/use-clock)
tap-gesture-handler (animated/use-gesture {:state tap-state})
on-master-event (animated/use-gesture
{:translationY master-translation-y
:state master-state
:velocityY master-velocity-y})
on-body-event on-master-event
sheet-height (min max-height
(+ styles/border-radius @height))
master-velocity-y (animated/use-value (:undetermined gesture-handler/states))
master-state (animated/use-value (:undetermined gesture-handler/states))
tap-state (animated/use-value 0)
manual-open (animated/use-value 0)
manual-close (animated/use-value 0)
offset (animated/use-value 0)
drag-over (animated/use-value 1)
clock (animated/use-clock)
tap-gesture-handler (animated/use-gesture {:state tap-state})
on-master-event (animated/use-gesture
{:translationY master-translation-y
:state master-state
:velocityY master-velocity-y})
on-body-event on-master-event
sheet-height (min max-height
(+ styles/border-radius @height))
open-snap-point (animated/use-value 0)
open-snap-point (animated/use-value 0)
close-snap-point 0
on-close (fn []
(when @visible
(reset! visible false)
(reset! height 0)
(when on-cancel
(on-cancel))))
close-sheet (fn []
(animated/set-value manual-close 1))
on-snap (fn [pos]
(when (= close-snap-point (aget pos 0))
(on-close)))
interrupted (animated/and* (animated/eq master-state (:began gesture-handler/states))
(animated/clock-running clock))
translate-y (react/use-memo
(fn []
(animated/with-easing
{:value (animated/cond* (animated/less-or-eq master-translation-y 0)
(animated/divide master-translation-y 2)
master-translation-y)
:velocity master-velocity-y
:offset offset
:state master-state
:animation-over drag-over
:snap-points [open-snap-point close-snap-point]}))
[])
opacity (react/use-memo
(fn []
(animated/cond*
open-snap-point
(animated/interpolate
translate-y
{:inputRange [(animated/multiply open-snap-point opacity-coeff) 0]
:outputRange [1 0]
:extrapolate (:clamp animated/extrapolate)})))
[])]
on-close (fn []
(when @visible
(reset! visible false)
(reset! height 0)
(when on-cancel
(on-cancel))))
close-sheet (fn []
(animated/set-value manual-close 1))
on-snap (fn [pos]
(when (= close-snap-point (aget pos 0))
(on-close)))
interrupted (animated/and* (animated/eq master-state (:began gesture-handler/states))
(animated/clock-running clock))
translate-y (react/use-memo
(fn []
(animated/with-easing
{:value (animated/cond* (animated/less-or-eq master-translation-y 0)
(animated/divide master-translation-y 2)
master-translation-y)
:velocity master-velocity-y
:offset offset
:state master-state
:animation-over drag-over
:snap-points [open-snap-point close-snap-point]}))
[])
opacity (react/use-memo
(fn []
(animated/cond*
open-snap-point
(animated/interpolate
translate-y
{:inputRange [(animated/multiply open-snap-point opacity-coeff) 0]
:outputRange [1 0]
:extrapolate (:clamp animated/extrapolate)})))
[])]
(animated/code!
(fn []
(animated/cond* (animated/and* (animated/eq master-state (:end gesture-handler/states))
@@ -185,48 +177,39 @@
(close-sheet)))
[visible?])
(reagent/as-element
[rn/view
{:style styles/container
:pointer-events :box-none}
[gesture-handler/tap-gesture-handler
(merge {:enabled backdrop-dismiss?}
tap-gesture-handler)
[animated/view
{:style (merge (styles/backdrop)
(when platform/ios?
{:opacity opacity
:background-color (:backdrop @colors/theme)}))}]]
[animated/view
{:style (merge (styles/content-container window-height)
{:transform [{:translateY (if (= sheet-height max-height)
(animated/add translate-y
keyboard-height-android-delta)
translate-y)}
{:translateY (* window-height 2)}]})}
[gesture-handler/pan-gesture-handler
(merge on-master-event
{:ref master-ref
:wait-for body-ref
:enabled (not disable-drag?)})
[animated/view {:style styles/content-header}
[rn/view {:style styles/container
:pointer-events :box-none}
[gesture-handler/tap-gesture-handler (merge {:enabled backdrop-dismiss?}
tap-gesture-handler)
[animated/view {:style (merge (styles/backdrop)
(when platform/ios?
{:opacity opacity
:background-color (:backdrop @colors/theme)}))}]]
[animated/view {:style (merge (styles/content-container window-height)
{:transform [{:translateY (if (= sheet-height max-height)
(animated/add translate-y keyboard-height-android-delta)
translate-y)}
{:translateY (* window-height 2)}]})}
[gesture-handler/pan-gesture-handler (merge on-master-event
{:ref master-ref
:wait-for body-ref
:enabled (not disable-drag?)})
[animated/view {:style styles/content-header}
(when show-handle?
[rn/view {:style styles/handle}])]]
[gesture-handler/pan-gesture-handler
(merge on-body-event
{:ref body-ref
:wait-for master-ref
:enabled (and (not disable-drag?)
(not= sheet-height max-height))})
[animated/view
{:height sheet-height
:flex 1}
[animated/view
{:style {:padding-top styles/vertical-padding
:padding-bottom (+ styles/vertical-padding
(if (and platform/ios? keyboard-shown)
keyboard-height
(:bottom safe-area)))}
:on-layout #(reset! height (.-nativeEvent.layout.height ^js %))}
[gesture-handler/pan-gesture-handler (merge on-body-event
{:ref body-ref
:wait-for master-ref
:enabled (and (not disable-drag?)
(not= sheet-height max-height))})
[animated/view {:height sheet-height
:flex 1}
[animated/view {:style {:padding-top styles/vertical-padding
:padding-bottom (+ styles/vertical-padding
(if (and platform/ios? keyboard-shown)
keyboard-height
(:bottom safe-area)))}
:on-layout #(reset! height (.-nativeEvent.layout.height ^js %))}
(into [:<>]
(react/get-children children))]]]]])))
+48 -54
View File
@@ -1,14 +1,14 @@
(ns quo.components.button.view
(:require [quo.components.animated.pressable :as animation]
[quo.components.text :as text] ;; FIXME:
[quo.react-native :as rn]
[quo.haptic :as haptic]
[quo.design-system.colors :as colors]
[quo.design-system.spacing :as spacing]
[quo.haptic :as haptic]
[quo.react-native :as rn]
[quo.components.text :as text]
;; FIXME:
[status-im.ui.components.icons.icons :as icons]))
(defn style-container
[type]
(defn style-container [type]
(merge {:height 44
:align-items :center
:justify-content :center
@@ -19,16 +19,14 @@
:icon {}
nil)))
(defn content-style
[type]
(defn content-style [type]
(case type
:primary (:base spacing/padding-horizontal)
:secondary (:x-tiny spacing/padding-horizontal)
:icon (:tiny spacing/padding-horizontal)
nil))
(defn themes
[theme]
(defn themes [theme]
(case theme
:main {:icon-color (:icon-04 @colors/theme)
:background-color (:interactive-02 @colors/theme)
@@ -56,46 +54,44 @@
:text-color (:text-01 @colors/theme)
:border-color (:ui-01 @colors/theme)}))
(defn button
[{:keys [on-press disabled type theme before after
haptic-feedback haptic-type on-long-press on-press-start
accessibility-label loading border-radius style test-ID]
:or {theme :main
type :primary
haptic-feedback true
border-radius 8
haptic-type :selection}}
children]
(let [theme' (cond
disabled :disabled
:else theme)
(defn button [{:keys [on-press disabled type theme before after
haptic-feedback haptic-type on-long-press on-press-start
accessibility-label loading border-radius style test-ID]
:or {theme :main
type :primary
haptic-feedback true
border-radius 8
haptic-type :selection}}
children]
(let [theme' (cond
disabled :disabled
:else theme)
{:keys [icon-color background-color text-color border-color]}
(themes theme')
optional-haptic (fn []
(when haptic-feedback
(haptic/trigger haptic-type)))]
[animation/pressable
(merge {:bg-color background-color
:border-radius border-radius
:type type
:disabled disabled
:accessibility-label accessibility-label}
(when border-color
{:border-color border-color
:border-width 1})
(when on-press
{:on-press (fn []
(optional-haptic)
(on-press))})
(when on-long-press
{:on-long-press (fn []
(optional-haptic)
(on-long-press))})
(when on-press-start
{:on-press-start (fn []
(optional-haptic)
(on-press-start))}))
optional-haptic (fn []
(when haptic-feedback
(haptic/trigger haptic-type)))]
[animation/pressable (merge {:bg-color background-color
:border-radius border-radius
:type type
:disabled disabled
:accessibility-label accessibility-label}
(when border-color
{:border-color border-color
:border-width 1})
(when on-press
{:on-press (fn []
(optional-haptic)
(on-press))})
(when on-long-press
{:on-long-press (fn []
(optional-haptic)
(on-long-press))})
(when on-press-start
{:on-press-start (fn []
(optional-haptic)
(on-press-start))}))
[rn/view {:test-ID test-ID :style (merge (style-container type) style)}
(when before
[rn/view
@@ -103,19 +99,17 @@
(when loading
[rn/view {:style {:position :absolute}}
[rn/activity-indicator]])
[rn/view
{:style (merge (content-style type)
(when loading
{:opacity 0}))}
[rn/view {:style (merge (content-style type)
(when loading
{:opacity 0}))}
(cond
(= type :icon)
[icons/icon children {:color icon-color}]
(string? children)
[text/text
{:weight :medium
:number-of-lines 1
:style {:color text-color}}
[text/text {:weight :medium
:number-of-lines 1
:style {:color text-color}}
children]
(vector? children)
+10 -18
View File
@@ -2,8 +2,7 @@
(:require [quo.animated :as animated]
[quo.design-system.colors :as colors]))
(defn switch-style
[state disabled]
(defn switch-style [state disabled]
{:width 52
:height 28
:border-radius 14
@@ -14,8 +13,7 @@
(:interactive-04 @colors/theme)
(:interactive-01 @colors/theme)))})
(defn switch-bullet-style
[state hold]
(defn switch-bullet-style [state hold]
{:width 20
:height 20
:border-radius 10
@@ -28,8 +26,7 @@
:shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}})
(defn radio-style
[state disabled]
(defn radio-style [state disabled]
{:width 20
:height 20
:border-radius 10
@@ -40,8 +37,7 @@
(:interactive-04 @colors/theme)
(:interactive-01 @colors/theme)))})
(defn radio-bullet-style
[state hold]
(defn radio-bullet-style [state hold]
{:width 12
:height 12
:border-radius 6
@@ -54,8 +50,7 @@
:shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}})
(defn animated-checkbox-style
[state disabled]
(defn animated-checkbox-style [state disabled]
{:width 18
:height 18
:border-radius 4
@@ -67,8 +62,7 @@
(:interactive-04 @colors/theme)
(:interactive-01 @colors/theme)))})
(defn checkbox-style
[value disabled]
(defn checkbox-style [value disabled]
{:width 18
:height 18
:border-radius 4
@@ -80,11 +74,9 @@
(:interactive-01 @colors/theme))
(:ui-01 @colors/theme))})
(defn animated-check-icon-style
[state hold]
{:opacity (animated/mix hold 1 0.6)
:transform [{:scale (animated/mix state 0.0001 1)}]})
(defn animated-check-icon-style [state hold]
{:opacity (animated/mix hold 1 0.6)
:transform [{:scale (animated/mix state 0.0001 1)}]})
(defn check-icon-style
[value]
(defn check-icon-style [value]
{:opacity (if value 1 0)})
+42 -54
View File
@@ -1,37 +1,34 @@
(ns quo.components.controls.view
(:require [cljs-bean.core :as bean]
[quo.animated :as animated]
[quo.components.controls.styles :as styles]
[quo.design-system.colors :as colors]
[quo.gesture-handler :as gh]
(:require [reagent.core :as reagent]
[cljs-bean.core :as bean]
[quo.react :as react]
[quo.react-native :as rn]
[reagent.core :as reagent]
[quo.animated :as animated]
[quo.gesture-handler :as gh]
[quo.design-system.colors :as colors]
[quo.components.controls.styles :as styles]
[status-im.ui.components.icons.icons :as icons]))
(defn control-builder
[component]
(defn control-builder [component]
(fn [props]
(let [{:keys [value onChange disabled]}
(bean/bean props)
state (animated/use-value 0)
tap-state (animated/use-value (:undetermined gh/states))
tap-handler (animated/on-gesture {:state tap-state})
hold (react/use-memo
(fn []
(animated/with-timing-transition
(animated/eq tap-state (:began gh/states))
{}))
[])
transition (react/use-memo
(fn []
(animated/with-spring-transition state
(:lazy
animated/springs)))
[])
press-end (fn []
(when (and (not disabled) onChange)
(onChange (not value))))]
state (animated/use-value 0)
tap-state (animated/use-value (:undetermined gh/states))
tap-handler (animated/on-gesture {:state tap-state})
hold (react/use-memo
(fn []
(animated/with-timing-transition
(animated/eq tap-state (:began gh/states))
{}))
[])
transition (react/use-memo
(fn []
(animated/with-spring-transition state (:lazy animated/springs)))
[])
press-end (fn []
(when (and (not disabled) onChange)
(onChange (not value))))]
(animated/code!
(fn []
(animated/cond* (animated/eq tap-state (:end gh/states))
@@ -44,47 +41,38 @@
(animated/set state (if (true? value) 1 0)))
[value])
(reagent/as-element
[gh/tap-gesture-handler
(merge tap-handler
{:shouldCancelWhenOutside true
:enabled (boolean (and onChange (not disabled)))})
[gh/tap-gesture-handler (merge tap-handler
{:shouldCancelWhenOutside true
:enabled (boolean (and onChange (not disabled)))})
[animated/view
[component
{:transition transition
:hold hold
:disabled disabled}]]]))))
[component {:transition transition
:hold hold
:disabled disabled}]]]))))
(defn switch-view
[{:keys [transition hold disabled value]}]
[animated/view
{:style (styles/switch-style transition disabled)
:accessibility-label (str "switch-" (if value "on" "off"))
:accessibility-role :switch}
(defn switch-view [{:keys [transition hold disabled value]}]
[animated/view {:style (styles/switch-style transition disabled)
:accessibility-label (str "switch-" (if value "on" "off"))
:accessibility-role :switch}
[animated/view {:style (styles/switch-bullet-style transition hold)}]])
(defn radio-view
[{:keys [transition hold disabled value]}]
[animated/view
{:style (styles/radio-style transition disabled)
:accessibility-label (str "radio-" (if value "on" "off"))
:accessibility-role :radio}
(defn radio-view [{:keys [transition hold disabled value]}]
[animated/view {:style (styles/radio-style transition disabled)
:accessibility-label (str "radio-" (if value "on" "off"))
:accessibility-role :radio}
[animated/view {:style (styles/radio-bullet-style transition hold)}]])
(defn checkbox-view
[props]
(defn checkbox-view [props]
(let [{:keys [value onChange disabled]} (bean/bean props)]
(reagent/as-element
[rn/touchable-without-feedback
{:on-press (when (and onChange (not disabled)) onChange)}
[rn/view
{:style (styles/checkbox-style value disabled)
:accessibility-label (str "checkbox-" (if value "on" "off"))
:accessibility-role :checkbox}
[rn/view {:style (styles/checkbox-style value disabled)
:accessibility-label (str "checkbox-" (if value "on" "off"))
:accessibility-role :checkbox}
[rn/view {:style (styles/check-icon-style value)}
[icons/tiny-icon :tiny-icons/tiny-check {:color colors/white}]]]])))
(defn animated-checkbox-view
[{:keys [transition hold disabled value]}]
(defn animated-checkbox-view [{:keys [transition hold disabled value]}]
[animated/view
{:style (styles/animated-checkbox-style transition disabled)
:accessibility-label (str "checkbox-" (if value "on" "off"))
+93 -123
View File
@@ -10,8 +10,7 @@
(def header-height 56)
(defn header-wrapper-style
[{:keys [height border-bottom background]}]
(defn header-wrapper-style [{:keys [height border-bottom background]}]
(merge
(:x-tiny spacing/padding-horizontal)
{:background-color (:ui-background @colors/theme)
@@ -22,37 +21,32 @@
{:border-bottom-width 1
:border-bottom-color (:ui-01 @colors/theme)})))
(def absolute-fill
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def absolute-fill {:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def content
{:flex 1
:flex-direction :row
:align-items :center
:justify-content :center})
(def content {:flex 1
:flex-direction :row
:align-items :center
:justify-content :center})
(def left
{:position :absolute
:left 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-start})
(def left {:position :absolute
:left 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-start})
(def right
{:position :absolute
:right 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-end})
(def right {:position :absolute
:right 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-end})
(defn title-style
[{:keys [left right]} title-align]
(defn title-style [{:keys [left right]} title-align]
(merge
absolute-fill
(case title-align
@@ -65,75 +59,66 @@
(def header-actions-style
(merge
{:flex 1
:flex-direction :row
:align-items :center
:justify-content :center}
{:flex 1
:flex-direction :row
:align-items :center
:justify-content :center}
(:x-tiny spacing/padding-horizontal)))
(def header-action-placeholder
{:width (:base spacing/spacing)})
(def element
{:align-items :center
:justify-content :center
:flex 1})
(def element {:align-items :center
:justify-content :center
:flex 1})
(defn header-action
[{:keys [icon label on-press disabled accessibility-label]}]
[button/button
(merge {:on-press on-press
:disabled disabled}
(cond
icon {:type :icon
:theme :icon}
label {:type :secondary})
(when accessibility-label
{:accessibility-label accessibility-label}))
(defn header-action [{:keys [icon label on-press disabled accessibility-label]}]
[button/button (merge {:on-press on-press
:disabled disabled}
(cond
icon {:type :icon
:theme :icon}
label {:type :secondary})
(when accessibility-label
{:accessibility-label accessibility-label}))
(cond
icon icon
label label)])
(defn header-actions
[{:keys [accessories component]}]
(defn header-actions [{:keys [accessories component]}]
[rn/view {:style element}
(cond
(seq accessories)
(into [rn/view {:style header-actions-style}]
(map header-action accessories))
component component
component component
:else
[rn/view {:style header-action-placeholder}])])
(defn header-title
[{:keys [title subtitle component title-align]}]
(defn header-title [{:keys [title subtitle component title-align]}]
[:<>
(cond
component component
component component
(and title subtitle)
[:<>
[text/text
{:weight :medium
:number-of-lines 1}
[text/text {:weight :medium
:number-of-lines 1}
title]
[text/text
{:weight :regular
:color :secondary
:number-of-lines 1}
[text/text {:weight :regular
:color :secondary
:number-of-lines 1}
subtitle]]
title [text/text
{:weight :bold
:number-of-lines 0
:align title-align
:size :large}
title])])
title [text/text {:weight :bold
:number-of-lines 0
:align title-align
:size :large}
title])])
(defn header
[{:keys [left-width right-width]}]
(defn header [{:keys [left-width right-width]}]
(let [layout (reagent/atom {:left {:width (or left-width 8)
:height header-height}
:right {:width (or right-width 8)
@@ -145,61 +130,46 @@
(let [width (oget evt "nativeEvent" "layout" "width")
height (oget evt "nativeEvent" "layout" "height")]
(when get-layout
(get-layout el
{:width width
:height height}))
(swap! layout assoc
el
{:width width
:height height}))))]
(fn
[{:keys [left-accessories left-component border-bottom
right-accessories right-component insets get-layout
title subtitle title-component style title-align
background]
:or {title-align :center
border-bottom true}}]
(get-layout el {:width width
:height height}))
(swap! layout assoc el {:width width
:height height}))))]
(fn [{:keys [left-accessories left-component border-bottom
right-accessories right-component insets get-layout
title subtitle title-component style title-align
background]
:or {title-align :center
border-bottom true}}]
(let [status-bar-height (get insets :top 0)
height (+ header-height status-bar-height)]
[animated/view
{:style (header-wrapper-style {:height height
:background background
:border-bottom border-bottom})}
[rn/view
{:pointer-events :box-none
:height status-bar-height}]
[rn/view
{:style (merge {:height header-height}
style)
:pointer-events :box-none}
[rn/view
{:style absolute-fill
:pointer-events :box-none}
[rn/view
{:style content
:pointer-events :box-none}
[rn/view
{:style left
:on-layout (handle-layout :left get-layout)
:pointer-events :box-none}
[header-actions
{:accessories left-accessories
:component left-component}]]
[animated/view {:style (header-wrapper-style {:height height
:background background
:border-bottom border-bottom})}
[rn/view {:pointer-events :box-none
:height status-bar-height}]
[rn/view {:style (merge {:height header-height}
style)
:pointer-events :box-none}
[rn/view {:style absolute-fill
:pointer-events :box-none}
[rn/view {:style content
:pointer-events :box-none}
[rn/view {:style left
:on-layout (handle-layout :left get-layout)
:pointer-events :box-none}
[header-actions {:accessories left-accessories
:component left-component}]]
[rn/view
{:style (title-style @layout title-align)
:on-layout (handle-layout :title get-layout)
:pointer-events :box-none}
[header-title
{:title title
:subtitle subtitle
:title-align title-align
:component title-component}]]
[rn/view {:style (title-style @layout title-align)
:on-layout (handle-layout :title get-layout)
:pointer-events :box-none}
[header-title {:title title
:subtitle subtitle
:title-align title-align
:component title-component}]]
[rn/view
{:style right
:on-layout (handle-layout :right get-layout)
:pointer-events :box-none}
[header-actions
{:accessories right-accessories
:component right-component}]]]]]]))))
[rn/view {:style right
:on-layout (handle-layout :right get-layout)
:pointer-events :box-none}
[header-actions {:accessories right-accessories
:component right-component}]]]]]]))))
+7 -10
View File
@@ -1,17 +1,14 @@
(ns quo.components.list.footer
(:require [quo.components.text :as text]
(:require [quo.react-native :as rn]
[quo.design-system.spacing :as spacing]
[quo.react-native :as rn]
[quo.components.text :as text]
[reagent.core :as reagent]))
(defn footer
[]
(let [this (reagent/current-component)
(defn footer []
(let [this (reagent/current-component)
{:keys [color]
:or {color :secondary}}
(reagent/props this)]
[rn/view
{:style (merge (:base spacing/padding-horizontal)
(:small spacing/padding-vertical))}
:or {color :secondary}} (reagent/props this)]
[rn/view {:style (merge (:base spacing/padding-horizontal)
(:small spacing/padding-vertical))}
(into [text/text {:color color}]
(reagent/children this))]))
+10 -14
View File
@@ -1,19 +1,15 @@
(ns quo.components.list.header
(:require [quo.components.text :as text]
[quo.design-system.spacing :as spacing]
(:require [reagent.core :as reagent]
[quo.react-native :as rn]
[reagent.core :as reagent]))
[quo.design-system.spacing :as spacing]
[quo.components.text :as text]))
(defn header
[]
(let [this (reagent/current-component)
(defn header []
(let [this (reagent/current-component)
{:keys [color]
:or {color :secondary}}
(reagent/props this)]
[rn/view
{:style (merge (:base spacing/padding-horizontal)
(:x-tiny spacing/padding-vertical))}
(into [text/text
{:color color
:style {:margin-top 10}}]
:or {color :secondary}} (reagent/props this)]
[rn/view {:style (merge (:base spacing/padding-horizontal)
(:x-tiny spacing/padding-vertical))}
(into [text/text {:color color
:style {:margin-top 10}}]
(reagent/children this))]))
+12 -14
View File
@@ -1,18 +1,16 @@
(ns quo.components.list.index
(:require [quo.components.text :as text]
[quo.design-system.colors :as colors]
[quo.react-native :as rn]))
(:require [quo.react-native :as rn]
[quo.components.text :as text]
[quo.design-system.colors :as colors]))
(defn index
[{:keys [title]}]
(defn index [{:keys [title]}]
[rn/view {:style {:padding-right 16}}
[rn/view
{:style {:border-top-width 1
:border-bottom-width 1
:border-right-width 1
:border-color (colors/get-color :border-01)
:padding-vertical 3
:padding-horizontal 16
:border-top-right-radius 16
:border-bottom-right-radius 16}}
[rn/view {:style {:border-top-width 1
:border-bottom-width 1
:border-right-width 1
:border-color (colors/get-color :border-01)
:padding-vertical 3
:padding-horizontal 16
:border-top-right-radius 16
:border-bottom-right-radius 16}}
[text/text title]]])
+154 -186
View File
@@ -1,29 +1,28 @@
(ns quo.components.list.item
(:require [quo.components.animated.pressable :as animated]
[quo.components.controls.view :as controls]
[quo.components.text :as text]
[quo.components.tooltip :as tooltip]
[quo.design-system.colors :as colors]
[quo.design-system.spacing :as spacing]
[quo.gesture-handler :as gh]
[quo.haptic :as haptic]
(:require [quo.react-native :as rn]
[quo.platform :as platform]
[quo.react-native :as rn]
[status-im.ui.components.icons.icons :as icons]))
[quo.haptic :as haptic]
[quo.gesture-handler :as gh]
[quo.design-system.spacing :as spacing]
[quo.design-system.colors :as colors]
[quo.components.text :as text]
[quo.components.controls.view :as controls]
[quo.components.tooltip :as tooltip]
[status-im.ui.components.icons.icons :as icons]
[quo.components.animated.pressable :as animated]))
(defn themes
[theme]
(defn themes [theme]
(case theme
:main {:icon-color (:icon-04 @colors/theme)
:icon-bg-color (:interactive-02 @colors/theme)
:active-background (:interactive-02 @colors/theme)
:passive-background (:ui-background @colors/theme)
:text-color (:text-01 @colors/theme)}
:accent {:icon-color (:icon-04 @colors/theme)
:icon-bg-color (:interactive-02 @colors/theme)
:active-background (:interactive-02 @colors/theme)
:passive-background (:ui-background @colors/theme)
:text-color (:text-04 @colors/theme)}
:main {:icon-color (:icon-04 @colors/theme)
:icon-bg-color (:interactive-02 @colors/theme)
:active-background (:interactive-02 @colors/theme)
:passive-background (:ui-background @colors/theme)
:text-color (:text-01 @colors/theme)}
:accent {:icon-color (:icon-04 @colors/theme)
:icon-bg-color (:interactive-02 @colors/theme)
:active-background (:interactive-02 @colors/theme)
:passive-background (:ui-background @colors/theme)
:text-color (:text-04 @colors/theme)}
:negative {:icon-color (:negative-01 @colors/theme)
:icon-bg-color (:negative-02 @colors/theme)
:active-background (:negative-02 @colors/theme)
@@ -40,34 +39,28 @@
:passive-background (:ui-background @colors/theme)
:text-color (:text-02 @colors/theme)}))
(defn size->icon-size
[size]
(defn size->icon-size [size]
(case size
:small 36
40))
(defn size->container-size
[size]
(defn size->container-size [size]
(case size
:small 52
64))
(defn size->single-title-size
[size]
(defn size->single-title-size [size]
(case size
:small :base
:large))
(defn container
[{:keys [size container-style]} & children]
(into [rn/view
{:style (merge (:tiny spacing/padding-horizontal)
{:min-height (size->container-size size)
:padding-vertical 8
:flex-direction :row
:align-items :center
:justify-content :space-between}
container-style)}]
(defn container [{:keys [size container-style]} & children]
(into [rn/view {:style (merge (:tiny spacing/padding-horizontal)
{:min-height (size->container-size size)
:padding-vertical 8
:flex-direction :row
:align-items :center
:justify-content :space-between} container-style)}]
children))
(defn icon-column
@@ -79,118 +72,103 @@
(vector? icon)
icon
(keyword? icon)
[rn/view
{:style {:width icon-size
:height icon-size
:align-items :center
:justify-content :center
:border-radius (/ icon-size 2)
:background-color icon-bg-color}}
[rn/view {:style {:width icon-size
:height icon-size
:align-items :center
:justify-content :center
:border-radius (/ icon-size 2)
:background-color icon-bg-color}}
[icons/icon icon {:color icon-color}]])])))
(defn title-column
[{:keys [title text-color subtitle subtitle-max-lines subtitle-secondary
title-accessibility-label size text-size title-text-weight
right-side-present?]}]
[rn/view
{:style (merge (:tiny spacing/padding-horizontal)
;; make left-side title grow if nothing is present on right-side
(when-not right-side-present?
{:flex 1
:justify-content :center}))}
[rn/view {:style (merge (:tiny spacing/padding-horizontal)
;; make left-side title grow if nothing is present on right-side
(when-not right-side-present?
{:flex 1
:justify-content :center}))}
(cond
(and title subtitle)
[:<>
;; FIXME(Ferossgp): ReactNative 63 will support view inside text on andrid, remove thess if when
;; migrating
;; FIXME(Ferossgp): ReactNative 63 will support view inside text on andrid, remove thess if when migrating
(if (string? title)
[text/text
{:weight (or title-text-weight :medium)
:style {:color text-color}
:accessibility-label title-accessibility-label
:ellipsize-mode :tail
:number-of-lines 1
:size text-size}
[text/text {:weight (or title-text-weight :medium)
:style {:color text-color}
:accessibility-label title-accessibility-label
:ellipsize-mode :tail
:number-of-lines 1
:size text-size}
title]
title)
(if (string? subtitle-secondary)
[rn/view {:flex-direction :row}
[text/text
{:style {:max-width "56.5%"}
:weight :regular
:color :secondary
:ellipsize-mode :tail
:number-of-lines subtitle-max-lines
:size text-size}
[text/text {:style {:max-width "56.5%"}
:weight :regular
:color :secondary
:ellipsize-mode :tail
:number-of-lines subtitle-max-lines
:size text-size}
subtitle]
[text/text
{:style {:width "7%" :text-align :center}
:weight :regular
:color :secondary
:ellipsize-mode :middle
:number-of-lines subtitle-max-lines
:size text-size}
[text/text {:style {:width "7%" :text-align :center}
:weight :regular
:color :secondary
:ellipsize-mode :middle
:number-of-lines subtitle-max-lines
:size text-size}
"•"]
[text/text
{:style {:max-width "36.5%"}
:weight :regular
:color :secondary
:ellipsize-mode :middle
:number-of-lines subtitle-max-lines
:size text-size}
[text/text {:style {:max-width "36.5%"}
:weight :regular
:color :secondary
:ellipsize-mode :middle
:number-of-lines subtitle-max-lines
:size text-size}
subtitle-secondary]]
(if (string? subtitle)
[text/text
{:weight :regular
:color :secondary
:ellipsize-mode :tail
:number-of-lines subtitle-max-lines
:size text-size}
[text/text {:weight :regular
:color :secondary
:ellipsize-mode :tail
:number-of-lines subtitle-max-lines
:size text-size}
subtitle]
subtitle))]
title
(if (string? title)
[text/text
{:weight (or title-text-weight :regular)
:number-of-lines 1
:style {:color text-color}
:title-accessibility-label title-accessibility-label
:ellipsize-mode :tail
:size (or text-size (size->single-title-size size))}
[text/text {:weight (or title-text-weight :regular)
:number-of-lines 1
:style {:color text-color}
:title-accessibility-label title-accessibility-label
:ellipsize-mode :tail
:size (or text-size (size->single-title-size size))}
title]
title))])
(defn left-side
[props]
[rn/view
{:style {:flex-direction :row
;; Occupy only content width, never grow, but shrink if need be
:flex-grow 0
:flex-shrink 1
:padding-right 16
:align-items (or (:left-side-alignment props) :center)}}
(defn left-side [props]
[rn/view {:style {:flex-direction :row
;; Occupy only content width, never grow, but shrink if need be
:flex-grow 0
:flex-shrink 1
:padding-right 16
:align-items (or (:left-side-alignment props) :center)}}
[icon-column props]
[title-column props]])
(defn right-side
[{:keys [chevron active disabled accessory accessory-text accessory-style animated-accessory?]}]
(defn right-side [{:keys [chevron active disabled accessory accessory-text accessory-style animated-accessory?]}]
(when (or chevron accessory)
[rn/view
{:style (merge {:align-items :center
:justify-content :flex-end
:flex-direction :row
;; Grow to occupy full space, shrink when need be, but always maitaining 16px left
;; gutter
:flex-grow 1
:flex-shrink 0
:margin-left 16
;; When the left-side leaves no room for right-side, the rendered element is pushed
;; out. A flex-basis ensures that there is some room reserved.
;; The number 80px was determined by trial and error.
:flex-basis 80}
accessory-style)}
[rn/view {:style (merge {:align-items :center
:justify-content :flex-end
:flex-direction :row
;; Grow to occupy full space, shrink when need be, but always maitaining 16px left gutter
:flex-grow 1
:flex-shrink 0
:margin-left 16
;; When the left-side leaves no room for right-side, the rendered element is pushed out. A flex-basis ensures that there is some room reserved.
;; The number 80px was determined by trial and error.
:flex-basis 80}
accessory-style)}
[rn/view {:style (:tiny spacing/padding-horizontal)}
(case accessory
:radio [controls/radio {:value active :disabled disabled}]
@@ -199,28 +177,25 @@
controls/checkbox)
{:value active :disabled disabled}]
:switch [controls/switch {:value active :disabled disabled}]
:text [text/text
{:color :secondary
:ellipsize-mode :middle
:number-of-lines 1}
:text [text/text {:color :secondary
:ellipsize-mode :middle
:number-of-lines 1}
accessory-text]
accessory)]
(when (and chevron platform/ios?)
[rn/view {:style {:padding-right (:tiny spacing/spacing)}}
[icons/icon :main-icons/next
{:container-style {:opacity 0.4
:align-items :center
:justify-content :center}
:resize-mode :center
:color (:icon-02 @colors/theme)}]])]))
[icons/icon :main-icons/next {:container-style {:opacity 0.4
:align-items :center
:justify-content :center}
:resize-mode :center
:color (:icon-02 @colors/theme)}]])]))
(defn list-item
[{:keys [theme accessory disabled subtitle-max-lines icon icon-container-style
left-side-alignment icon-color icon-bg-color
title subtitle subtitle-secondary active on-press on-long-press chevron size text-size
accessory-text accessibility-label title-accessibility-label accessory-style
haptic-feedback haptic-type error animated animated-accessory? title-text-weight
container-style
haptic-feedback haptic-type error animated animated-accessory? title-text-weight container-style
active-background-enabled background-color]
:or {subtitle-max-lines 1
theme :main
@@ -228,29 +203,26 @@
animated platform/ios?
active-background-enabled true
haptic-type :selection}}]
(let [theme (if disabled :disabled theme)
(let [theme (if disabled :disabled theme)
{:keys [text-color active-background passive-background]}
(themes theme)
icon-color (or icon-color
(:icon-color (themes theme)))
icon-bg-color (or icon-bg-color
(:icon-bg-color (themes theme)))
optional-haptic (fn []
(when haptic-feedback
(haptic/trigger haptic-type)))
component (cond
(and (not on-press)
(not on-long-press))
rn/view
animated animated/pressable
:else gh/touchable-highlight)]
[rn/view
{:background-color (cond (not= background-color nil)
background-color
(and (= accessory :radio) active)
active-background
:else
passive-background)}
icon-color (or icon-color (:icon-color (themes theme)))
icon-bg-color (or icon-bg-color (:icon-bg-color (themes theme)))
optional-haptic (fn []
(when haptic-feedback
(haptic/trigger haptic-type)))
component (cond
(and (not on-press)
(not on-long-press))
rn/view
animated animated/pressable
:else gh/touchable-highlight)]
[rn/view {:background-color (cond (not= background-color nil)
background-color
(and (= accessory :radio) active)
active-background
:else
passive-background)}
[component
(merge {:type :list-item
:disabled disabled
@@ -265,39 +237,35 @@
(optional-haptic)
(on-long-press))}))
[container {:size size :container-style container-style}
[left-side
{:icon-color icon-color
:text-color (if on-press
text-color
(:text-color (themes :main)))
:left-side-alignment left-side-alignment
:icon-bg-color icon-bg-color
:title-accessibility-label title-accessibility-label
:icon icon
:icon-container-style icon-container-style
:title title
:title-text-weight title-text-weight
:size size
:text-size text-size
:subtitle subtitle
:subtitle-max-lines subtitle-max-lines
:subtitle-secondary subtitle-secondary
:right-side-present? (or accessory chevron)}]
[right-side
{:chevron chevron
:active active
:disabled disabled
:on-press on-press
:accessory-text accessory-text
:animated-accessory? animated-accessory?
:accessory-style accessory-style
:accessory accessory}]]]
[left-side {:icon-color icon-color
:text-color (if on-press
text-color
(:text-color (themes :main)))
:left-side-alignment left-side-alignment
:icon-bg-color icon-bg-color
:title-accessibility-label title-accessibility-label
:icon icon
:icon-container-style icon-container-style
:title title
:title-text-weight title-text-weight
:size size
:text-size text-size
:subtitle subtitle
:subtitle-max-lines subtitle-max-lines
:subtitle-secondary subtitle-secondary
:right-side-present? (or accessory chevron)}]
[right-side {:chevron chevron
:active active
:disabled disabled
:on-press on-press
:accessory-text accessory-text
:animated-accessory? animated-accessory?
:accessory-style accessory-style
:accessory accessory}]]]
(when error
[tooltip/tooltip
(merge {:bottom-value 0}
(when accessibility-label
{:accessibility-label (str (name accessibility-label) "-error")}))
[text/text
{:color :negative
:size :small}
[tooltip/tooltip (merge {:bottom-value 0}
(when accessibility-label
{:accessibility-label (str (name accessibility-label) "-error")}))
[text/text {:color :negative
:size :small}
error]])]))
+4 -6
View File
@@ -1,21 +1,19 @@
(ns quo.components.safe-area
(:require ["react-native-safe-area-context" :as safe-area-context :refer
(SafeAreaView SafeAreaProvider SafeAreaInsetsContext useSafeAreaInsets)]
(:require ["react-native-safe-area-context" :as safe-area-context
:refer (SafeAreaView SafeAreaProvider SafeAreaInsetsContext useSafeAreaInsets)]
[reagent.core :as reagent]))
(def provider (reagent/adapt-react-class SafeAreaProvider))
(def ^:private consumer-raw (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext)))
(def view (reagent/adapt-react-class SafeAreaView))
(defn consumer
[component]
(defn consumer [component]
[consumer-raw
(fn [insets]
(reagent/as-element
[component (js->clj insets :keywordize-keys true)]))])
(defn use-safe-area
[]
(defn use-safe-area []
(let [insets (useSafeAreaInsets)]
{:top (.-top ^js insets)
:bottom (.-bottom ^js insets)
+3 -4
View File
@@ -1,9 +1,8 @@
(ns quo.components.separator
(:require [quo.design-system.colors :as colors]
[quo.react-native :as react]))
(:require [quo.react-native :as react]
[quo.design-system.colors :as colors]))
(defn separator
[{:keys [color style]}]
(defn separator [{:keys [color style]}]
[react/view
{:style
(merge
+6 -10
View File
@@ -5,8 +5,7 @@
[quo.react-native :as rn]
[reagent.core :as reagent]))
(defn text-style
[{:keys [size align weight monospace color style]}]
(defn text-style [{:keys [size align weight monospace color style]}]
;; NOTE(Ferossgo): or in destructoring will keep nil as a value
(merge (if monospace
;; TODO(Ferossgp): Add all weights for monospace
@@ -39,15 +38,12 @@
{:text-align (or align :auto)}
style))
(defn text
[]
(defn text []
(let [this (reagent/current-component)
props (reagent/props this)
component (if (:animated? props) animated/text rn/text)]
(into [component
(merge {:style (text-style props)}
(dissoc props
:style :size
:weight :color
:align :animated?))]
(into [component (merge {:style (text-style props)}
(dissoc props
:style :size :weight :color
:align :animated?))]
(reagent/children this))))
+121 -160
View File
@@ -1,15 +1,16 @@
(ns quo.components.text-input
(:require [clojure.spec.alpha :as s]
[oops.core :refer [ocall]]
[quo.components.text :as text]
[quo.components.tooltip :as tooltip]
[quo.design-system.colors :as colors]
[quo.design-system.spacing :as spacing]
[quo.design-system.typography :as typography]
[quo.platform :as platform]
[quo.react-native :as rn] ;; TODO(Ferossgp): Move icon component to lib
[reagent.core :as reagent]
[status-im.ui.components.icons.icons :as icons]))
[oops.core :refer [ocall]]
[quo.react-native :as rn]
;; TODO(Ferossgp): Move icon component to lib
[status-im.ui.components.icons.icons :as icons]
[quo.components.tooltip :as tooltip]
[quo.platform :as platform]
[quo.design-system.typography :as typography]
[quo.design-system.spacing :as spacing]
[quo.design-system.colors :as colors]
[quo.components.text :as text]))
;; NOTE(Ferossgp): Refactor with hooks when available
;; We track all currently mounted text input refs
@@ -21,26 +22,19 @@
(s/def ::multiline boolean?)
(s/def ::secure-text-entry boolean?)
(s/def ::show-cancel boolean?)
(s/def ::label
(s/nilable (s/or :string string?
:component vector?)))
(s/def ::label (s/nilable (s/or :string string? :component vector?)))
(s/def ::cancel-label (s/nilable string?))
(s/def ::default-value (s/nilable string?))
(s/def ::placeholder (s/nilable string?))
(s/def ::keyboard-type
(s/nilable (s/or :string string?
:keyword keyword?))) ; TODO: make set
(s/def ::accessibility-label
(s/nilable (s/or :string string?
:keyword keyword?)))
(s/def ::keyboard-type (s/nilable (s/or :string string? :keyword keyword?))) ; TODO: make set
(s/def ::accessibility-label (s/nilable (s/or :string string? :keyword keyword?)))
(s/def ::on-focus fn?)
(s/def ::on-blur fn?)
(s/def ::on-press fn?)
(s/def ::accessory
(s/keys :opt-un [::on-press
::icon
::component]))
(s/def ::accessory (s/keys :opt-un [::on-press
::icon
::component]))
(s/def ::after (s/nilable ::accessory))
(s/def ::before (s/nilable ::accessory))
@@ -48,27 +42,25 @@
(s/def ::input-style ::style)
(s/def ::container-style ::style)
(s/def ::text-input
(s/keys :opt-un
[::label
::multiline
::error
::style
::input-style
::keyboard-type
::before
::after
::cancel-label
::on-focus
::on-blur
::container-style
::show-cancel
::accessibility-label
::bottom-value
::secure-text-entry]))
(s/def ::text-input (s/keys :opt-un
[::label
::multiline
::error
::style
::input-style
::keyboard-type
::before
::after
::cancel-label
::on-focus
::on-blur
::container-style
::show-cancel
::accessibility-label
::bottom-value
::secure-text-entry]))
(defn check-spec
[spec prop]
(defn check-spec [spec prop]
(if (s/valid? spec prop)
true
(do
@@ -78,17 +70,14 @@
(def height 44) ; 22 line-height + 11*2 vertical padding
(def multiline-height 88) ; 3 * 22 three line-height + 11* vertical padding
(defn label-style
[]
(defn label-style []
{:margin-bottom (:tiny spacing/spacing)})
(defn text-input-row-style
[]
(defn text-input-row-style []
{:flex-direction :row
:align-items :center})
(defn text-input-view-style
[style]
(defn text-input-view-style [style]
(merge {:border-radius 8
:flex-direction :row
:flex 1
@@ -96,8 +85,7 @@
:background-color (:ui-01 @colors/theme)}
style))
(defn text-input-style
[multiline input-style monospace before after]
(defn text-input-style [multiline input-style monospace before after]
(merge (if monospace
typography/monospace
typography/font-regular)
@@ -119,76 +107,66 @@
:height multiline-height})
input-style))
(defn cancel-style
[]
(defn cancel-style []
{:margin-left (:tiny spacing/spacing)
:padding-left (:tiny spacing/spacing)
:justify-content :center
:align-self :stretch})
(defn accessory-style
[]
(defn accessory-style []
(merge (:base spacing/padding-horizontal)
{:flex 1
:justify-content :center}))
(defn accessory-element
[{:keys [icon component icon-opts style accessibility-label on-press]}]
(defn accessory-element [{:keys [icon component icon-opts style accessibility-label on-press]}]
(let [el (if on-press
rn/touchable-opacity
rn/view)]
[el
(merge {:style {:align-self :stretch}}
(when on-press
{:on-press on-press}))
[rn/view
(merge {:style (merge (accessory-style)
style)}
(when accessibility-label
{:accessibility-label accessibility-label}))
[el (merge {:style {:align-self :stretch}}
(when on-press
{:on-press on-press}))
[rn/view (merge {:style (merge (accessory-style)
style)}
(when accessibility-label
{:accessibility-label accessibility-label}))
(cond
icon
[icons/icon icon
(merge {:color (:icon-01 @colors/theme)}
icon-opts)]
[icons/icon icon (merge {:color (:icon-01 @colors/theme)}
icon-opts)]
component
component
:else
nil)]]))
(defn text-input-raw
[]
(defn text-input-raw []
(let [focused (reagent/atom nil)
visible (reagent/atom false)
ref (atom nil)
blur (fn []
(some-> @ref
(ocall "blur")))]
(fn
[{:keys [label multiline error style input-style keyboard-type before after
cancel-label on-focus on-blur show-cancel accessibility-label
bottom-value secure-text-entry container-style get-ref on-cancel
monospace auto-complete-type auto-correct]
:or {cancel-label "Cancel"}
:as props}]
(some-> @ref (ocall "blur")))]
(fn [{:keys [label multiline error style input-style keyboard-type before after
cancel-label on-focus on-blur show-cancel accessibility-label
bottom-value secure-text-entry container-style get-ref on-cancel
monospace auto-complete-type auto-correct]
:or {cancel-label "Cancel"}
:as props}]
{:pre [(check-spec ::text-input props)]}
(let [show-cancel (if (nil? show-cancel)
;; Enabled by default on iOs and disabled on Android
platform/ios?
show-cancel)
after (cond
(and secure-text-entry @visible)
{:icon :main-icons/hide
:on-press #(reset! visible false)}
(let [show-cancel (if (nil? show-cancel)
;; Enabled by default on iOs and disabled on Android
platform/ios?
show-cancel)
after (cond
(and secure-text-entry @visible)
{:icon :main-icons/hide
:on-press #(reset! visible false)}
(and secure-text-entry (not @visible))
{:icon :main-icons/show
:on-press #(reset! visible true)}
(and secure-text-entry (not @visible))
{:icon :main-icons/show
:on-press #(reset! visible true)}
:else after)
secure (boolean (and secure-text-entry (not @visible))) ; must be a boolean to work on
; iOS
:else after)
secure (boolean (and secure-text-entry (not @visible))) ; must be a boolean to work on iOS
auto-complete (cond
(= keyboard-type :visible-password)
:off
@@ -198,9 +176,7 @@
:else
auto-complete-type)
auto-correct (and (not= keyboard-type :visible-password)
(not secure-text-entry)
auto-correct)
auto-correct (and (not= keyboard-type :visible-password) (not secure-text-entry) auto-correct)
on-cancel (fn []
(when on-cancel
(on-cancel))
@@ -209,10 +185,8 @@
(and platform/ios? (= keyboard-type :visible-password))
:default
; the correct approach on Android would be keep secure-text-entry on set
; keyboard type
; to visible-password. But until
; https://github.com/facebook/react-native/issues/27946
; the correct approach on Android would be keep secure-text-entry on set keyboard type
; to visible-password. But until https://github.com/facebook/react-native/issues/27946
; is solved that's the second best way.
(and platform/android? secure-text-entry)
:default
@@ -224,71 +198,59 @@
[text/text {:style (label-style)}
label])
[rn/view {:style (text-input-row-style)}
[rn/view
{:style (text-input-view-style style)
:important-for-accessibility (if secure-text-entry
:no-hide-descendants
:auto)}
[rn/view {:style (text-input-view-style style)
:important-for-accessibility (if secure-text-entry
:no-hide-descendants
:auto)}
(when before
[accessory-element before])
[rn/text-input
(merge
{:style (text-input-style multiline input-style monospace before after)
:ref (fn [r]
(reset! ref r)
(when get-ref (get-ref r)))
:placeholder-text-color (:text-02 @colors/theme)
:underline-color-android :transparent
:auto-capitalize :none
:secure-text-entry secure
:auto-correct auto-correct
:auto-complete-type auto-complete
:on-focus (fn [evt]
(when on-focus (on-focus evt))
(when show-cancel
(rn/configure-next (:ease-in-ease-out
rn/layout-animation-presets)))
(reset! focused true))
:on-blur (fn [evt]
(when on-blur (on-blur evt))
(when show-cancel
(rn/configure-next (:ease-in-ease-out
rn/layout-animation-presets)))
(reset! focused false))
:keyboard-type keyboard-type}
(when (and platform/ios? (not after))
{:clear-button-mode :while-editing})
(dissoc props
:style
:keyboard-type :on-focus
:on-blur
:secure-text-entry
:ref :get-ref
:auto-correct :auto-complete-type))]
(merge {:style (text-input-style multiline input-style monospace before after)
:ref (fn [r]
(reset! ref r)
(when get-ref (get-ref r)))
:placeholder-text-color (:text-02 @colors/theme)
:underline-color-android :transparent
:auto-capitalize :none
:secure-text-entry secure
:auto-correct auto-correct
:auto-complete-type auto-complete
:on-focus (fn [evt]
(when on-focus (on-focus evt))
(when show-cancel
(rn/configure-next (:ease-in-ease-out rn/layout-animation-presets)))
(reset! focused true))
:on-blur (fn [evt]
(when on-blur (on-blur evt))
(when show-cancel
(rn/configure-next (:ease-in-ease-out rn/layout-animation-presets)))
(reset! focused false))
:keyboard-type keyboard-type}
(when (and platform/ios? (not after))
{:clear-button-mode :while-editing})
(dissoc props
:style :keyboard-type :on-focus :on-blur
:secure-text-entry :ref :get-ref :auto-correct :auto-complete-type))]
(when after
[accessory-element after])]
(when (and show-cancel
(not multiline)
@focused)
[rn/touchable-opacity
{:style (cancel-style)
:on-press on-cancel}
[rn/touchable-opacity {:style (cancel-style)
:on-press on-cancel}
[text/text {:color :link} cancel-label]])
(when error
[tooltip/tooltip
(merge {:bottom-value (if bottom-value bottom-value 0)}
(when accessibility-label
{:accessibility-label (str (name accessibility-label) "-error")}))
[text/text
{:color :negative
:align :center
:size :small}
[tooltip/tooltip (merge {:bottom-value (if bottom-value bottom-value 0)}
(when accessibility-label
{:accessibility-label (str (name accessibility-label) "-error")}))
[text/text {:color :negative
:align :center
:size :small}
error]])]]))))
;; TODO(Ferossgp): Refactor me when hooks available
(defn text-input
[{:keys [preserve-input?]
:as props}]
(defn text-input [{:keys [preserve-input?]
:as props}]
(if preserve-input?
[text-input-raw props]
(let [id (random-uuid)]
@@ -299,11 +261,10 @@
:reagent-render
(fn [{:keys [get-ref default-value]
:as props}]
[text-input-raw
(merge props
{:get-ref (fn [r]
;; Store input and its defaultValue
;; one we receive a non-nil ref
(when r
(swap! text-input-refs assoc id {:ref r :value default-value}))
(when get-ref (get-ref r)))})])}))))
[text-input-raw (merge props
{:get-ref (fn [r]
;; Store input and its defaultValue
;; one we receive a non-nil ref
(when r
(swap! text-input-refs assoc id {:ref r :value default-value}))
(when get-ref (get-ref r)))})])}))))
+28 -36
View File
@@ -1,17 +1,17 @@
(ns quo.components.tooltip
(:require [oops.core :refer [oget]]
(:require [reagent.core :as reagent]
[oops.core :refer [oget]]
[quo.animated :as animated]
[quo.react-native :as rn]
[quo.design-system.colors :as colors]
[quo.design-system.spacing :as spacing]
[quo.platform :as platform] ;; FIXME(Ferossgp): Dependecy on status
[quo.react-native :as rn]
[reagent.core :as reagent]
[quo.platform :as platform]
;; FIXME(Ferossgp): Dependecy on status
[status-im.ui.components.icons.icons :as icons]))
(def ^:private initial-height 22)
(defn tooltip-style
[{:keys [bottom-value animation]}]
(defn tooltip-style [{:keys [bottom-value animation]}]
(merge
(:base spacing/padding-horizontal)
{:position :absolute
@@ -22,8 +22,7 @@
:opacity animation
:transform [{:translateY (animated/mix animation 10 0)}]}))
(defn container-style
[]
(defn container-style []
{:z-index 2
:align-items :center
:shadow-radius 16
@@ -31,50 +30,43 @@
:shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}})
(defn content-style
[]
(defn content-style []
(merge (:base spacing/padding-horizontal)
{:padding-vertical 6
:elevation 2
:background-color (:ui-background @colors/theme)
:border-radius 8}))
(defn tooltip
[]
(defn tooltip []
(let [layout (reagent/atom {:height initial-height})
animation-v (animated/value 0)
animation (animated/with-timing-transition
animation-v
{:easing (:ease-in animated/easings)})
animation-v
{:easing (:ease-in animated/easings)})
on-layout (fn [evt]
(let [width (oget evt "nativeEvent" "layout" "width")
height (oget evt "nativeEvent" "layout" "height")]
(reset! layout {:width width
:height height})))]
(fn [{:keys [bottom-value accessibility-label]} & children]
(fn [{:keys [bottom-value accessibility-label]} & children]
[:<>
[animated/code
{:exec (animated/cond* (animated/not* animation-v)
(animated/set animation-v 1))}]
[animated/view
{:style (tooltip-style {:bottom-value (- (get @layout :height)
bottom-value)
:animation animation})
:pointer-events :box-none}
[animated/view
{:style (container-style)
:pointer-events :box-none}
(into [rn/view
{:style (content-style)
:pointer-events :box-none
:accessibility-label accessibility-label
:on-layout on-layout}]
[animated/code {:exec (animated/cond* (animated/not* animation-v)
(animated/set animation-v 1))}]
[animated/view {:style (tooltip-style {:bottom-value (- (get @layout :height)
bottom-value)
:animation animation})
:pointer-events :box-none}
[animated/view {:style (container-style)
:pointer-events :box-none}
(into [rn/view {:style (content-style)
:pointer-events :box-none
:accessibility-label accessibility-label
:on-layout on-layout}]
children)
(when platform/ios?
;; NOTE(Ferossgp): Android does not show elevation for tooltip making it lost on white bg
[icons/icon :icons/tooltip-tip
{:width 18
:height 8
:container-style {:elevation 3}
:color (:ui-background @colors/theme)}])]]])))
[icons/icon :icons/tooltip-tip {:width 18
:height 8
:container-style {:elevation 3}
:color (:ui-background @colors/theme)}])]]])))
+9 -9
View File
@@ -1,18 +1,18 @@
(ns quo.core
(:require [quo.components.animated-header :as animated-header]
[quo.components.bottom-sheet.view :as bottom-sheet]
[quo.components.button.view :as button]
[quo.components.controls.view :as controls]
[quo.components.header :as header]
[quo.components.list.footer :as list-footer]
[quo.components.list.header :as list-header]
[quo.components.list.index :as list-index]
[quo.components.list.item :as list-item]
[quo.components.safe-area :as safe-area]
[quo.components.separator :as separator]
[quo.components.text :as text]
[quo.components.text-input :as text-input]
[quo.components.tooltip :as tooltip]
[quo.components.text :as text]
[quo.components.button.view :as button]
[quo.components.list.header :as list-header]
[quo.components.list.footer :as list-footer]
[quo.components.list.item :as list-item]
[quo.components.list.index :as list-index]
[quo.components.controls.view :as controls]
[quo.components.bottom-sheet.view :as bottom-sheet]
[quo.components.separator :as separator]
[quo.design-system.colors :as colors]))
(def text text/text)
+30 -43
View File
@@ -1,6 +1,6 @@
(ns quo.design-system.colors
(:require [clojure.string :as string]
[reagent.core :as reagent]))
(:require [reagent.core :as reagent]
[clojure.string :as string]))
;; Colors mapping from figma to code, note that theme is more extended and
;; one can follow the comments from the light theme to choose what to use in a component.
@@ -19,17 +19,14 @@
(def light-theme
{:positive-01 "rgba(68,208,88,1)" ; Primary Positive, text, icons color
:positive-02 "rgba(78,188,96,0.1)" ; Secondary Positive, Supporting color for success
; illustrations
:positive-02 "rgba(78,188,96,0.1)" ; Secondary Positive, Supporting color for success illustrations
:positive-03 "rgba(78,188,96,1)" ; Lighter Positive, Supporting color for success illustrations
:negative-01 "rgba(255,45,85,1)" ; Primary Negative, text, icons color
:negative-02 "rgba(255,45,85,0.1))" ; Secondary Negative, Supporting color for errors
; illustrations
:negative-02 "rgba(255,45,85,0.1))" ; Secondary Negative, Supporting color for errors illustrations
:warning-01 "rgba(255, 202, 15, 1)"
:warning-02 "rgba(255, 202, 15, 0.1)"
:interactive-01 "rgba(67,96,223,1)" ; Accent color, buttons, own message, actions,active state
:interactive-02 "rgba(236,239,252,1)" ; Light Accent, buttons background, actions background,
; messages
:interactive-02 "rgba(236,239,252,1)" ; Light Accent, buttons background, actions background, messages
:interactive-03 "rgba(255,255,255,0.1)" ; Background for interactive above accent
:interactive-04 "rgba(147,155,161,1)" ; Disabled state
:ui-background "rgba(255,255,255,1)" ; Default view background
@@ -88,14 +85,12 @@
(def theme (reagent/atom light-theme))
(defn get-color
[color]
(defn get-color [color]
(get @theme color))
;; LEGACY COLORS
(defn alpha
[value opacity]
(defn alpha [value opacity]
(if (string/starts-with? value "#")
(let [hex (string/replace value #"#" "")
r (js/parseInt (subs hex 0 2) 16)
@@ -105,15 +100,13 @@
(let [rgb (string/split value #",")]
(str (string/join "," (butlast rgb)) "," opacity ")"))))
(def old-colors-mapping-light
{:mentioned-background "#def6fc"
:mentioned-border "#b8ecf9"
:pin-background "#FFEECC"})
(def old-colors-mapping-light {:mentioned-background "#def6fc"
:mentioned-border "#b8ecf9"
:pin-background "#FFEECC"})
(def old-colors-mapping-dark
{:mentioned-background "#2a4046"
:mentioned-border "#2a4046"
:pin-background "#34232B"})
(def old-colors-mapping-dark {:mentioned-background "#2a4046"
:mentioned-border "#2a4046"
:pin-background "#34232B"})
(def old-colors-mapping-themes {:dark old-colors-mapping-dark :light old-colors-mapping-light})
@@ -134,8 +127,7 @@
;; BLACK
(def black (:text-01 light-theme)) ;; Used as the default text color
(def black-persist (:ui-background dark-theme)) ;; this doesn't with theme
(def black-transparent (:ui-02 light-theme)) ;; Used as background color for rounded button on dark background and as background
;; color for containers like "Backup recovery phrase"
(def black-transparent (:ui-02 light-theme)) ;; Used as background color for rounded button on dark background and as background color for containers like "Backup recovery phrase"
(def black-transparent-20 (:backdrop light-theme)) ; accounts divider
(def black-transparent-40 (:backdrop light-theme))
(def black-transparent-40-persist (:backdrop light-theme))
@@ -144,8 +136,7 @@
(def black-transparent-86 (:ui-03 light-theme))
;; DARK GREY
(def gray (:text-02 light-theme)) ;; Dark grey, used as a background for a light foreground and as
;; section header and secondary text color
(def gray (:text-02 light-theme)) ;; Dark grey, used as a background for a light foreground and as section header and secondary text color
(def gray-transparent-10 (alpha gray 0.1))
(def gray-transparent-40 (alpha gray 0.4))
;; LIGHT GREY
@@ -173,22 +164,20 @@
(def purple "#887af9")
(def orange "#FE8F59")
(def chat-colors
["#fa6565"
"#7cda00"
purple
"#51d0f0"
orange
"#d37ef4"])
(def chat-colors ["#fa6565"
"#7cda00"
purple
"#51d0f0"
orange
"#d37ef4"])
(def account-colors
["#9B832F"
"#D37EF4"
"#1D806F"
"#FA6565"
"#7CDA00"
purple
"#8B3131"])
(def account-colors ["#9B832F"
"#D37EF4"
"#1D806F"
"#FA6565"
"#7CDA00"
purple
"#8B3131"])
(def mention-incoming "#0DA4C9")
(def mention-outgoing "#9EE8FA")
@@ -202,12 +191,10 @@
(def theme-type (reagent/atom :light))
(defn dark?
[]
(defn dark? []
(= :dark @theme-type))
(defn set-legacy-theme-type
[type]
(defn set-legacy-theme-type [type]
(when-not (= type @theme-type)
(let [old-colors-mapping-colors (get old-colors-mapping-themes type)]
(set! white (:ui-background @theme))
+15 -18
View File
@@ -1,22 +1,19 @@
(ns quo.design-system.spacing)
(def spacing
{:x-tiny 4
:tiny 8
:small 12
:base 16
:large 24
:x-large 32
:xx-large 48})
(def spacing {:x-tiny 4
:tiny 8
:small 12
:base 16
:large 24
:x-large 32
:xx-large 48})
(def padding-horizontal
(reduce-kv (fn [m k v]
(assoc m k {:padding-horizontal v}))
{}
spacing))
(def padding-horizontal (reduce-kv (fn [m k v]
(assoc m k {:padding-horizontal v}))
{}
spacing))
(def padding-vertical
(reduce-kv (fn [m k v]
(assoc m k {:padding-vertical v}))
{}
spacing))
(def padding-vertical (reduce-kv (fn [m k v]
(assoc m k {:padding-vertical v}))
{}
spacing))
+14 -21
View File
@@ -1,32 +1,25 @@
(ns quo.design-system.typography)
(def tiny
{:font-size 10
:line-height 14})
(def tiny {:font-size 10
:line-height 14})
(def x-small
{:font-size 12
:line-height 16})
(def x-small {:font-size 12
:line-height 16})
(def small
{:font-size 13
:line-height 18})
(def small {:font-size 13
:line-height 18})
(def base
{:font-size 15
:line-height 22})
(def base {:font-size 15
:line-height 22})
(def large
{:font-size 17
:line-height 24})
(def large {:font-size 17
:line-height 24})
(def x-large
{:font-size 22
:line-height 30})
(def x-large {:font-size 22
:line-height 30})
(def xx-large
{:font-size 28
:line-height 38})
(def xx-large {:font-size 28
:line-height 38})
(def font-regular {:font-family "Inter-Regular"}) ; 400
+16 -24
View File
@@ -1,17 +1,12 @@
(ns quo.gesture-handler
(:require ["react-native-gesture-handler" :refer
(TapGestureHandler PanGestureHandler
LongPressGestureHandler
TouchableWithoutFeedback
TouchableOpacity
TouchableHighlight
State
NativeViewGestureHandler
FlatList
ScrollView)]
[oops.core :refer [oget]]
(:require [oops.core :refer [oget]]
[reagent.core :as reagent]
[quo.design-system.colors :as colors]
[reagent.core :as reagent]))
["react-native-gesture-handler"
:refer (TapGestureHandler PanGestureHandler LongPressGestureHandler
TouchableWithoutFeedback TouchableOpacity
TouchableHighlight State NativeViewGestureHandler
FlatList ScrollView)]))
(def flat-list-raw FlatList)
@@ -35,11 +30,9 @@
(def touchable-highlight-class (reagent/adapt-react-class TouchableHighlight))
(defn touchable-highlight
[props & children]
(into [touchable-highlight-class
(merge {:underlay-color (:interactive-02 @colors/theme)}
props)]
(defn touchable-highlight [props & children]
(into [touchable-highlight-class (merge {:underlay-color (:interactive-02 @colors/theme)}
props)]
children))
(def touchable-opacity
@@ -47,10 +40,9 @@
(def native-view-gesture-handler (reagent/adapt-react-class NativeViewGestureHandler))
(def states
{:began (oget State "BEGAN")
:active (oget State "ACTIVE")
:cancelled (oget State "CANCELLED")
:end (oget State "END")
:failed (oget State "FAILED")
:undetermined (oget State "UNDETERMINED")})
(def states {:began (oget State "BEGAN")
:active (oget State "ACTIVE")
:cancelled (oget State "CANCELLED")
:end (oget State "END")
:failed (oget State "FAILED")
:undetermined (oget State "UNDETERMINED")})
+11 -12
View File
@@ -9,17 +9,16 @@
:notification-success "notificationSuccess"
:notification-warning "notificationWarning"
:notification-error "notificationError"
:clock-tick "clockTick" ; (Android only)
:context-click "contextClick" ; (Android only)
:keyboard-press "keyboardPress" ; (Android only)
:keyboard-release "keyboardRelease" ; (Android only)
:keyboard-tap "keyboardTap" ; (Android only)
:long-press "longPress" ; (Android only)
:text-handle-move "textHandleMove" ; (Android only)
:virtual-key "virtualKey" ; (Android only)
:virtual-key-release "virtualKeyRelease" ; (Android only)
})
:clock-tick "clockTick" ; (Android only)
:context-click "contextClick" ; (Android only)
:keyboard-press "keyboardPress" ; (Android only)
:keyboard-release "keyboardRelease" ; (Android only)
:keyboard-tap "keyboardTap" ; (Android only)
:long-press "longPress" ; (Android only)
:text-handle-move "textHandleMove" ; (Android only)
:virtual-key "virtualKey" ; (Android only)
:virtual-key-release "virtualKeyRelease" ; (Android only)
})
(defn trigger
[method]
(defn trigger [method]
(.trigger ^js react-native-haptic-feedback (get haptic-methods method)))
+43 -54
View File
@@ -1,29 +1,27 @@
(ns quo.previews.bottom-sheet
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]
(:require [reagent.core :as reagent]
[quo.core :as quo]
[quo.react-native :as rn]
[reagent.core :as reagent]))
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]))
(def descriptor
[{:label "Show handle:"
:key :show-handle?
:type :boolean}
{:label "Backdrop dismiss:"
:key :backdrop-dismiss?
:type :boolean}
{:label "Disable drag:"
:key :disable-drag?
:type :boolean}
{:label "Android back cancel:"
:key :back-button-cancel
:type :boolean}
{:label "Scrollable:"
:key :scrollable
:type :boolean}])
(def descriptor [{:label "Show handle:"
:key :show-handle?
:type :boolean}
{:label "Backdrop dismiss:"
:key :backdrop-dismiss?
:type :boolean}
{:label "Disable drag:"
:key :disable-drag?
:type :boolean}
{:label "Android back cancel:"
:key :back-button-cancel
:type :boolean}
{:label "Scrollable:"
:key :scrollable
:type :boolean}])
(defn cool-preview
[]
(defn cool-preview []
(let [state (reagent/atom {:show-handle? true
:backdrop-dismiss? true
:disable-drag? false
@@ -31,47 +29,38 @@
visible (reagent/atom false)
scrollable (reagent/cursor state [:scrollable])]
(fn []
[rn/view
{:margin-bottom 50
:padding 16}
[rn/view {:margin-bottom 50
:padding 16}
[preview/customizer state descriptor]
[:<>
[rn/view
{:style {:align-items :center
:padding 16}}
[rn/view {:style {:align-items :center
:padding 16}}
[rn/touchable-opacity {:on-press #(reset! visible true)}
[rn/view
{:style {:padding-horizontal 16
:padding-vertical 8
:border-radius 4
:background-color (:interactive-01 @colors/theme)}}
[rn/view {:style {:padding-horizontal 16
:padding-vertical 8
:border-radius 4
:background-color (:interactive-01 @colors/theme)}}
[quo/text {:color :secondary-inverse}
(str "Open sheet: " @visible)]]]]
[quo/bottom-sheet
(merge @state
{:visible? @visible
:on-cancel #(reset! visible false)})
[rn/view
{:style {:height (if @scrollable 1200 400)
:justify-content :center
:align-items :center}}
[quo/bottom-sheet (merge @state
{:visible? @visible
:on-cancel #(reset! visible false)})
[rn/view {:style {:height (if @scrollable 1200 400)
:justify-content :center
:align-items :center}}
[rn/touchable-opacity {:on-press #(reset! visible false)}
[quo/text {:color :link} "Close"]]
[rn/touchable-opacity
{:on-press #(swap! scrollable not)
:style {:padding-vertical 16}}
[rn/touchable-opacity {:on-press #(swap! scrollable not)
:style {:padding-vertical 16}}
[quo/text {:color :link} "Toggle size"]]
[quo/text "Hello world!"]]]]])))
(defn preview
[]
(defn preview []
(fn []
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:key-fn str}]]))
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:key-fn str}]]))
+58 -68
View File
@@ -1,46 +1,44 @@
(ns quo.previews.button
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]
(:require [reagent.core :as reagent]
[quo.core :as quo]
[quo.react-native :as rn]
[reagent.core :as reagent]))
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]))
(def descriptor
[{:label "Type:"
:key :type
:type :select
:options [{:key :primary
:value "Primary"}
{:key :secondary
:value "Secondary"}
{:key :icon
:value "Icon"}]}
{:label "Theme:"
:key :theme
:type :select
:options [{:key :main
:value "Main"}
{:key :negative
:value "Negative"}
{:key :positive
:value "Positive"}
{:key :accent
:value "Accent"}]}
{:label "After icon:"
:key :after
:type :boolean}
{:label "Before icon:"
:key :before
:type :boolean}
{:label "Disabled:"
:key :disabled
:type :boolean}
{:label "Label"
:key :label
:type :text}])
(def descriptor [{:label "Type:"
:key :type
:type :select
:options [{:key :primary
:value "Primary"}
{:key :secondary
:value "Secondary"}
{:key :icon
:value "Icon"}]}
{:label "Theme:"
:key :theme
:type :select
:options [{:key :main
:value "Main"}
{:key :negative
:value "Negative"}
{:key :positive
:value "Positive"}
{:key :accent
:value "Accent"}]}
{:label "After icon:"
:key :after
:type :boolean}
{:label "Before icon:"
:key :before
:type :boolean}
{:label "Disabled:"
:key :disabled
:type :boolean}
{:label "Label"
:key :label
:type :text}])
(defn cool-preview
[]
(defn cool-preview []
(let [state (reagent/atom {:label "Press Me"
:type :primary
:theme :main
@@ -50,36 +48,28 @@
before (reagent/cursor state [:before])
after (reagent/cursor state [:after])]
(fn []
[rn/view
{:margin-bottom 50
:padding 16}
[rn/view {:margin-bottom 50
:padding 16}
[rn/view {:flex 1}
[preview/customizer state descriptor]]
[rn/view
{:padding-vertical 16
:flex-direction :row
:justify-content :center}
[quo/button
(merge (dissoc @state
:theme
:before
:after)
{:on-press #(println "Hello world!")}
(when @theme
{:theme @theme})
(when @before
{:before :main-icons/back})
(when @after
{:after :main-icons/next}))
[rn/view {:padding-vertical 16
:flex-direction :row
:justify-content :center}
[quo/button (merge (dissoc @state
:theme :before :after)
{:on-press #(println "Hello world!")}
(when @theme
{:theme @theme})
(when @before
{:before :main-icons/back})
(when @after
{:after :main-icons/next}))
@label]]])))
(defn preview-button
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:key-fn str}]])
(defn preview-button []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:key-fn str}]])
+33 -44
View File
@@ -1,55 +1,44 @@
(ns quo.previews.controls
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
(:require [reagent.core :as reagent]
[quo.core :as quo]
[quo.react-native :as rn]
[reagent.core :as reagent]))
[quo.design-system.colors :as colors]))
(defn preview
[]
(defn preview []
(let [switch-state (reagent/atom true)
radio-state (reagent/atom true)
checkbox-state (reagent/atom true)]
(fn []
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/view
{:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity
{:style {:margin-vertical 10
:padding 10}
:on-press #(swap! switch-state not)}
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/view {:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity {:style {:margin-vertical 10
:padding 10}
:on-press #(swap! switch-state not)}
[quo/text (str "Switch state: " @switch-state)]]
[quo/switch
{:value @switch-state
:on-change #(reset! switch-state %)}]]
[quo/switch {:value @switch-state
:on-change #(reset! switch-state %)}]]
[rn/view
{:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity
{:style {:margin-vertical 10
:padding 10}
:on-press #(swap! radio-state not)}
[rn/view {:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity {:style {:margin-vertical 10
:padding 10}
:on-press #(swap! radio-state not)}
[quo/text (str "Radio state: " @radio-state)]]
[quo/radio
{:value @radio-state
:on-change #(reset! radio-state %)}]]
[rn/view
{:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity
{:style {:margin-vertical 10
:padding 10}
:on-press #(swap! checkbox-state not)}
[quo/radio {:value @radio-state
:on-change #(reset! radio-state %)}]]
[rn/view {:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity {:style {:margin-vertical 10
:padding 10}
:on-press #(swap! checkbox-state not)}
[quo/text (str "Checkbox state: " @checkbox-state)]]
[quo/checkbox
{:value @checkbox-state
:on-change #(reset! checkbox-state %)}]]])))
[quo/checkbox {:value @checkbox-state
:on-change #(reset! checkbox-state %)}]]])))
+36 -43
View File
@@ -1,51 +1,44 @@
(ns quo.previews.header
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.react-native :as rn])
[quo.react-native :as rn]
[quo.design-system.colors :as colors])
(:require-macros [quo.previews.preview :as preview]))
(def accessories
[nil
[{:icon :main-icons/close
:on-press identity}]
[{:icon :main-icons/close
:on-press identity}
{:icon :main-icons/add
:on-press identity}]
[{:icon :main-icons/add
:on-press identity}
{:label "Text"
:on-press identity}]
[{:label "Text"
:on-press identity}]])
(def accessories [nil
[{:icon :main-icons/close
:on-press identity}]
[{:icon :main-icons/close
:on-press identity}
{:icon :main-icons/add
:on-press identity}]
[{:icon :main-icons/add
:on-press identity}
{:label "Text"
:on-press identity}]
[{:label "Text"
:on-press identity}]])
(def all-props
(preview/list-comp [left-accessories accessories
right-accessories accessories
title [nil "This is a title" "This is a very long super title"]
subtitle [nil "This is a subtitle"]
title-align [:left :center]]
{:left-accessories left-accessories
:right-accessories right-accessories
:title title
:subtitle subtitle
:title-align title-align}))
(def all-props (preview/list-comp [left-accessories accessories
right-accessories accessories
title [nil "This is a title" "This is a very long super title"]
subtitle [nil "This is a subtitle"]
title-align [:left :center]]
{:left-accessories left-accessories
:right-accessories right-accessories
:title title
:subtitle subtitle
:title-align title-align}))
(defn render-item
[props]
[rn/view
{:border-bottom-color "#EEF2F5"
:border-bottom-width 2}
(defn render-item [props]
[rn/view {:border-bottom-color "#EEF2F5"
:border-bottom-width 2}
[quo/header props]])
(defn preview-header
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:data all-props
:render-fn render-item
:key-fn str}]])
(defn preview-header []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:data all-props
:render-fn render-item
:key-fn str}]])
+3 -5
View File
@@ -3,11 +3,9 @@
[quo.react-native :as rn]
[status-im.ui.components.icons.icons :as icons]))
(defn preview
[]
[rn/scroll-view
{:background-color (:ui-background @colors/theme)
:flex 1}
(defn preview []
[rn/scroll-view {:background-color (:ui-background @colors/theme)
:flex 1}
(for [i (keys icons/icons)]
[rn/view {:flex-direction :row}
[icons/icon (keyword i)]
+85 -96
View File
@@ -1,94 +1,87 @@
(ns quo.previews.lists
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]
(:require [reagent.core :as reagent]
[quo.core :as quo]
[quo.react-native :as rn]
[reagent.core :as reagent]))
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]))
(def all-props (preview/list-comp [] {}))
(defn avatar
[]
[rn/view
{:border-radius 20
:width 40
:height 40
:justify-content :center
:align-items :center
:background-color :red}
[quo/text
{:weight :bold
:size :large}
(defn avatar []
[rn/view {:border-radius 20
:width 40
:height 40
:justify-content :center
:align-items :center
:background-color :red}
[quo/text {:weight :bold
:size :large}
"T"]])
(defn icon-element
[type]
(defn icon-element [type]
(case type
:icon :main-icons/add-contact
:component [avatar]
nil))
(def descriptor
[{:label "Accessory:"
:key :accessory
:type :select
:options [{:key :radio
:value "Radio"}
{:key :checkbox
:value "Checkbox"}
{:key :switch
:value "Switch"}
{:key :text
:value "Text"}
{:key :default
:value "Default"}]}
{:label "Size:"
:key :size
:type :select
:options [{:key :small
:value "Small"}
{:key :default
:value "Default"}]}
{:label "Icon:"
:key :icon
:type :select
:options [{:key :icon
:value "Icon"}
{:key :component
:value "Component"}]}
{:label "Theme:"
:key :theme
:type :select
:options [{:key :main
:value "Main"}
{:key :accent
:value "Accent"}
{:key :negative
:value "Negative"}
{:key :positive
:value "Positive"}]}
{:label "Selectable"
:key :selectable
:type :boolean}
{:label "Chevron"
:key :chevron
:type :boolean}
{:label "Disabled:"
:key :disabled
:type :boolean}
{:label "Title"
:key :title
:type :text}
{:label "Subtitle"
:key :subtitle
:type :text}])
(def descriptor [{:label "Accessory:"
:key :accessory
:type :select
:options [{:key :radio
:value "Radio"}
{:key :checkbox
:value "Checkbox"}
{:key :switch
:value "Switch"}
{:key :text
:value "Text"}
{:key :default
:value "Default"}]}
{:label "Size:"
:key :size
:type :select
:options [{:key :small
:value "Small"}
{:key :default
:value "Default"}]}
{:label "Icon:"
:key :icon
:type :select
:options [{:key :icon
:value "Icon"}
{:key :component
:value "Component"}]}
{:label "Theme:"
:key :theme
:type :select
:options [{:key :main
:value "Main"}
{:key :accent
:value "Accent"}
{:key :negative
:value "Negative"}
{:key :positive
:value "Positive"}]}
{:label "Selectable"
:key :selectable
:type :boolean}
{:label "Chevron"
:key :chevron
:type :boolean}
{:label "Disabled:"
:key :disabled
:type :boolean}
{:label "Title"
:key :title
:type :text}
{:label "Subtitle"
:key :subtitle
:type :text}])
(defn render-item
[_]
[rn/view {:style {:padding-vertical 24}}])
(defn render-item [_]
[rn/view {:style {:padding-vertical 24}}])
(defn cool-preview
[]
(defn cool-preview []
(let [state (reagent/atom {:title "Title"
:active false})
icon (reagent/cursor state [:icon])
@@ -99,23 +92,19 @@
[rn/view {:padding-horizontal 16}
[preview/customizer state descriptor]]
[rn/view {:padding-vertical 16}
[quo/list-item
(merge (dissoc @state :active :selectable)
(when @selectable
{:active @active
:on-press #(swap! active not)})
{:accessory-text "Accessory"
:icon (icon-element @icon)})]]])))
[quo/list-item (merge (dissoc @state :active :selectable)
(when @selectable
{:active @active
:on-press #(swap! active not)})
{:accessory-text "Accessory"
:icon (icon-element @icon)})]]])))
(defn preview
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])
(defn preview []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])
+63 -72
View File
@@ -1,83 +1,75 @@
(ns quo.previews.main
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.bottom-sheet :as bottom-sheet]
[quo.previews.button :as button]
[quo.previews.controls :as controls]
[quo.previews.header :as header]
[quo.previews.icons :as icons]
[quo.previews.lists :as lists]
(:require [quo.previews.header :as header]
[quo.previews.text :as text]
[quo.previews.text-input :as text-input]
[quo.previews.tooltip :as tooltip]
[quo.previews.button :as button]
[quo.previews.lists :as lists]
[quo.previews.bottom-sheet :as bottom-sheet]
[quo.previews.controls :as controls]
[quo.react-native :as rn]
[quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.theme :as theme]
[quo.previews.icons :as icons]
[re-frame.core :as re-frame]))
(def screens
[{:name :texts
:insets {:top false}
:component text/preview-text}
{:name :tooltip
:insets {:top false}
:component tooltip/preview-tooltip}
{:name :text-input
:insets {:top false}
:component text-input/preview-text}
{:name :headers
:insets {:top false}
:component header/preview-header}
{:name :button
:insets {:top false}
:component button/preview-button}
{:name :lists
:instes {:top false}
:component lists/preview}
{:name :bottom-sheet
:insets {:top false}
:component bottom-sheet/preview}
{:name :controls
:insets {:top false}
:component controls/preview}
{:name :icons
:insets {:top false}
:component icons/preview}])
(def screens [{:name :texts
:insets {:top false}
:component text/preview-text}
{:name :tooltip
:insets {:top false}
:component tooltip/preview-tooltip}
{:name :text-input
:insets {:top false}
:component text-input/preview-text}
{:name :headers
:insets {:top false}
:component header/preview-header}
{:name :button
:insets {:top false}
:component button/preview-button}
{:name :lists
:instes {:top false}
:component lists/preview}
{:name :bottom-sheet
:insets {:top false}
:component bottom-sheet/preview}
{:name :controls
:insets {:top false}
:component controls/preview}
{:name :icons
:insets {:top false}
:component icons/preview}])
(defn theme-switcher
[]
[rn/view
{:style {:flex-direction :row
:margin-vertical 8
:border-radius 4
:background-color (:ui-01 @colors/theme)
:border-width 1
:border-color (:ui-02 @colors/theme)}}
[rn/touchable-opacity
{:style {:padding 8
:flex 1
:justify-content :center
:align-items :center}
:on-press #(theme/set-theme :light)}
(defn theme-switcher []
[rn/view {:style {:flex-direction :row
:margin-vertical 8
:border-radius 4
:background-color (:ui-01 @colors/theme)
:border-width 1
:border-color (:ui-02 @colors/theme)}}
[rn/touchable-opacity {:style {:padding 8
:flex 1
:justify-content :center
:align-items :center}
:on-press #(theme/set-theme :light)}
[quo/text "Set light theme"]]
[rn/view
{:width 1
:margin-vertical 4
:background-color (:ui-02 @colors/theme)}]
[rn/touchable-opacity
{:style {:padding 8
:flex 1
:justify-content :center
:align-items :center}
:on-press #(theme/set-theme :dark)}
[rn/view {:width 1
:margin-vertical 4
:background-color (:ui-02 @colors/theme)}]
[rn/touchable-opacity {:style {:padding 8
:flex 1
:justify-content :center
:align-items :center}
:on-press #(theme/set-theme :dark)}
[quo/text "Set dark theme"]]])
(defn main-screen
[]
[rn/scroll-view
{:flex 1
:padding-vertical 8
:padding-horizontal 16
:background-color (:ui-background @colors/theme)}
(defn main-screen []
[rn/scroll-view {:flex 1
:padding-vertical 8
:padding-horizontal 16
:background-color (:ui-background @colors/theme)}
[theme-switcher]
[rn/view
(for [{:keys [name]} screens]
@@ -86,7 +78,6 @@
[rn/view {:style {:padding-vertical 8}}
[quo/text (str "Preview " name)]]])]])
(def main-screens
[{:name :quo-preview
:insets {:top false}
:component main-screen}])
(def main-screens [{:name :quo-preview
:insets {:top false}
:component main-screen}])
+3 -5
View File
@@ -1,17 +1,15 @@
(ns quo.previews.preview)
(defn descriptor->values
[{:keys [key options type]}]
(defn descriptor->values [{:keys [key options type]}]
{key (case type
:boolean [false true]
:text [nil "Just simple text"] ; NOTE(Ferossgp): add example with long text?
:select (mapv :key options))})
(defmacro list-comp
[[binding seq-expr & bindings] body-expr]
(defmacro list-comp [[binding seq-expr & bindings] body-expr]
(cond (not binding)
`(list ~body-expr)
:else
`(mapcat (fn [~binding] (list-comp ~bindings ~body-expr))
~seq-expr)))
~seq-expr)))
+66 -89
View File
@@ -1,26 +1,23 @@
(ns quo.previews.preview
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
(:require [reagent.core :as reagent]
[quo.react-native :as rn]
[reagent.core :as reagent])
[quo.core :as quo]
[quo.design-system.colors :as colors])
(:require-macros quo.previews.preview))
(def container
{:flex-direction :row
:padding-vertical 8
:flex 1
:align-items :center})
(def container {:flex-direction :row
:padding-vertical 8
:flex 1
:align-items :center})
(defn touchable-style
[]
(defn touchable-style []
{:flex 1
:align-items :center
:justify-content :center
:padding-horizontal 16
:height 44})
(defn select-style
[]
(defn select-style []
{:flex 1
:flex-direction :row
:align-items :center
@@ -31,8 +28,7 @@
:border-width 1
:border-color (:ui-02 @colors/theme)})
(defn select-option-style
[selected]
(defn select-option-style [selected]
(merge (select-style)
{:margin-vertical 8
:justify-content :center}
@@ -40,27 +36,23 @@
{:background-color (:interactive-02 @colors/theme)}
{:background-color (:ui-01 @colors/theme)})))
(def label-style
{:flex 0.4
:padding-right 8})
(def label-style {:flex 0.4
:padding-right 8})
(defn label-view
[state label]
(defn label-view [state label]
[rn/view {:style label-style}
[quo/text
(when-let [label-color (:preview-label-color @state)]
{:style {:color label-color}})
label]])
(defn modal-container
[]
(defn modal-container []
{:flex 1
:justify-content :center
:padding-horizontal 24
:background-color "rgba(0,0,0,0.4)"})
(defn modal-view
[]
(defn modal-view []
{:padding-horizontal 16
:padding-vertical 8
:border-radius 8
@@ -72,25 +64,21 @@
(let [state* (reagent/cursor state [key])]
[rn/view {:style container}
[label-view state label]
[rn/view
{:style {:flex-direction :row
:flex 0.6
:border-radius 4
:background-color (:ui-01 @colors/theme)
:border-width 1
:border-color (:ui-02 @colors/theme)}}
[rn/touchable-opacity
{:style (touchable-style)
:on-press #(reset! state* true)}
[quo/text {:color (if @state* :link :secondary)}
[rn/view {:style {:flex-direction :row
:flex 0.6
:border-radius 4
:background-color (:ui-01 @colors/theme)
:border-width 1
:border-color (:ui-02 @colors/theme)}}
[rn/touchable-opacity {:style (touchable-style)
:on-press #(reset! state* true)}
[quo/text {:color (if @state* :link :secondary)}
"True"]]
[rn/view
{:width 1
:margin-vertical 4
:background-color (:ui-02 @colors/theme)}]
[rn/touchable-opacity
{:style (touchable-style)
:on-press #(reset! state* false)}
[rn/view {:width 1
:margin-vertical 4
:background-color (:ui-02 @colors/theme)}]
[rn/touchable-opacity {:style (touchable-style)
:on-press #(reset! state* false)}
[quo/text {:color (if (not @state*) :link :secondary)}
"False"]]]]))
@@ -100,22 +88,20 @@
[rn/view {:style container}
[label-view state label]
[rn/view {:style {:flex 0.6}}
[quo/text-input
{:value @state*
:show-cancel false
:style {:border-radius 4
:border-width 1
:border-color (:ui-02 @colors/theme)}
:on-change-text #(do
(reset! state* %)
(reagent/flush))}]]]))
[quo/text-input {:value @state*
:show-cancel false
:style {:border-radius 4
:border-width 1
:border-color (:ui-02 @colors/theme)}
:on-change-text #(do
(reset! state* %)
(reagent/flush))}]]]))
(defn value-for-key
[id v]
(:value (first (filter #(= (:key %) id) v))))
(defn customizer-select
[]
(defn customizer-select []
(let [open (reagent/atom nil)]
(fn [{:keys [label key state options]}]
(let [state* (reagent/cursor state [key])
@@ -123,63 +109,54 @@
[rn/view {:style container}
[label-view state label]
[rn/view {:style {:flex 0.6}}
[rn/modal
{:visible @open
:on-request-close #(reset! open false)
:statusBarTranslucent true
:transparent true
:animation :slide}
[rn/modal {:visible @open
:on-request-close #(reset! open false)
:statusBarTranslucent true
:transparent true
:animation :slide}
[rn/view {:style (modal-container)}
[rn/view {:style (modal-view)}
[rn/scroll-view
(doall
(for [{:keys [key value]} options]
^{:key key}
[rn/touchable-opacity
{:style (select-option-style (= @state* key))
:on-press #(do
(reset! open false)
(reset! state* key))}
[rn/touchable-opacity {:style (select-option-style (= @state* key))
:on-press #(do
(reset! open false)
(reset! state* key))}
[quo/text {:color (if (= @state* key) :link :secondary)}
value]]))
[rn/view {:flex-direction :row}
[rn/touchable-opacity
{:style (select-option-style false)
:on-press #(do
(reset! state* nil)
(reset! open false))}
[rn/touchable-opacity {:style (select-option-style false)
:on-press #(do
(reset! state* nil)
(reset! open false))}
[quo/text "Clear"]]
[rn/view {:width 16}]
[rn/touchable-opacity
{:style (select-option-style false)
:on-press #(reset! open false)}
[rn/touchable-opacity {:style (select-option-style false)
:on-press #(reset! open false)}
[quo/text "Close"]]]]]]]
[rn/touchable-opacity
{:style (select-style)
:on-press #(reset! open true)}
[rn/touchable-opacity {:style (select-style)
:on-press #(reset! open true)}
(if selected
[quo/text {:color :link} selected]
[quo/text "Select option"])
[rn/view
{:position :absolute
:right 16
:top 0
:bottom 0
:justify-content :center}
[rn/view {:position :absolute
:right 16
:top 0
:bottom 0
:justify-content :center}
[quo/text "↓"]]]]]))))
(defn customizer
[state descriptors]
[rn/view
{:style {:flex 1}
:padding-horizontal 16}
(defn customizer [state descriptors]
[rn/view {:style {:flex 1}
:padding-horizontal 16}
(doall
(for [{:keys [key type]
:as desc}
descriptors
:let [descriptor (merge desc
{:state state})]]
:as desc} descriptors
:let [descriptor (merge desc
{:state state})]]
^{:key key}
[:<>
(case type
+73 -83
View File
@@ -1,99 +1,89 @@
(ns quo.previews.text
(:require [quo.animated :as animated]
(:require [reagent.core :as reagent]
[quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]
[quo.animated :as animated]
[quo.react-native :as rn]
[reagent.core :as reagent]))
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]))
(def all-props
(preview/list-comp [size [:tiny :small :base :large :x-large :xx-large]
weight [:regular :medium :semi-bold :bold :monospace]]
{:weight weight
:size size}))
(def all-props (preview/list-comp [size [:tiny :small :base :large :x-large :xx-large]
weight [:regular :medium :semi-bold :bold :monospace]]
{:weight weight
:size size}))
(def descriptor
[{:label "Size:"
:key :size
:type :select
:options [{:key :tiny
:value "Tiny"}
{:key :small
:value "Small"}
{:key :base
:value "Base"}
{:key :large
:value "Large"}
{:key :x-large
:value "X-Large"}
{:key :xx-large
:value "XX-Large"}]}
{:label "Weight:"
:key :weight
:type :select
:options [{:key :regular
:value "Regular"}
{:key :medium
:value "Medium"}
{:key :semi-bold
:value "Semi-bold"}
{:key :bold
:value "Bold"}
{:key :monospace
:value "Monospace"}]}
{:label "Color:"
:key :color
:type :select
:options [{:key :main
:value "main"}
{:key :secondary
:value "secondary"}
{:key :secondary-inverse
:value "secondary-inverse"}
{:key :link
:value "link"}
{:key :negative
:value "negative"}
{:key :positive
:value "positive"}]}
{:label "Animated:"
:key :animated?
:type :boolean}])
(def descriptor [{:label "Size:"
:key :size
:type :select
:options [{:key :tiny
:value "Tiny"}
{:key :small
:value "Small"}
{:key :base
:value "Base"}
{:key :large
:value "Large"}
{:key :x-large
:value "X-Large"}
{:key :xx-large
:value "XX-Large"}]}
{:label "Weight:"
:key :weight
:type :select
:options [{:key :regular
:value "Regular"}
{:key :medium
:value "Medium"}
{:key :semi-bold
:value "Semi-bold"}
{:key :bold
:value "Bold"}
{:key :monospace
:value "Monospace"}]}
{:label "Color:"
:key :color
:type :select
:options [{:key :main
:value "main"}
{:key :secondary
:value "secondary"}
{:key :secondary-inverse
:value "secondary-inverse"}
{:key :link
:value "link"}
{:key :negative
:value "negative"}
{:key :positive
:value "positive"}]}
{:label "Animated:"
:key :animated?
:type :boolean}])
(defn render-item
[props]
[rn/view
{:style {:padding-vertical 24
:padding-horizontal 16}}
(defn render-item [props]
[rn/view {:style {:padding-vertical 24
:padding-horizontal 16}}
[quo/text props
(str "Text size " props " number 0 1x2")]])
(defn cool-preview
[]
(defn cool-preview []
(let [state (reagent/atom {})
animation (animated/value 0)]
animation (animated/value 0)]
(fn []
[rn/view
{:margin-bottom 50
:padding 16}
[rn/view {:margin-bottom 50
:padding 16}
[animated/code {:exec (animated/set animation (animated/loop* {:duration 1000}))}]
[preview/customizer state descriptor]
[rn/view {:padding-vertical 16}
[quo/text
(merge @state
(when (:animated? @state)
{:opacity animation}))
[quo/text (merge @state
(when (:animated? @state)
{:opacity animation}))
"This is a demo text 1 2 0 2x2 0x0"]]])))
(defn preview-text
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])
(defn preview-text []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])
+64 -74
View File
@@ -1,62 +1,57 @@
(ns quo.previews.text-input
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]
(:require [reagent.core :as reagent]
[quo.core :as quo]
[quo.react-native :as rn]
[reagent.core :as reagent]))
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]))
(def all-props
(preview/list-comp [multiline [false true]
label [nil "Input label"]
default-value [nil "Test initial value"]
placeholder [nil "Placeholder value"]
before [nil {:icon :main-icons/search}]
after [nil {:icon :main-icons/close}]
error [nil "Something went wrong!"]
secure [false true]
show-cancel [false true]]
{:label label
:default-value default-value
:placeholder placeholder
:multiline multiline
:before before
:after after
:error error
:show-cancel show-cancel
:secure-text-entry secure}))
(def all-props (preview/list-comp [multiline [false true]
label [nil "Input label"]
default-value [nil "Test initial value"]
placeholder [nil "Placeholder value"]
before [nil {:icon :main-icons/search}]
after [nil {:icon :main-icons/close}]
error [nil "Something went wrong!"]
secure [false true]
show-cancel [false true]]
{:label label
:default-value default-value
:placeholder placeholder
:multiline multiline
:before before
:after after
:error error
:show-cancel show-cancel
:secure-text-entry secure}))
(def descriptor
[{:label "Multiline:"
:key :multiline
:type :boolean}
{:label "Show cancel:"
:key :show-cancel
:type :boolean}
{:label "Secure:"
:key :secure-text-entry
:type :boolean}
{:label "After icon:"
:key :after
:type :boolean}
{:label "Before icon:"
:key :before
:type :boolean}
{:label "Show error:"
:key :error
:type :boolean}
{:label "Label"
:key :label
:type :text}])
(def descriptor [{:label "Multiline:"
:key :multiline
:type :boolean}
{:label "Show cancel:"
:key :show-cancel
:type :boolean}
{:label "Secure:"
:key :secure-text-entry
:type :boolean}
{:label "After icon:"
:key :after
:type :boolean}
{:label "Before icon:"
:key :before
:type :boolean}
{:label "Show error:"
:key :error
:type :boolean}
{:label "Label"
:key :label
:type :text}])
(defn render-item
[props]
[rn/view
{:style {:padding-horizontal 16
:padding-vertical 24}}
(defn render-item [props]
[rn/view {:style {:padding-horizontal 16
:padding-vertical 24}}
[quo/text-input props]])
(defn cool-preview
[]
(defn cool-preview []
(let [state (reagent/atom {:secure false
:show-cancel false
:multiline false
@@ -65,27 +60,22 @@
after (reagent/cursor state [:after])
error (reagent/cursor state [:error])]
(fn []
[rn/view
{:margin-bottom 50
:padding 16}
[rn/view {:margin-bottom 50
:padding 16}
[preview/customizer state descriptor]
[quo/text-input
(merge @state
{:default-value nil
:placeholder "I'm a cool placeholder"
:before (when @before {:icon :main-icons/search})
:after (when @after {:icon :main-icons/close})
:error (when @error "Something went wrong!")})]])))
[quo/text-input (merge @state
{:default-value nil
:placeholder "I'm a cool placeholder"
:before (when @before {:icon :main-icons/search})
:after (when @after {:icon :main-icons/close})
:error (when @error "Something went wrong!")})]])))
(defn preview-text
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])
(defn preview-text []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])
+21 -29
View File
@@ -1,38 +1,30 @@
(ns quo.previews.tooltip
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.react-native :as rn])
[quo.react-native :as rn]
[quo.design-system.colors :as colors])
(:require-macros [quo.previews.preview :as preview]))
(def all-props
(preview/list-comp
[child [[quo/text {:size :small} "Simple text"]
[quo/text
{:color :negative
:size :small}
"Error text"]
[rn/view {:width 100 :height 20 :background-color :red}]
[quo/text
"Just text, but long. Officia autem est repellendus ad quia exercitationem veniam."]]]
child))
(def all-props (preview/list-comp
[child [[quo/text {:size :small} "Simple text"]
[quo/text {:color :negative
:size :small}
"Error text"]
[rn/view {:width 100 :height 20 :background-color :red}]
[quo/text "Just text, but long. Officia autem est repellendus ad quia exercitationem veniam."]]]
child))
(defn render-item
[children]
(defn render-item [children]
[rn/view {:margin-vertical 50}
[rn/view
{:height 20
:background-color "rgba(0,0,0,0.1)"}]
[rn/view {:height 20
:background-color "rgba(0,0,0,0.1)"}]
[quo/tooltip {}
children]])
(defn preview-tooltip
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:data all-props
:render-fn render-item
:key-fn str}]])
(defn preview-tooltip []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:data all-props
:render-fn render-item
:key-fn str}]])
+2 -4
View File
@@ -1,11 +1,9 @@
(ns quo.react)
(defmacro maybe-js-deps
[deps]
(defmacro maybe-js-deps [deps]
`(if ~deps (into-array ~deps) js/undefined))
(defmacro with-deps-check
[[prev-deps] f deps]
(defmacro with-deps-check [[prev-deps] f deps]
`(let [~prev-deps (quo.react/ref ~deps)]
(when (not= @~prev-deps ~deps)
(reset! ~prev-deps ~deps))
+38 -43
View File
@@ -2,12 +2,12 @@
(:refer-clojure :exclude [ref])
(:require ["react" :as react]
[oops.core :refer [oget oset!]])
(:require-macros [quo.react :refer [with-deps-check maybe-js-deps]]))
(:require-macros [quo.react :refer [with-deps-check
maybe-js-deps]]))
(def create-ref react/createRef)
(defn current-ref
[ref]
(defn current-ref [ref]
(oget ref "current"))
;; Inspired from UIX, Rum and Rumext
@@ -16,35 +16,33 @@
(oset! ref "current" val)
val)
(defn set-native-props
[^js ref ^js props]
(defn set-native-props [^js ref ^js props]
(when-let [curr-ref ^js (current-ref ref)]
(.setNativeProps curr-ref props)))
(deftype StateHook [value set-value]
cljs.core/IHash
(-hash [o] (goog/getUid o))
(-hash [o] (goog/getUid o))
cljs.core/IDeref
(-deref [_o]
value)
(-deref [_o]
value)
cljs.core/IReset
(-reset! [_o new-value]
(set-value new-value))
(-reset! [_o new-value]
(set-value new-value))
cljs.core/ISwap
(-swap! [_o f]
(set-value f))
(-swap! [_o f a]
(set-value #(f % a)))
(-swap! [_o f a b]
(set-value #(f % a b)))
(-swap! [_o f a b xs]
(set-value #(apply f % a b xs))))
(-swap! [_o f]
(set-value f))
(-swap! [_o f a]
(set-value #(f % a)))
(-swap! [_o f a b]
(set-value #(f % a b)))
(-swap! [_o f a b xs]
(set-value #(apply f % a b xs))))
(defn state
[value]
(defn state [value]
(let [[value set-value] (react/useState value)
sh (react/useMemo #(StateHook. value set-value) #js [])]
(react/useMemo (fn []
@@ -53,33 +51,31 @@
sh)
#js [value set-value])))
(defn use-ref
[val]
(defn use-ref [val]
(let [ref (react/useRef val)]
(reify
cljs.core/IHash
(-hash [_] (goog/getUid ref))
cljs.core/IHash
(-hash [_] (goog/getUid ref))
cljs.core/IDeref
(-deref [_]
(current-ref ref))
cljs.core/IDeref
(-deref [_]
(current-ref ref))
cljs.core/IReset
(-reset! [_ new-value]
(set-ref-val! ref new-value))
cljs.core/IReset
(-reset! [_ new-value]
(set-ref-val! ref new-value))
cljs.core/ISwap
(-swap! [_ f]
(-reset! ref (f (current-ref ref))))
(-swap! [_ f a]
(-reset! ref (f (current-ref ref) a)))
(-swap! [_ f a b]
(-reset! ref (f (current-ref ref) a b)))
(-swap! [_ f a b xs]
(-reset! ref (apply f (current-ref ref) a b xs))))))
cljs.core/ISwap
(-swap! [_ f]
(-reset! ref (f (current-ref ref))))
(-swap! [_ f a]
(-reset! ref (f (current-ref ref) a)))
(-swap! [_ f a b]
(-reset! ref (f (current-ref ref) a b)))
(-swap! [_ f a b xs]
(-reset! ref (apply f (current-ref ref) a b xs))))))
(defn ref
[value]
(defn ref [value]
(let [vref (use-ref value)]
(react/useMemo (fn [] vref) #js [])))
@@ -129,8 +125,7 @@
(def memo react/memo)
(defn get-children
[^js children]
(defn get-children [^js children]
(->> children
(react/Children.toArray)
(into [])))
+53 -76
View File
@@ -1,13 +1,13 @@
(ns quo.react-native
(:require ["@react-native-community/hooks" :as hooks]
["react-native" :as rn]
["react-native-draggable-flatlist" :default DraggableFlatList]
["react-native-hole-view" :refer (RNHoleView)]
["react-native-navigation" :refer (Navigation)]
["rn-emoji-keyboard" :refer (EmojiKeyboard)]
(:require [reagent.core :as reagent]
[cljs-bean.core :as bean]
[quo.platform :as platform]
[reagent.core :as reagent]))
["react-native" :as rn]
["@react-native-community/hooks" :as hooks]
["react-native-navigation" :refer (Navigation)]
["react-native-hole-view" :refer (RNHoleView)]
["rn-emoji-keyboard" :refer (EmojiKeyboard)]
["react-native-draggable-flatlist" :default DraggableFlatList]))
(def hole-view (reagent/adapt-react-class RNHoleView))
@@ -25,9 +25,7 @@
(def image (reagent/adapt-react-class (.-Image rn)))
(def text (reagent/adapt-react-class (.-Text ^js rn)))
(defn resolve-asset-source
[uri]
(js->clj (.resolveAssetSource ^js (.-Image ^js rn) uri) :keywordize-keys true))
(defn resolve-asset-source [uri] (js->clj (.resolveAssetSource ^js (.-Image ^js rn) uri) :keywordize-keys true))
(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js rn)))
(def modal (reagent/adapt-react-class (.-Modal ^js rn)))
@@ -44,12 +42,11 @@
(.then (.constants Navigation)
(fn [^js consts]
(reset! navigation-const {:top-bar-height (.-topBarHeight consts)
(reset! navigation-const {:top-bar-height (.-topBarHeight consts)
:bottom-tabs-height (.-bottomTabsHeight consts)
:status-bar-height (.-statusBarHeight consts)})))
:status-bar-height (.-statusBarHeight consts)})))
(defn keyboard-avoiding-view
[]
(defn keyboard-avoiding-view []
(let [this (reagent/current-component)
props (reagent/props this)]
(into [keyboard-avoiding-view-class
@@ -69,8 +66,7 @@
(def pan-responder (.-PanResponder ^js rn))
(defn create-pan-responder
[opts]
(defn create-pan-responder [opts]
(.create ^js pan-responder (clj->js opts)))
(def animated (.-Animated rn))
@@ -83,99 +79,82 @@
(def animated-view
(reagent/adapt-react-class (.-View ^js animated)))
(def ui-manager (.-UIManager ^js rn))
(def ui-manager (.-UIManager ^js rn))
(def layout-animation (.-LayoutAnimation ^js rn))
(def configure-next (.-configureNext ^js layout-animation))
(def create-animation (.-create ^js layout-animation))
(def layout-animation-presets
{:ease-in-ease-out (-> ^js layout-animation .-Presets .-easeInEaseOut)
:linear (-> ^js layout-animation .-Presets .-linear)
:spring (-> ^js layout-animation .-Presets .-spring)})
(def layout-animation-presets {:ease-in-ease-out (-> ^js layout-animation .-Presets .-easeInEaseOut)
:linear (-> ^js layout-animation .-Presets .-linear)
:spring (-> ^js layout-animation .-Presets .-spring)})
(def layout-animation-types
{:spring (-> ^js layout-animation .-Types .-spring)
:linear (-> ^js layout-animation .-Types .-linear)
:ease-in-ease-out (-> ^js layout-animation .-Types .-easeInEaseOut)
:ease-in (-> ^js layout-animation .-Types .-easeIn)
:ease-out (-> ^js layout-animation .-Types .-easeOut)})
(def layout-animation-types {:spring (-> ^js layout-animation .-Types .-spring)
:linear (-> ^js layout-animation .-Types .-linear)
:ease-in-ease-out (-> ^js layout-animation .-Types .-easeInEaseOut)
:ease-in (-> ^js layout-animation .-Types .-easeIn)
:ease-out (-> ^js layout-animation .-Types .-easeOut)})
(def layout-animation-properties
{:opacity (-> ^js layout-animation .-Properties .-opacity)
:scale-x (-> ^js layout-animation .-Properties .-scaleX)
:scale-y (-> ^js layout-animation .-Properties .-scaleY)
:scale-xy (-> ^js layout-animation .-Properties .-scaleXY)})
(def layout-animation-properties {:opacity (-> ^js layout-animation .-Properties .-opacity)
:scale-x (-> ^js layout-animation .-Properties .-scaleX)
:scale-y (-> ^js layout-animation .-Properties .-scaleY)
:scale-xy (-> ^js layout-animation .-Properties .-scaleXY)})
(def custom-animations
{:ease-opacity-200 #js
{:duration 200
:create #js
{:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}
:update #js
{:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}
:delete #js
{:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}}})
(def custom-animations {:ease-opacity-200 #js {:duration 200
:create #js {:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}
:update #js {:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}
:delete #js {:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}}})
(defonce enable-layout-animations
(when platform/android?
(.setLayoutAnimationEnabledExperimental ^js ui-manager true)))
(when platform/android?
(.setLayoutAnimationEnabledExperimental ^js ui-manager true)))
(def activity-indicator (reagent/adapt-react-class (.-ActivityIndicator ^js rn)))
;; Flat-list
(def ^:private rn-flat-list (reagent/adapt-react-class (.-FlatList ^js rn)))
(defn- wrap-render-fn
[f render-data]
(defn- wrap-render-fn [f render-data]
(fn [data]
(reagent/as-element [f (.-item ^js data) (.-index ^js data)
(.-separators ^js data) render-data
(.-isActive ^js data) (.-drag ^js data)])))
(defn- wrap-on-drag-end-fn
[f]
(defn- wrap-on-drag-end-fn [f]
(fn [data]
(f (.-from ^js data) (.-to ^js data) (.-data ^js data))))
(defn- wrap-key-fn
[f]
(defn- wrap-key-fn [f]
(fn [data index]
{:post [(some? %)]}
(f data index)))
(defn base-list-props
[{:keys [key-fn render-fn empty-component header footer separator data render-data on-drag-end-fn]
:as props}]
(merge
{:data (to-array data)}
(when key-fn {:keyExtractor (wrap-key-fn key-fn)})
(when render-fn {:renderItem (wrap-render-fn render-fn render-data)})
(when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))})
(when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))})
(when header {:ListHeaderComponent (reagent/as-element header)})
(when footer {:ListFooterComponent (reagent/as-element footer)})
(when on-drag-end-fn {:onDragEnd (wrap-on-drag-end-fn on-drag-end-fn)})
(dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn :on-drag-end-fn)))
[{:keys [key-fn render-fn empty-component header footer separator data render-data on-drag-end-fn] :as props}]
(merge {:data (to-array data)}
(when key-fn {:keyExtractor (wrap-key-fn key-fn)})
(when render-fn {:renderItem (wrap-render-fn render-fn render-data)})
(when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))})
(when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))})
(when header {:ListHeaderComponent (reagent/as-element header)})
(when footer {:ListFooterComponent (reagent/as-element footer)})
(when on-drag-end-fn {:onDragEnd (wrap-on-drag-end-fn on-drag-end-fn)})
(dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn :on-drag-end-fn)))
(defn flat-list
[props]
(defn flat-list [props]
[rn-flat-list (base-list-props props)])
(defn draggable-flat-list
[props]
(defn draggable-flat-list [props]
[rn-draggable-flatlist (base-list-props props)])
(defn animated-flat-list
[props]
(defn animated-flat-list [props]
[animated-flat-list-class (base-list-props props)])
;; Hooks
(defn use-window-dimensions
[]
(defn use-window-dimensions []
(let [window (rn/useWindowDimensions)]
{:font-scale (.-fontScale window)
:height (.-height ^js window)
@@ -184,14 +163,12 @@
(def use-back-handler (.-useBackHandler hooks))
(defn use-keyboard
[]
(defn use-keyboard []
(let [kb (.useKeyboard hooks)]
{:keyboard-shown (.-keyboardShown ^js kb)
:keyboard-height (.-keyboardHeight ^js kb)}))
(defn use-layout
[]
(defn use-layout []
(let [{:keys [onLayout x y height width]} (bean/bean (.useLayout hooks))]
{:on-layout onLayout
:x x
+3 -6
View File
@@ -4,16 +4,13 @@
(def theme (reagent/atom nil))
(defn dark?
[]
(defn dark? []
(= :dark @theme))
(defn get-theme
[]
(defn get-theme []
@theme)
(defn set-theme
[value]
(defn set-theme [value]
(reset! theme value)
(reset! colors/theme (case value
:dark colors/dark-theme
+15 -18
View File
@@ -1,15 +1,14 @@
(ns quo2.components.avatars.account-avatar
(:require [quo2.components.icon :as icons]
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
[quo2.components.icon :as icons]
[quo2.theme :as theme]))
(def icon-color-value
{:dark (get-in colors/customization [:dark :purple])
:light (get-in colors/customization [:light :purple])})
(defn get-border-radius
[size]
(defn get-border-radius [size]
(case size
80 16
48 12
@@ -17,8 +16,7 @@
24 8
20 6))
(defn get-inner-icon-sizes
[size]
(defn get-inner-icon-sizes [size]
(case size
80 36
48 24
@@ -30,18 +28,17 @@
[{:keys [size icon]
:or {size 80
icon :main-icons/placeholder}}]
(let [icon-color (if (theme/dark?)
(:dark icon-color-value)
(:light icon-color-value))
(let [icon-color (if (theme/dark?)
(:dark icon-color-value)
(:light icon-color-value))
avatar-border-radius (get-border-radius size)
inner-icon-size (get-inner-icon-sizes size)]
[rn/view
{:style {:width size
:background-color icon-color
:height size
:border-radius avatar-border-radius
:justify-content :center
:align-items :center}}
inner-icon-size (get-inner-icon-sizes size)]
[rn/view {:style {:width size
:background-color icon-color
:height size
:border-radius avatar-border-radius
:justify-content :center
:align-items :center}}
[icons/icon icon
{:no-color true
:size inner-icon-size}]]))
+32 -38
View File
@@ -1,47 +1,41 @@
(ns quo2.components.avatars.channel-avatar
(:require [quo2.components.icon :as icons]
(:require [quo2.foundations.colors :as colors]
[react-native.core :as rn]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
[quo2.theme :as theme]))
(defn channel-avatar
[{:keys [big? locked? emoji-background-color emoji]}]
(defn channel-avatar [{:keys [big? locked? emoji-background-color emoji]}]
(let [lock-exists? (some? locked?)
dark? (theme/dark?)]
[rn/view
{:style {:width (if big? 32 24)
:height (if big? 32 24)
:border-radius (if big? 32 24)
:justify-content :center
:align-items :center
:background-color emoji-background-color}}
[rn/view
{:style {:display :flex
:justify-content :center
:align-items :center}}
[text/text
{:size (if big?
:paragraph-1
:label)} emoji]
[rn/view {:style {:width (if big? 32 24)
:height (if big? 32 24)
:border-radius (if big? 32 24)
:justify-content :center
:align-items :center
:background-color emoji-background-color}}
[rn/view {:style {:display :flex
:justify-content :center
:align-items :center}}
[text/text {:size (if big?
:paragraph-1
:label)} emoji]
(when lock-exists?
[rn/view
{:style {:position :absolute
:left (if big?
14
8)
:top (if big?
15
8)
:background-color (if dark?
colors/neutral-90
colors/white)
:border-radius 15
:padding 2}}
[icons/icon
(if locked?
:main-icons/locked
:main-icons/unlocked)
[rn/view {:style {:position :absolute
:left (if big?
14
8)
:top (if big?
15
8)
:background-color (if dark?
colors/neutral-90
colors/white)
:border-radius 15
:padding 2}}
[icons/icon (if locked?
:main-icons/locked
:main-icons/unlocked)
{:color (if dark?
colors/neutral-40
colors/neutral-50)
+12 -16
View File
@@ -1,6 +1,6 @@
(ns quo2.components.avatars.group-avatar
(:require [quo2.components.icon :as icon]
[quo2.foundations.colors :as colors]
(:require [quo2.foundations.colors :as colors]
[quo2.components.icon :as icon]
[react-native.core :as rn]))
(def sizes
@@ -11,20 +11,16 @@
:medium 32
:large 48}})
;; TODO: this implementation does not support group display picture (can only display default group
;; icon).
(defn group-avatar
[_]
;; TODO: this implementation does not support group display picture (can only display default group icon).
(defn group-avatar [_]
(fn [{:keys [color size]}]
(let [container-size (get-in sizes [:container size])
icon-size (get-in sizes [:icon size])]
[rn/view
{:width container-size
:height container-size
:align-items :center
:justify-content :center
:border-radius (/ container-size 2)
:background-color (colors/custom-color-by-theme color 50 60)}
[icon/icon :i/group
{:size icon-size
:color colors/white-opa-70}]])))
[rn/view {:width container-size
:height container-size
:align-items :center
:justify-content :center
:border-radius (/ container-size 2)
:background-color (colors/custom-color-by-theme color 50 60)}
[icon/icon :i/group {:size icon-size
:color colors/white-opa-70}]])))
+16 -18
View File
@@ -1,7 +1,7 @@
(ns quo2.components.avatars.icon-avatar
(:require [quo2.components.icon :as icons]
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
[quo2.components.icon :as icons]))
(def sizes
{:big 48
@@ -12,19 +12,17 @@
[{:keys [size icon color opacity]
:or {opacity 20}}]
(let [component-size (size sizes)
circle-color (colors/custom-color color 50 opacity)
icon-color (colors/custom-color-by-theme color 50 60)
icon-size (case size
:big 20
:medium 16
:small 12)]
[rn/view
{:style {:width component-size
:height component-size
:border-radius component-size
:background-color circle-color
:justify-content :center
:align-items :center}}
[icons/icon icon
{:size icon-size
:color icon-color}]]))
circle-color (colors/custom-color color 50 opacity)
icon-color (colors/custom-color-by-theme color 50 60)
icon-size (case size
:big 20
:medium 16
:small 12)]
[rn/view {:style {:width component-size
:height component-size
:border-radius component-size
:background-color circle-color
:justify-content :center
:align-items :center}}
[icons/icon icon {:size icon-size
:color icon-color}]]))
+89 -100
View File
@@ -1,96 +1,91 @@
(ns quo2.components.avatars.user-avatar
(:require [clojure.string :refer [blank? split upper-case]]
[quo2.components.icon :as icons]
(:require [react-native.core :as rn]
[react-native.fast-image :as fast-image]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]
[quo2.theme :refer [dark?]]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]))
[clojure.string :refer [upper-case split blank?]]))
(def sizes
{:big {:outer 80
:inner 72
:status-indicator 20
:status-indicator-border 4
:font-size :heading-1}
:medium {:outer 48
:inner 44
:status-indicator 12
:status-indicator-border 2
:font-size :paragraph-1}
:small {:outer 32
:inner 28
:status-indicator 12
:status-indicator-border 2
:font-size :paragraph-2}
:xs {:outer 24
:inner 24
:status-indicator 0
:status-indicator-border 0
:font-size :paragraph-2}
:xxs {:outer 20
:inner 20
:status-indicator 0
:status-indicator-border 0
:font-size :label}
:xxxs {:outer 16
:inner 16
:status-indicator 0
:status-indicator-border 0
:font-size :label}})
(def sizes {:big {:outer 80
:inner 72
:status-indicator 20
:status-indicator-border 4
:font-size :heading-1}
:medium {:outer 48
:inner 44
:status-indicator 12
:status-indicator-border 2
:font-size :paragraph-1}
:small {:outer 32
:inner 28
:status-indicator 12
:status-indicator-border 2
:font-size :paragraph-2}
:xs {:outer 24
:inner 24
:status-indicator 0
:status-indicator-border 0
:font-size :paragraph-2}
:xxs {:outer 20
:inner 20
:status-indicator 0
:status-indicator-border 0
:font-size :label}
:xxxs {:outer 16
:inner 16
:status-indicator 0
:status-indicator-border 0
:font-size :label}})
(defn dot-indicator
[size status-indicator? online? ring? dark?]
(when status-indicator?
(let [dimensions (get-in sizes [size :status-indicator])
(let [dimensions (get-in sizes [size :status-indicator])
border-width (get-in sizes [size :status-indicator-border])
right (case size
:big 2
:medium 0
:small -2
0)
bottom (case size
:big (if ring?
-1
2)
:medium (if ring?
0
-2)
:small (if ring?
-2
-2)
0)]
[rn/view
{:style {:background-color (if online?
colors/success-50
colors/neutral-40)
:width dimensions
:height dimensions
:border-width border-width
:border-radius dimensions
:border-color (if dark?
colors/neutral-100
colors/white)
:position :absolute
:bottom bottom
:right right}}])))
right (case size
:big 2
:medium 0
:small -2
0)
bottom (case size
:big (if ring?
-1
2)
:medium (if ring?
0
-2)
:small (if ring?
-2
-2)
0)]
[rn/view {:style {:background-color (if online?
colors/success-50
colors/neutral-40)
:width dimensions
:height dimensions
:border-width border-width
:border-radius dimensions
:border-color (if dark?
colors/neutral-100
colors/white)
:position :absolute
:bottom bottom
:right right}}])))
(defn container-styling
[inner-dimensions outer-dimensions]
{:width inner-dimensions
:position :absolute
:top (/ (- outer-dimensions inner-dimensions) 2)
:left (/ (- outer-dimensions inner-dimensions) 2)
:height inner-dimensions
(defn container-styling [inner-dimensions outer-dimensions]
{:width inner-dimensions
:position :absolute
:top (/ (- outer-dimensions inner-dimensions) 2)
:left (/ (- outer-dimensions inner-dimensions) 2)
:height inner-dimensions
:border-radius inner-dimensions})
(defn container
[inner-dimensions outer-dimensions & children]
[rn/view
{:style (merge {:background-color (colors/custom-color-by-theme :turquoise 50 60)
:justify-content :center
:align-items :center}
(container-styling inner-dimensions outer-dimensions))}
(defn container [inner-dimensions outer-dimensions & children]
[rn/view {:style (merge {:background-color (colors/custom-color-by-theme :turquoise 50 60)
:justify-content :center
:align-items :center}
(container-styling inner-dimensions outer-dimensions))}
children])
(def small-sizes #{:xs :xxs :xxxs})
@@ -117,11 +112,9 @@
identicon? (contains? identicon-sizes size)
small? (contains? small-sizes size)
outer-dimensions (get-in sizes [size :outer])
inner-dimensions (get-in sizes
[size
(if ring?
:inner
:outer)])
inner-dimensions (get-in sizes [size (if ring?
:inner
:outer)])
font-size (get-in sizes [size :font-size])
icon-text (if-not (or (blank? first-initial-letter)
(blank? initials))
@@ -129,26 +122,22 @@
first-initial-letter
initials)
"")]
[rn/view
{:accessibility-label :user-avatar
:style {:width outer-dimensions
:height outer-dimensions
:border-radius outer-dimensions}}
[rn/view {:accessibility-label :user-avatar
:style {:width outer-dimensions
:height outer-dimensions
:border-radius outer-dimensions}}
(when (and ring? identicon?)
[icons/icon :i/identicon-ring
{:size outer-dimensions
:no-color true}])
[icons/icon :i/identicon-ring {:size outer-dimensions
:no-color true}])
(if profile-picture
;; display image
[fast-image/fast-image
{:source {:uri profile-picture}
:style (container-styling inner-dimensions outer-dimensions)}]
[fast-image/fast-image {:source {:uri profile-picture}
:style (container-styling inner-dimensions outer-dimensions)}]
;; else display initials
[container inner-dimensions outer-dimensions
^{:key :icon-text}
[text/text
{:weight :semi-bold
:size font-size
:style {:color colors/white-opa-70}}
[text/text {:weight :semi-bold
:size font-size
:style {:color colors/white-opa-70}}
(upper-case icon-text)]])
[dot-indicator size status-indicator? online? ring? (dark?)]]))
@@ -1,35 +1,31 @@
(ns quo2.components.avatars.wallet-user-avatar
(:require [clojure.string :as string]
[quo2.components.markdown.text :as text]
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
[quo2.components.markdown.text :as text]
[clojure.string :as string]))
(def circle-sizes
{:small 20
:medium 32
:large 48
:x-large 80})
(def circle-sizes {:small 20
:medium 32
:large 48
:x-large 80})
(def font-sizes
{:small :label
:medium :paragraph-2
:large :paragraph-1
:x-large :heading-1})
(def font-sizes {:small :label
:medium :paragraph-2
:large :paragraph-1
:x-large :heading-1})
(def font-weights
{:small :medium
:medium :semi-bold
:large :semi-bold
:x-large :medium})
(def font-weights {:small :medium
:medium :semi-bold
:large :semi-bold
:x-large :medium})
(defn wallet-user-avatar
"params, first name, last name, color, size
and if it's dark or not!"
[{:keys [f-name l-name color size]
:or {f-name "John"
l-name "Doe"
color :red
size :x-large}}]
[{:keys [f-name l-name color size] :or {f-name "John"
l-name "Doe"
color :red
size :x-large}}]
(let [circle-size (size circle-sizes)
small? (= size :small)
f-name-initial (-> f-name
@@ -40,18 +36,16 @@
(subs 0 1))
circle-color (colors/custom-color color 50 20)
text-color (colors/custom-color-by-theme color 50 60)]
[rn/view
{:style {:width circle-size
:height circle-size
:border-radius circle-size
:text-align :center
:justify-content :center
:align-items :center
:background-color circle-color}}
[text/text
{:size (size font-sizes)
:weight (size font-weights)
:style {:color text-color}}
[rn/view {:style {:width circle-size
:height circle-size
:border-radius circle-size
:text-align :center
:justify-content :center
:align-items :center
:background-color circle-color}}
[text/text {:size (size font-sizes)
:weight (size font-weights)
:style {:color text-color}}
(if small?
(str f-name-initial)
(str f-name-initial l-name-initial))]]))
+211 -247
View File
@@ -1,143 +1,142 @@
(ns quo2.components.buttons.button
(:require [quo2.components.icon :as quo2.icons]
[quo2.components.markdown.text :as text]
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
[quo2.components.markdown.text :as text]
[quo2.theme :as theme]
[react-native.core :as rn]
[reagent.core :as reagent]))
[reagent.core :as reagent]
[quo2.components.icon :as quo2.icons]))
(def themes
{:light {:primary {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/primary-50
:pressed colors/primary-60
:disabled colors/primary-50}}
:secondary {:icon-color colors/primary-50
:label-color colors/primary-50
:background-color {:default colors/primary-50-opa-20
:pressed colors/primary-50-opa-40
:disabled colors/primary-50-opa-20}}
:grey {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:default colors/neutral-10
:pressed colors/neutral-20
:disabled colors/neutral-10}}
:dark-grey {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:default colors/neutral-20
:pressed colors/neutral-30
:disabled colors/neutral-20}}
:outline {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:border-color {:default colors/neutral-20
:pressed colors/neutral-40
:disabled colors/neutral-20}}
:ghost {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:pressed colors/neutral-10}}
:danger {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/danger-50
:pressed colors/danger-60
:disabled colors/danger-50}}
:positive {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/success-50
:pressed colors/success-60
:disabled colors/success-50-opa-30}}
:photo-bg {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:background-color {:default colors/white-opa-40
:pressed colors/white-opa-50
:disabled colors/white-opa-40}}
:blur-bg {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:background-color {:default colors/neutral-80-opa-5
:pressed colors/neutral-80-opa-10
:disabled colors/neutral-80-opa-5}}
:blur-bg-outline {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:border-color {:default colors/neutral-80-opa-10
:pressed colors/neutral-80-opa-20
:disabled colors/neutral-80-opa-10}}
:shell {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/neutral-95
:pressed colors/neutral-95
:disabled colors/neutral-95}}}
:dark {:primary {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/primary-60
:pressed colors/primary-50
:disabled colors/primary-60}}
:secondary {:icon-color colors/primary-50
:label-color colors/primary-50
:background-color {:default colors/primary-50-opa-20
:pressed colors/primary-50-opa-30
:disabled colors/primary-50-opa-20}}
:grey {:icon-color colors/white
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:default colors/neutral-80
:pressed colors/neutral-60
:disabled colors/neutral-80}}
:dark-grey {:icon-color colors/white
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:default colors/neutral-70
:pressed colors/neutral-60
:disabled colors/neutral-70}}
:outline {:icon-color colors/neutral-40
:icon-secondary-color colors/neutral-40
:label-color colors/white
:border-color {:default colors/neutral-70
:pressed colors/neutral-60
:disabled colors/neutral-70}}
:ghost {:icon-color colors/neutral-40
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:pressed colors/neutral-80}}
:danger {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/danger-60
:pressed colors/danger-50
:disabled colors/danger-60}}
:positive {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/success-60
:pressed colors/success-50
:disabled colors/success-60-opa-30}}
:photo-bg {:icon-color colors/white
:icon-secondary-color colors/neutral-30
:label-color colors/white
:background-color {:default colors/neutral-80-opa-40
:pressed colors/neutral-80-opa-50
:disabled colors/neutral-80-opa-40}}
:blur-bg {:icon-color colors/white
:icon-secondary-color colors/white-opa-40
:label-color colors/white
:background-color {:default colors/white-opa-5
:pressed colors/white-opa-10
:disabled colors/white-opa-5}}
:blur-bg-outline {:icon-color colors/white
:icon-secondary-color colors/white-opa-40
:label-color colors/white
:border-color {:default colors/white-opa-10
:pressed colors/white-opa-20
:disabled colors/white-opa-5}}
:shell {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/neutral-95}}}})
{:light {:primary {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/primary-50
:pressed colors/primary-60
:disabled colors/primary-50}}
:secondary {:icon-color colors/primary-50
:label-color colors/primary-50
:background-color {:default colors/primary-50-opa-20
:pressed colors/primary-50-opa-40
:disabled colors/primary-50-opa-20}}
:grey {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:default colors/neutral-10
:pressed colors/neutral-20
:disabled colors/neutral-10}}
:dark-grey {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:default colors/neutral-20
:pressed colors/neutral-30
:disabled colors/neutral-20}}
:outline {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:border-color {:default colors/neutral-20
:pressed colors/neutral-40
:disabled colors/neutral-20}}
:ghost {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:pressed colors/neutral-10}}
:danger {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/danger-50
:pressed colors/danger-60
:disabled colors/danger-50}}
:positive {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/success-50
:pressed colors/success-60
:disabled colors/success-50-opa-30}}
:photo-bg {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:background-color {:default colors/white-opa-40
:pressed colors/white-opa-50
:disabled colors/white-opa-40}}
:blur-bg {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:background-color {:default colors/neutral-80-opa-5
:pressed colors/neutral-80-opa-10
:disabled colors/neutral-80-opa-5}}
:blur-bg-outline {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:border-color {:default colors/neutral-80-opa-10
:pressed colors/neutral-80-opa-20
:disabled colors/neutral-80-opa-10}}
:shell {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/neutral-95
:pressed colors/neutral-95
:disabled colors/neutral-95}}}
:dark {:primary {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/primary-60
:pressed colors/primary-50
:disabled colors/primary-60}}
:secondary {:icon-color colors/primary-50
:label-color colors/primary-50
:background-color {:default colors/primary-50-opa-20
:pressed colors/primary-50-opa-30
:disabled colors/primary-50-opa-20}}
:grey {:icon-color colors/white
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:default colors/neutral-80
:pressed colors/neutral-60
:disabled colors/neutral-80}}
:dark-grey {:icon-color colors/white
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:default colors/neutral-70
:pressed colors/neutral-60
:disabled colors/neutral-70}}
:outline {:icon-color colors/neutral-40
:icon-secondary-color colors/neutral-40
:label-color colors/white
:border-color {:default colors/neutral-70
:pressed colors/neutral-60
:disabled colors/neutral-70}}
:ghost {:icon-color colors/neutral-40
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:pressed colors/neutral-80}}
:danger {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/danger-60
:pressed colors/danger-50
:disabled colors/danger-60}}
:positive {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/success-60
:pressed colors/success-50
:disabled colors/success-60-opa-30}}
:photo-bg {:icon-color colors/white
:icon-secondary-color colors/neutral-30
:label-color colors/white
:background-color {:default colors/neutral-80-opa-40
:pressed colors/neutral-80-opa-50
:disabled colors/neutral-80-opa-40}}
:blur-bg {:icon-color colors/white
:icon-secondary-color colors/white-opa-40
:label-color colors/white
:background-color {:default colors/white-opa-5
:pressed colors/white-opa-10
:disabled colors/white-opa-5}}
:blur-bg-outline {:icon-color colors/white
:icon-secondary-color colors/white-opa-40
:label-color colors/white
:border-color {:default colors/white-opa-10
:pressed colors/white-opa-20
:disabled colors/white-opa-5}}
:shell {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/neutral-95}}}})
(defn shape-style-container
[type icon size]
(defn shape-style-container [type icon size]
{:border-radius (if (and icon (#{:primary :secondary :danger} type))
24
(case size
@@ -146,43 +145,22 @@
32 10
24 8))})
(defn style-container
[type size disabled background-color border-color icon above width before after]
(defn style-container [type size disabled background-color border-color icon above width before after]
(merge {:height size
:align-items :center
:justify-content :center
:flex-direction (if above :column :row)
:background-color background-color
:padding-horizontal (when-not (or icon before after)
(case size
56 16
40 16
32 12
24 8))
(case size 56 16 40 16 32 12 24 8))
:padding-left (when-not (or icon before)
(case size
56 16
40 16
32 12
24 8))
(case size 56 16 40 16 32 12 24 8))
:padding-right (when-not (or icon after)
(case size
56 16
40 16
32 12
24 8))
(case size 56 16 40 16 32 12 24 8))
:padding-top (when-not (or icon before after)
(case size
56 0
40 9
32 5
24 3))
(case size 56 0 40 9 32 5 24 3))
:padding-bottom (when-not (or icon before after)
(case size
56 0
40 9
32 5
24 4))}
(case size 56 0 40 9 32 5 24 4))}
(shape-style-container type icon size)
(when width
{:width width})
@@ -194,8 +172,7 @@
(when disabled
{:opacity 0.3})))
(defn community-themed?
[type community-color]
(defn community-themed? [type community-color]
(and (= type :community) (string? community-color)))
(defn button
@@ -215,97 +192,85 @@
[button {:icon true} :main-icons/close-circle]"
[_ _]
(let [pressed (reagent/atom false)]
(fn
[{:keys [on-press disabled type size community-color community-text-color before after above
width
override-theme override-background-color
on-long-press accessibility-label icon icon-no-color style test-ID]
:or {type :primary
size 40}}
children]
(fn [{:keys [on-press disabled type size community-color community-text-color before after above width
override-theme override-background-color
on-long-press accessibility-label icon icon-no-color style test-ID]
:or {type :primary
size 40}}
children]
(let [{:keys [icon-color icon-secondary-color background-color label-color border-color]}
(get-in themes
[(or
override-theme
(theme/get-theme)) type])
state (cond disabled :disabled
@pressed :pressed
:else :default)
icon-size (when (= 24 size) 12)
(get-in themes [(or
override-theme
(theme/get-theme)) type])
state (cond disabled :disabled @pressed :pressed :else :default)
icon-size (when (= 24 size) 12)
icon-secondary-color (or icon-secondary-color icon-color)]
[rn/touchable-without-feedback
(merge {:test-ID test-ID
:disabled disabled
:accessibility-label accessibility-label}
(when on-press
{:on-press (fn []
(on-press))})
(when on-long-press
{:on-long-press (fn []
(on-long-press))})
{:on-press-in (fn []
(reset! pressed true))}
{:on-press-out (fn []
(reset! pressed nil))})
[rn/touchable-without-feedback (merge {:test-ID test-ID
:disabled disabled
:accessibility-label accessibility-label}
(when on-press
{:on-press (fn []
(on-press))})
(when on-long-press
{:on-long-press (fn []
(on-long-press))})
{:on-press-in (fn []
(reset! pressed true))}
{:on-press-out (fn []
(reset! pressed nil))})
[rn/view
{:style (merge
(shape-style-container type icon size)
{:background-color
(if (= state :pressed)
(colors/theme-colors colors/neutral-100 colors/white)
:transparent)}
style)}
[rn/view
{:style (merge
(style-container
type
size
disabled
(or override-background-color (get background-color state))
(get border-color state)
icon
above
width
before
after)
(when
(community-themed? type community-color)
(merge
{:background-color community-color}
(when (= state :pressed) {:opacity 0.9}))))}
[rn/view {:style (merge
(shape-style-container type icon size)
{:background-color
(if (= state :pressed)
(colors/theme-colors colors/neutral-100 colors/white)
:transparent)}
style)}
[rn/view {:style (merge
(style-container
type
size
disabled
(or override-background-color (get background-color state))
(get border-color state)
icon
above
width
before
after)
(when
(community-themed? type community-color)
(merge
{:background-color community-color}
(when (= state :pressed) {:opacity 0.9}))))}
(when above
[rn/view
[quo2.icons/icon above
{:container-style {:margin-bottom 2}
:color icon-secondary-color
:size icon-size}]])
[quo2.icons/icon above {:container-style {:margin-bottom 2}
:color icon-secondary-color
:size icon-size}]])
(when before
[rn/view
[quo2.icons/icon before
{:container-style {:margin-left (if (= size 40) 12 8)
:margin-right 4}
:color icon-secondary-color
:size icon-size}]])
[quo2.icons/icon before {:container-style {:margin-left (if (= size 40) 12 8)
:margin-right 4}
:color icon-secondary-color
:size icon-size}]])
[rn/view
(cond
(or icon icon-no-color)
[quo2.icons/icon children
{:color icon-color
:no-color icon-no-color
:size icon-size}]
[quo2.icons/icon children {:color icon-color
:no-color icon-no-color
:size icon-size}]
(string? children)
[text/text
{:size (when (#{56 24} size) :paragraph-2)
:weight :medium
:number-of-lines 1
:style {:color (if
(and
(community-themed? type community-color)
(string? community-text-color))
community-text-color
label-color)}}
[text/text {:size (when (#{56 24} size) :paragraph-2)
:weight :medium
:number-of-lines 1
:style {:color (if
(and
(community-themed? type community-color)
(string? community-text-color))
community-text-color
label-color)}}
children]
@@ -313,8 +278,7 @@
children)]
(when after
[rn/view
[quo2.icons/icon after
{:container-style {:margin-left 4
:margin-right (if (= size 40) 12 8)}
:color icon-secondary-color
:size icon-size}]])]]]))))
[quo2.icons/icon after {:container-style {:margin-left 4
:margin-right (if (= size 40) 12 8)}
:color icon-secondary-color
:size icon-size}]])]]]))))
+30 -35
View File
@@ -1,12 +1,11 @@
(ns quo2.components.buttons.dynamic-button
(:require [quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
(:require [react-native.core :as rn]
[reagent.core :as reagent]
[quo2.components.icon :as icon]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[reagent.core :as reagent]))
[quo2.components.markdown.text :as text]))
(defn- get-button-color
[type pressed? customization-color]
(defn- get-button-color [type pressed? customization-color]
(if (#{:jump-to :mention} type)
(if pressed?
(colors/custom-color-by-theme customization-color 60 50)
@@ -15,14 +14,12 @@
(colors/theme-colors colors/neutral-80-opa-80 colors/white-opa-80)
(colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70))))
(defn- get-icon-and-text-color
[type]
(defn- get-icon-and-text-color [type]
(if (#{:jump-to :mention} type)
colors/white
(colors/theme-colors colors/white colors/neutral-100)))
(defn- icon-view
[type]
(defn- icon-view [type]
[icon/icon
(case type
:jump-to :i/jump-to
@@ -70,34 +67,32 @@
:active-opacity 1
:style {:padding 5}
:accessibility-label type}
[rn/view
{:style (merge
{:flex-direction :row
:height 24
:border-radius 12
:background-color (get-button-color type @pressed? (or customization-color :primary))}
style)}
[rn/view {:style (merge
{:flex-direction :row
:height 24
:border-radius 12
:background-color (get-button-color type @pressed? (or customization-color :primary))}
style)}
(when (#{:mention :search :search-with-label :bottom} type)
[icon-view type])
(when (#{:jump-to :mention :notification-down :notification-up :search-with-label} type)
[text/text
{:weight :medium
:size :paragraph-2
:style {:color (get-icon-and-text-color type)
:margin-top 2.5
:margin-bottom 3.5
:margin-left (case type
:jump-to 8
:mention 0
:notification-down 8
:notification-up 8
:search-with-label 0)
:margin-right (case type
:jump-to 0
:mention 8
:notification-down 0
:notification-up 0
:search-with-label 8)}}
[text/text {:weight :medium
:size :paragraph-2
:style {:color (get-icon-and-text-color type)
:margin-top 2.5
:margin-bottom 3.5
:margin-left (case type
:jump-to 8
:mention 0
:notification-down 8
:notification-up 8
:search-with-label 0)
:margin-right (case type
:jump-to 0
:mention 8
:notification-down 0
:notification-up 0
:search-with-label 8)}}
(case type
:jump-to label
:search-with-label label
+74 -95
View File
@@ -3,36 +3,35 @@
[cljs-bean.core :as bean]
[clojure.string :as string]
[oops.core :as oops]
[react-native.core :as rn]
[quo2.theme :as theme]
[quo2.components.buttons.button :as button]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[react-native.linear-gradient :as linear-gradient]
[react-native.masked-view :as masked-view]
[react-native.syntax-highlighter :as highlighter]
[reagent.core :as reagent]))
[react-native.syntax-highlighter :as highlighter]))
;; Example themes:
;; https://github.com/react-syntax-highlighter/react-syntax-highlighter/tree/master/src/styles/hljs
(def ^:private themes
{:light {:hljs-comment {:color colors/neutral-40}
:hljs-title {:color (colors/custom-color :blue 50)}
:hljs-keyword {:color (colors/custom-color :green 50)}
:hljs-string {:color (colors/custom-color :turquoise 50)}
{:light {:hljs-comment {:color colors/neutral-40},
:hljs-title {:color (colors/custom-color :blue 50)},
:hljs-keyword {:color (colors/custom-color :green 50)},
:hljs-string {:color (colors/custom-color :turquoise 50)},
:hljs-literal {:color (colors/custom-color :turquoise 50)}
:hljs-number {:color (colors/custom-color :turquoise 50)}
:line-number {:color colors/neutral-40}}
:dark {:hljs-comment {:color colors/neutral-60}
:hljs-title {:color (colors/custom-color :blue 60)}
:hljs-keyword {:color (colors/custom-color :green 60)}
:hljs-string {:color (colors/custom-color :turquoise 60)}
:dark {:hljs-comment {:color colors/neutral-60},
:hljs-title {:color (colors/custom-color :blue 60)},
:hljs-keyword {:color (colors/custom-color :green 60)},
:hljs-string {:color (colors/custom-color :turquoise 60)},
:hljs-literal {:color (colors/custom-color :turquoise 60)}
:hljs-number {:color (colors/custom-color :turquoise 60)}
:line-number {:color colors/neutral-40}}})
(defn- text-style
[class-names]
(defn- text-style [class-names]
(->> class-names
(map keyword)
(reduce #(merge %1 (get-in themes [(theme/get-theme) %2]))
@@ -44,41 +43,35 @@
;; layout.
:line-height 18})))
(defn- render-nodes
[nodes]
(defn- render-nodes [nodes]
(map (fn [{:keys [children value] :as node}]
;; Node can have :children or a :value.
(if children
(into [text/text
{:weight :code
:size :paragraph-2
:style (text-style (get-in node [:properties :className]))}]
(into [text/text {:weight :code
:size :paragraph-2
:style (text-style (get-in node [:properties :className]))}]
(render-nodes children))
;; Remove newlines as we already render each line separately.
(-> value string/trim-newline)))
nodes))
(defn- code-block
[{:keys [rows line-number-width]}]
(defn- code-block [{:keys [rows line-number-width]}]
[into [:<>]
(->> rows
(render-nodes)
;; Line numbers
(map-indexed (fn [idx row]
(conj [rn/view {:style {:flex-direction :row}}
[rn/view
{:style {:width line-number-width
;; 8+12 margin
:margin-right 20}}
[text/text
{:weight :code
:size :paragraph-2
:style (text-style ["line-number"])}
[rn/view {:style {:width line-number-width
;; 8+12 margin
:margin-right 20}}
[text/text {:weight :code
:size :paragraph-2
:style (text-style ["line-number"])}
(inc idx)]]]
row))))])
(defn- native-renderer
[]
(defn- native-renderer []
(let [text-height (reagent/atom nil)]
(fn [{:keys [rows max-lines on-copy-press]}]
(let [background-color (colors/theme-colors
@@ -96,81 +89,67 @@
max-line-digits (-> rows count (min max-rows) str count)
;; ~ 9 is char width, 18 is width used in Figma.
line-number-width (* font-scale (max 18 (* 9 max-line-digits)))
max-text-height (some-> max-lines
(* font-scale 18)) ;; 18 is font's line height.
max-text-height (some-> max-lines (* font-scale 18)) ;; 18 is font's line height.
truncated? (and max-text-height (< max-text-height @text-height))
maybe-mask-wrapper (if truncated?
[masked-view/masked-view
{:mask-element
(reagent/as-element
[linear-gradient/linear-gradient
{:colors ["black" "transparent"]
:locations [0.75 1]
:style {:flex 1}}])}]
[linear-gradient/linear-gradient {:colors ["black" "transparent"]
:locations [0.75 1]
:style {:flex 1}}])}]
[:<>])]
[rn/view
{:style {:overflow :hidden
:padding 8
:background-color background-color
:border-color border-color
:border-width 1
:border-radius 8
;; Hide on intial render to avoid flicker when mask-wrapper is shown.
:opacity (if @text-height 1 0)}}
[rn/view {:style {:overflow :hidden
:padding 8
:background-color background-color
:border-color border-color
:border-width 1
:border-radius 8
;; Hide on intial render to avoid flicker when mask-wrapper is shown.
:opacity (if @text-height 1 0)}}
;; Line number container
[rn/view
{:style {:position :absolute
:bottom 0
:top 0
:left 0
:width (+ line-number-width 8 8)
:background-color background-color-left
:border-right-color border-color
:border-right-width 1}}]
(conj maybe-mask-wrapper
[rn/view {:max-height max-text-height}
[rn/view
{:on-layout (fn [evt]
(let [height (oops/oget evt "nativeEvent.layout.height")]
(reset! text-height height)))}
[code-block
{:rows (take max-rows rows)
:line-number-width line-number-width}]]])
[rn/view {:style {:position :absolute
:bottom 0
:top 0
:left 0
:width (+ line-number-width 8 8)
:background-color background-color-left
:border-right-color border-color
:border-right-width 1}}]
(conj maybe-mask-wrapper [rn/view {:max-height max-text-height}
[rn/view {:on-layout (fn [evt]
(let [height (oops/oget evt "nativeEvent.layout.height")]
(reset! text-height height)))}
[code-block {:rows (take max-rows rows)
:line-number-width line-number-width}]]])
;; Copy button
[rn/view
{:style {:position :absolute
:bottom 8
:right 8}}
[button/button
{:icon true
:type :grey
:size 24
:on-press on-copy-press}
[rn/view {:style {:position :absolute
:bottom 8
:right 8}}
[button/button {:icon true
:type :grey
:size 24
:on-press on-copy-press}
:main-icons/copy]]]))))
(defn- wrap-renderer-fn
[f {:keys [max-lines on-copy-press]}]
(defn- wrap-renderer-fn [f {:keys [max-lines on-copy-press]}]
(fn [^js props]
(reagent/as-element [:f> f
{:rows (.-rows props)
:max-lines max-lines
:on-copy-press on-copy-press}])))
(reagent/as-element [:f> f {:rows (.-rows props)
:max-lines max-lines
:on-copy-press on-copy-press}])))
(defn snippet
[{:keys [language max-lines on-copy-press]} children]
[highlighter/highlighter
{:language language
:renderer (wrap-renderer-fn
native-renderer
{:max-lines max-lines
:on-copy-press #(when on-copy-press
(on-copy-press children))})
;; Default props to adapt Highlighter for react-native.
:CodeTag react-native/View
:PreTag react-native/View
:show-line-numbers false
:style #js {}
:custom-style #js {:backgroundColor nil}}
(defn snippet [{:keys [language max-lines on-copy-press]} children]
[highlighter/highlighter {:language language
:renderer (wrap-renderer-fn
native-renderer {:max-lines max-lines
:on-copy-press #(when on-copy-press
(on-copy-press children))})
;; Default props to adapt Highlighter for react-native.
:CodeTag react-native/View
:PreTag react-native/View
:show-line-numbers false
:style #js {}
:custom-style #js {:backgroundColor nil}}
children])
@@ -1,18 +1,18 @@
(ns quo2.components.community.community-card-view
(:require [quo2.components.community.community-view :as community-view]
[quo2.components.community.style :as style]
[react-native.core :as rn]))
(:require
[quo2.components.community.community-view :as community-view]
[quo2.components.community.style :as style]
[react-native.core :as rn]))
(defn community-card-view-item
[{:keys [name description locked
status tokens cover tags width]} on-press]
[rn/touchable-opacity {:on-press on-press}
[rn/view {:style (style/community-card 20)}
[rn/view
{:style {:width width
:height 230
:border-radius 20}
:on-press on-press}
[rn/view {:style {:width width
:height 230
:border-radius 20}
:on-press on-press}
[rn/view
{:flex 1}
[rn/view (style/community-cover-container 40)
@@ -27,10 +27,9 @@
;[communities.icon/community-icon-redesign community 48]]
(when (= status :gated)
[rn/view (style/permission-tag-styles)
[community-view/permission-tag-container
{:locked locked
:status status
:tokens tokens}]])
[community-view/permission-tag-container {:locked locked
:status status
:tokens tokens}]])
[community-view/community-title
{:title name
:description description}]
@@ -1,131 +1,112 @@
(ns quo2.components.community.community-list-view
(:require [quo2.components.community.community-view :as community-view]
[quo2.components.community.style :as style]
[quo2.components.counter.counter :as counter]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]))
(:require
[quo2.components.community.community-view :as community-view]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.components.counter.counter :as counter]
[quo2.components.icon :as icons]
[quo2.components.community.style :as style]
[react-native.fast-image :as fast-image]
[react-native.core :as rn]))
(defn community-icon-view
[community-icon]
[rn/view
{:width 32
:height 32}
[fast-image/fast-image
{:source {:uri community-icon}
:style {:height 32
:border-radius 16
:width 32}}]])
(defn community-icon-view [community-icon]
[rn/view {:width 32
:height 32}
[fast-image/fast-image {:source {:uri community-icon}
:style {:height 32
:border-radius 16
:width 32}}]])
(defn notification-view
[{:keys [muted?
unread-messages?
unread-mentions-count]}]
(defn notification-view [{:keys [muted?
unread-messages?
unread-mentions-count]}]
(cond
muted?
[icons/icon :i/muted
{:container-style {:align-items :center
:justify-content :center}
:resize-mode :center
:size 20
:color (colors/theme-colors
colors/neutral-40
colors/neutral-50)}]
[icons/icon :i/muted {:container-style {:align-items :center
:justify-content :center}
:resize-mode :center
:size 20
:color (colors/theme-colors
colors/neutral-40
colors/neutral-50)}]
(pos? unread-mentions-count)
[counter/counter {:type :default} unread-mentions-count]
unread-messages?
[rn/view
{:style {:width 8
:height 8
:border-radius 4
:background-color (colors/theme-colors
colors/neutral-40
colors/neutral-60)}}]))
[rn/view {:style {:width 8
:height 8
:border-radius 4
:background-color (colors/theme-colors
colors/neutral-40
colors/neutral-60)}}]))
(defn communities-list-view-item
[props
{:keys [name
locked?
status
muted?
unread-messages?
unread-mentions-count
community-icon
tokens
background-color]}]
[rn/view
{:style (merge (style/community-card 16)
{:margin-bottom 12
:margin-horizontal 20})}
[rn/touchable-highlight
(merge {:style {:height 56
:border-radius 16}}
props)
(defn communities-list-view-item [props {:keys [name
locked?
status
muted?
unread-messages?
unread-mentions-count
community-icon
tokens
background-color]}]
[rn/view {:style (merge (style/community-card 16)
{:margin-bottom 12
:margin-horizontal 20})}
[rn/touchable-highlight (merge {:style {:height 56
:border-radius 16}}
props)
[rn/view {:flex 1}
[rn/view
{:flex-direction :row
:border-radius 16
:padding-horizontal 12
:align-items :center
:padding-vertical 8
:background-color background-color}
[rn/view {:flex-direction :row
:border-radius 16
:padding-horizontal 12
:align-items :center
:padding-vertical 8
:background-color background-color}
[rn/view]
(when community-icon
[community-icon-view community-icon])
[rn/view
{:flex 1
:margin-horizontal 12}
[text/text
{:weight :semi-bold
:size :paragraph-1
:accessibility-label :community-name-text
:number-of-lines 1
:ellipsize-mode :tail
:style {:color (when muted?
(colors/theme-colors
colors/neutral-40
colors/neutral-60))}}
[rn/view {:flex 1
:margin-horizontal 12}
[text/text {:weight :semi-bold
:size :paragraph-1
:accessibility-label :community-name-text
:number-of-lines 1
:ellipsize-mode :tail
:style {:color (when muted?
(colors/theme-colors
colors/neutral-40
colors/neutral-60))}}
name]
[community-view/community-stats-column :list-view]]
(if (= status :gated)
[community-view/permission-tag-container
{:locked? locked?
:tokens tokens}]
[notification-view
{:muted? muted?
:unread-mentions-count unread-mentions-count
:unread-messages? unread-messages?}])]]]])
[community-view/permission-tag-container {:locked? locked?
:tokens tokens}]
[notification-view {:muted? muted?
:unread-mentions-count unread-mentions-count
:unread-messages? unread-messages?}])]]]])
(defn communities-membership-list-item
[props
{:keys [name
muted?
unread-messages?
unread-mentions-count
status
community-icon
tokens
locked?]}]
(defn communities-membership-list-item [props {:keys [name
muted?
unread-messages?
unread-mentions-count
status
community-icon
tokens
locked?]}]
[rn/view {:margin-bottom 20}
[rn/touchable-highlight
(merge {:underlay-color colors/primary-50-opa-5
:style {:border-radius 12}}
props)
[rn/touchable-highlight (merge {:underlay-color colors/primary-50-opa-5
:style {:border-radius 12}}
props)
[rn/view {:flex 1}
[rn/view
{:flex-direction :row
:border-radius 16
:align-items :center}
[rn/view {:flex-direction :row
:border-radius 16
:align-items :center}
(when community-icon
[community-icon-view community-icon])
[rn/view
{:flex 1
:margin-left 12
:justify-content :center}
[rn/view {:flex 1
:margin-left 12
:justify-content :center}
[text/text
{:accessibility-label :chat-name-text
:number-of-lines 1
@@ -134,14 +115,11 @@
:size :paragraph-1}
name]]
[rn/view
{:justify-content :center
:margin-right 16}
[rn/view {:justify-content :center
:margin-right 16}
(if (= status :gated)
[community-view/permission-tag-container
{:locked? locked?
:tokens tokens}]
[notification-view
{:muted? muted?
:unread-mentions-count unread-mentions-count
:unread-messages? unread-messages?}])]]]]])
[community-view/permission-tag-container {:locked? locked?
:tokens tokens}]
[notification-view {:muted? muted?
:unread-mentions-count unread-mentions-count
:unread-messages? unread-messages?}])]]]]])
@@ -1,46 +1,38 @@
(ns quo2.components.community.community-view
(:require [quo2.components.community.style :as style]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.components.tags.permission-tag :as permission]
[quo2.components.tags.tag :as tag]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(:require
[quo2.components.markdown.text :as text]
[quo2.components.icon :as icons]
[quo2.foundations.colors :as colors]
[quo2.components.tags.permission-tag :as permission]
[quo2.components.tags.tag :as tag]
[quo2.components.community.style :as style]
[react-native.core :as rn]))
(defn community-stats
[{:keys [icon members-count icon-color]}]
(defn community-stats [{:keys [icon members-count icon-color]}]
[rn/view (style/stats-count-container)
[rn/view {:margin-right 4}
[icons/icon icon
{:container-style {:align-items :center
:justify-content :center}
:resize-mode :center
:size 16
:color icon-color}]]
[text/text
{:weight :regular
:size :paragraph-1}
[icons/icon icon {:container-style {:align-items :center
:justify-content :center}
:resize-mode :center
:size 16
:color icon-color}]]
[text/text {:weight :regular
:size :paragraph-1}
members-count]])
(defn community-stats-column
[type]
(defn community-stats-column [type]
(let [icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)]
[rn/view
(if (= type :card-view)
(style/card-stats-container)
(style/list-stats-container))
[community-stats
{:icon :i/group
:members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from
;;outside this component
:icon-color icon-color}]
[community-stats
{:icon :i/lightning
:members-count "112.1K"
:icon-color icon-color}]]))
[rn/view (if (= type :card-view)
(style/card-stats-container)
(style/list-stats-container))
[community-stats {:icon :i/group
:members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from outside this component
:icon-color icon-color}]
[community-stats {:icon :i/lightning
:members-count "112.1K"
:icon-color icon-color}]]))
(defn community-tags
[tags]
(defn community-tags [tags]
[rn/view (style/community-tags-container)
(for [{:keys [id tag-label resource]} tags]
^{:key id}
@@ -53,8 +45,7 @@
:labelled true
:resource resource}]])])
(defn community-title
[{:keys [title description size] :or {size :small}}]
(defn community-title [{:keys [title description size] :or {size :small}}]
[rn/view (style/community-title-description-container (if (= size :large) 56 32))
(when title
[text/text
@@ -74,14 +65,12 @@
:style {:margin-top (if (= size :large) 8 2)}}
description])])
(defn permission-tag-container
[{:keys [locked? tokens on-press]}]
[permission/tag
{:background-color (colors/theme-colors
colors/neutral-10
colors/neutral-80)
:locked? locked?
:tokens tokens
(defn permission-tag-container [{:keys [locked? tokens on-press]}]
[permission/tag {:background-color (colors/theme-colors
colors/neutral-10
colors/neutral-80)
:locked? locked?
:tokens tokens
:size 24
:on-press on-press}])
:size 24
:on-press on-press}])
@@ -1,107 +1,94 @@
(ns quo2.components.community.discover-card
(:require [quo2.components.community.style :as style]
[quo2.components.markdown.text :as text]
(:require [quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.components.community.style :as style]
[react-native.core :as rn]))
;; Discover card placeholders images.
;; TODO replaced when real data is available
(def images
{:images [{:id 1 :column-images [{:id 1 :image ""}]}
{:images [{:id 1 :column-images [{:id 1 :image ""}]}
{:id 2 :column-images [{}]}]})
(defn card-title-and-description
[title description]
(defn card-title-and-description [title description]
[rn/view
{:flex 1
:padding-top 8
:padding-bottom 8
:border-radius 12}
[rn/view
{:flex 1
:padding-horizontal 12}
[text/text
{:accessibility-label :community-name-text
:ellipsize-mode :tail
:number-of-lines 1
:weight :semi-bold
:size :paragraph-1}
{:flex 1
:padding-top 8
:padding-bottom 8
:border-radius 12}
[rn/view {:flex 1
:padding-horizontal 12}
[text/text {:accessibility-label :community-name-text
:ellipsize-mode :tail
:number-of-lines 1
:weight :semi-bold
:size :paragraph-1}
title]
[text/text
{:accessibility-label :community-name-text
:ellipsize-mode :tail
:number-of-lines 1
:color (colors/theme-colors
colors/neutral-50
colors/neutral-40)
:weight :regular
:size :paragraph-2}
[text/text {:accessibility-label :community-name-text
:ellipsize-mode :tail
:number-of-lines 1
:color (colors/theme-colors
colors/neutral-50
colors/neutral-40)
:weight :regular
:size :paragraph-2}
description]]])
(defn placeholder-list-images
[{:keys [images width height border-radius]}]
(defn placeholder-list-images [{:keys [images width height border-radius]}]
[rn/view
[rn/view {:justify-content :center}
[rn/view {:justify-content :center}
(for [{:keys [id]} images]
^{:key id}
[rn/view
{:border-radius border-radius
:margin-top 4
:margin-right 4
:width width
:height height
:background-color colors/neutral-10}])]])
[rn/view {:border-radius border-radius
:margin-top 4
:margin-right 4
:width width
:height height
:background-color colors/neutral-10}])]])
(defn placeholder-row-images
[{:keys [first-image last-image images width height
border-radius]}]
(defn placeholder-row-images [{:keys [first-image last-image images width height
border-radius]}]
[rn/view
(when first-image
[rn/view
{:border-bottom-right-radius 6
:border-bottom-left-radius 6
:margin-right 4
:width width
:height height
:background-color colors/neutral-10}])
[rn/view {:border-bottom-right-radius 6
:border-bottom-left-radius 6
:margin-right 4
:width width
:height height
:background-color colors/neutral-10}])
(when images
[placeholder-list-images
{:images images
:width 32
:height 32
:border-radius 6}])
[placeholder-list-images {:images images
:width 32
:height 32
:border-radius 6}])
(when last-image
[rn/view
{:border-top-left-radius border-radius
:border-top-right-radius 6
:margin-top 4
:width width
:height height
:background-color colors/neutral-10}])])
[rn/view {:border-top-left-radius border-radius
:border-top-right-radius 6
:margin-top 4
:width width
:height height
:background-color colors/neutral-10}])])
(defn discover-card
[{:keys [title description on-press accessibility-label]}]
(let [on-joined-images (get images :images)]
(defn discover-card [{:keys [title description on-press accessibility-label]}]
(let [on-joined-images (get images :images)]
[rn/touchable-without-feedback
{:on-press on-press
:accessibility-label accessibility-label}
[rn/view
(merge (style/community-card 16)
{:background-color (colors/theme-colors
colors/white
colors/neutral-90)}
{:flex-direction :row
:margin-horizontal 20
:height 56
:padding-right 12})
[rn/view (merge (style/community-card 16)
{:background-color (colors/theme-colors
colors/white
colors/neutral-90)}
{:flex-direction :row
:margin-horizontal 20
:height 56
:padding-right 12})
[card-title-and-description title description]
(for [{:keys [id column-images]} on-joined-images]
^{:key id}
[placeholder-row-images
{:images (when (= id 1)
column-images)
:width 32
:height (if (= id 1) 8 26)
:border-radius 6
:first-image "" ; TODO replace with real data
:last-image ""}])]])) ; TODO replace with real data
[placeholder-row-images {:images (when (= id 1)
column-images)
:width 32
:height (if (= id 1) 8 26)
:border-radius 6
:first-image "" ; TODO replace with real data
:last-image ""}])]])) ; TODO replace with real data
+45 -59
View File
@@ -8,66 +8,57 @@
:height 52
:padding-left 18})
(defn community-card
[radius]
{:shadow-offset {:width 0
(defn community-card [radius]
{:shadow-offset {:width 0
:height 2}
:shadow-radius radius
:shadow-opacity 1
:shadow-color colors/shadow
:border-radius radius
:justify-content :space-between
:elevation 2
:background-color (colors/theme-colors
colors/white
colors/neutral-90)})
:shadow-radius radius
:shadow-opacity 1
:shadow-color colors/shadow
:border-radius radius
:justify-content :space-between
:elevation 2
:background-color (colors/theme-colors
colors/white
colors/neutral-90)})
(defn stats-count-container
[]
(defn stats-count-container []
{:flex-direction :row
:align-items :center
:margin-right 16})
(defn card-stats-container
[]
(defn card-stats-container []
{:flex-direction :row})
(defn list-stats-container
[]
(defn list-stats-container []
{:flex-direction :row
:align-items :center})
(defn community-tags-container
[]
(defn community-tags-container []
{:flex-direction :row})
(defn card-stats-position
[]
{:position :absolute
:top 116
:right 12
:left 12})
(defn card-stats-position []
{:position :absolute
:top 116
:right 12
:left 12})
(defn community-tags-position
[]
{:position :absolute
:top 154
:right 12
:left 12})
(defn community-tags-position []
{:position :absolute
:top 154
:right 12
:left 12})
(defn card-view-content-container
[padding-horizontal]
{:flex 1
:height 20
:padding-left padding-horizontal
:padding-right padding-horizontal
:border-radius 16
(defn card-view-content-container [padding-horizontal]
{:flex 1
:height 20
:padding-left padding-horizontal
:padding-right padding-horizontal
:border-radius 16
:background-color (colors/theme-colors
colors/white
colors/neutral-90)})
(defn card-view-chat-icon
[icon-size]
(defn card-view-chat-icon [icon-size]
{:border-radius 48
:position :absolute
:top (- (/ icon-size 2))
@@ -77,34 +68,29 @@
colors/white
colors/neutral-90)})
(defn list-view-content-container
[]
{:flex-direction :row
:border-radius 16
:align-items :center
(defn list-view-content-container []
{:flex-direction :row
:border-radius 16
:align-items :center
:background-color (colors/theme-colors
colors/white
colors/neutral-90)})
(defn list-view-chat-icon
[]
{:border-radius 32
:padding 12})
(defn list-view-chat-icon []
{:border-radius 32
:padding 12})
(defn community-title-description-container
[margin-top]
(defn community-title-description-container [margin-top]
{:margin-top margin-top})
(defn community-cover-container
[height]
(defn community-cover-container [height]
{:flex-direction :row
:height height
:border-top-right-radius 20
:border-top-left-radius 20
:background-color colors/primary-50-opa-20})
(defn permission-tag-styles
[]
{:position :absolute
:top 8
:right 8})
(defn permission-tag-styles []
{:position :absolute
:top 8
:right 8})
+110 -142
View File
@@ -13,83 +13,75 @@
(def ^:private token-tag-horizontal-spacing 7)
(def token-tag-vertical-spacing 5)
(def styles
{:container {:flex 1
:padding-horizontal 20}
:header-container {:flex-direction :row
:align-items :center}
:header-spacing-community {:margin-bottom 16}
:header-spacing-channel {:margin-bottom 24}
:header-avatar {:margin-right 8}
:header-community-avatar-image {:width 32 :height 32}
:header-title-container {:flex 1
:flex-direction :row
:align-items :center}
:header-title-lock {:margin-left 6}
:header-info-button {:margin-left 8}
:token-requirement-text-spacing {:margin-vertical 8}
:token-requirement-list-spacing {:padding-vertical (- 8 token-tag-vertical-spacing)}
:token-row {:flex-direction :row
:flex-wrap :wrap
:align-items :center}
:token-row-container {:flex-direction :row
:align-items :center
:margin-horizontal (- token-tag-horizontal-spacing)}
:token-row-container-no-shift {:margin-left 0}
:token-row-or-text {:margin-right 3}
:token-tag-spacing {:margin-horizontal token-tag-horizontal-spacing
:margin-vertical token-tag-vertical-spacing}
:divider {:height 1
:margin-horizontal -20
:margin-top (- 12 token-tag-vertical-spacing)
:margin-bottom 8}
:membership-request-denied {:margin-top 13}
:enter-button {:margin-top 16}
:info-text {:margin-top 12
:text-align :center
:padding-horizontal 20}})
(def styles {:container {:flex 1
:padding-horizontal 20}
:header-container {:flex-direction :row
:align-items :center}
:header-spacing-community {:margin-bottom 16}
:header-spacing-channel {:margin-bottom 24}
:header-avatar {:margin-right 8}
:header-community-avatar-image {:width 32 :height 32}
:header-title-container {:flex 1
:flex-direction :row
:align-items :center}
:header-title-lock {:margin-left 6}
:header-info-button {:margin-left 8}
:token-requirement-text-spacing {:margin-vertical 8}
:token-requirement-list-spacing {:padding-vertical (- 8 token-tag-vertical-spacing)}
:token-row {:flex-direction :row
:flex-wrap :wrap
:align-items :center}
:token-row-container {:flex-direction :row
:align-items :center
:margin-horizontal (- token-tag-horizontal-spacing)}
:token-row-container-no-shift {:margin-left 0}
:token-row-or-text {:margin-right 3}
:token-tag-spacing {:margin-horizontal token-tag-horizontal-spacing
:margin-vertical token-tag-vertical-spacing}
:divider {:height 1
:margin-horizontal -20
:margin-top (- 12 token-tag-vertical-spacing)
:margin-bottom 8}
:membership-request-denied {:margin-top 13}
:enter-button {:margin-top 16}
:info-text {:margin-top 12
:text-align :center
:padding-horizontal 20}})
(defn multiple-token-requirements?
[token-requirement-lists]
(defn multiple-token-requirements? [token-requirement-lists]
(when (vector? (first token-requirement-lists))
(> (count token-requirement-lists) 1)))
(defn is-token-requirement-met?
[token-requirement-list]
(defn is-token-requirement-met? [token-requirement-list]
(and (seq token-requirement-list)
(every?
(fn [token]
(get token :is-sufficient?))
token-requirement-list)))
(defn are-multiple-token-requirements-met?
[token-requirement-lists]
(defn are-multiple-token-requirements-met? [token-requirement-lists]
(if (multiple-token-requirements? token-requirement-lists)
(some is-token-requirement-met? token-requirement-lists)
(is-token-requirement-met? token-requirement-lists)))
(defn token-requirement-list-row
[tokens community-color]
[rn/view
{:style (merge
(get styles :token-row)
(get styles :token-requirement-list-spacing))}
(defn token-requirement-list-row [tokens community-color]
[rn/view {:style (merge
(get styles :token-row)
(get styles :token-requirement-list-spacing))}
(map-indexed (fn [token-index token]
(let [{:keys [token-img-src token amount is-sufficient? is-purchasable?]} token]
^{:key token-index}
[rn/view {:style (get styles :token-tag-spacing)}
[token-tag/token-tag
{:token token
:value amount
:size 24
:border-color community-color
:is-required is-sufficient?
:is-purchasable is-purchasable?
:token-img-src token-img-src}]]))
[token-tag/token-tag {:token token
:value amount
:size 24
:border-color community-color
:is-required is-sufficient?
:is-purchasable is-purchasable?
:token-img-src token-img-src}]]))
tokens)])
(defn token-requirement-list
[props community-color]
(defn token-requirement-list [props community-color]
(let [{:keys [gate token-requirements-changed? required-tokens-lost?]} props
[gate-type token-requirement-lists] gate
multiple-token-requirements? (multiple-token-requirements? token-requirement-lists)
@@ -97,17 +89,15 @@
you-must-hold-label (if (= gate-type :join)
(cond
token-requirements-changed? :t/you-must-now-hold
required-tokens-lost? :t/you-must-always-hold
:else :t/you-must-hold)
required-tokens-lost? :t/you-must-always-hold
:else :t/you-must-hold)
:t/you-must-hold)
message-label (cond
(= gate-type :join)
(cond
token-requirements-changed? :t/community-join-requirements-changed
required-tokens-lost? :t/community-join-requirements-tokens-lost
:else (if is-sufficient?
:t/community-join-requirements-met
:t/community-join-requirements-not-met))
required-tokens-lost? :t/community-join-requirements-tokens-lost
:else (if is-sufficient? :t/community-join-requirements-met :t/community-join-requirements-not-met))
(= gate-type :read)
(if is-sufficient?
:t/community-channel-read-requirements-met
@@ -118,14 +108,12 @@
:t/community-channel-write-requirements-not-met))]
[rn/view
[rn/view {:style (get styles :token-requirement-text-spacing)}
[text/text
{:weight :medium
:number-of-lines 1}
[text/text {:weight :medium
:number-of-lines 1}
(i18n/label
message-label)]
[text/text
{:size :paragraph-2
:number-of-lines 1}
[text/text {:size :paragraph-2
:number-of-lines 1}
(i18n/label
you-must-hold-label)]]
@@ -133,26 +121,22 @@
(map-indexed
(fn [token-requirement-index tokens]
^{:key token-requirement-index}
[rn/view
{:style (merge
(get styles :token-row-container)
(when-not (= token-requirement-index 0)
(get styles :token-row-container-no-shift)))}
[rn/view {:style (merge
(get styles :token-row-container)
(when-not (= token-requirement-index 0)
(get styles :token-row-container-no-shift)))}
(when-not (= token-requirement-index 0)
[text/text
{:style (get styles :token-row-or-text)
:size :paragraph-2} "or"])
[text/text {:style (get styles :token-row-or-text)
:size :paragraph-2} "or"])
[token-requirement-list-row tokens community-color]])
token-requirement-lists)
[rn/view {:style (get styles :token-row-container)}
[token-requirement-list-row token-requirement-lists community-color]])]))
(defn is-community-locked?
[community]
(defn is-community-locked? [community]
(not (are-multiple-token-requirements-met? (get-in community [:gates :join]))))
(defn is-channel-locked?
[channel]
(defn is-channel-locked? [channel]
(not (are-multiple-token-requirements-met?
(or (get-in channel [:gates :read])
(get-in channel [:gates :write])))))
@@ -190,98 +174,82 @@
membership-request-denied?
token-requirements-changed?
required-tokens-lost?
gates]}
(if (= type :community) community channel)
gates]} (if (= type :community) community channel)
locked? (if
(= type :community)
(= type :community)
(is-community-locked? community)
(is-channel-locked? channel))]
[rn/view
{:style (merge (get styles :container)
{:background-color (colors/theme-colors colors/white colors/neutral-90)})}
[rn/view
{:style (merge
(get styles :header-container)
(if
(= type :community)
(get styles :header-spacing-community)
(get styles :header-spacing-channel)))}
[rn/view {:style (merge (get styles :container) {:background-color (colors/theme-colors colors/white colors/neutral-90)})}
[rn/view {:style (merge
(get styles :header-container)
(if
(= type :community)
(get styles :header-spacing-community)
(get styles :header-spacing-channel)))}
[rn/view {:style (get styles :header-avatar)}
(if (= type :community)
[fast-image/fast-image
{:source community-avatar-img-src
:style (get styles :header-community-avatar-image)}]
[fast-image/fast-image {:source community-avatar-img-src
:style (get styles :header-community-avatar-image)}]
[channel-avatar/channel-avatar
{:big? true
:locked? locked?
:emoji emoji
:emoji-background-color emoji-background-color}])]
[channel-avatar/channel-avatar {:big? true
:locked? locked?
:emoji emoji
:emoji-background-color emoji-background-color}])]
[rn/view {:style (get styles :header-title-container)}
[text/text
{:weight :semi-bold
:number-of-lines 1
:size :heading-1
:style (get styles :header-text)} (if (= type :community) name (str "# " name))]
[text/text {:weight :semi-bold
:number-of-lines 1
:size :heading-1
:style (get styles :header-text)} (if (= type :community) name (str "# " name))]
(when (= type :community)
[icon/icon
(if locked?
:main-icons2/locked
:main-icons2/unlocked)
[icon/icon (if locked?
:main-icons2/locked
:main-icons2/unlocked)
{:container-style (get styles :header-title-lock)
:color (colors/theme-colors
colors/neutral-50
colors/neutral-40)}])]
[button/button
{:type :outline
:size 32
:icon true
:style (get styles :header-info-button)} :main-icons2/info]]
[button/button {:type :outline
:size 32
:icon true
:style (get styles :header-info-button)} :main-icons2/info]]
(map-indexed (fn [gate-index gate]
^{:key gate-index}
[:<>
(when-not (= gate-index 0)
[rn/view
{:style (merge
(get styles :divider)
{:background-color (colors/theme-colors
colors/neutral-10
colors/neutral-80)})}])
[token-requirement-list
{:gate gate
:token-requirements-changed? token-requirements-changed?
:required-tokens-lost? required-tokens-lost?} community-color]])
[rn/view {:style (merge
(get styles :divider)
{:background-color (colors/theme-colors
colors/neutral-10
colors/neutral-80)})}])
[token-requirement-list {:gate gate :token-requirements-changed? token-requirements-changed? :required-tokens-lost? required-tokens-lost?} community-color]])
gates)
(when (= type :channel)
(if membership-request-denied?
[information-box/information-box
{:type :error
:icon :main-icons2/untrustworthy
:no-icon-color? true
:style (get styles :membership-request-denied)}
[information-box/information-box {:type :error
:icon :main-icons2/untrustworthy
:no-icon-color? true
:style (get styles :membership-request-denied)}
(i18n/label
:t/membership-request-denied)]
[:<>
[button/button
{:type :community
:community-color community-color
:community-text-color community-text-color
:style (get styles :enter-button)
:disabled locked?
:on-press on-enter-channel}
(str "# "
(i18n/label
:t/enter-channel))]
[button/button {:type :community
:community-color community-color
:community-text-color community-text-color
:style (get styles :enter-button)
:disabled locked?
:on-press on-enter-channel}
(str "# " (i18n/label
:t/enter-channel))]
[text/text
{:size :paragraph-2
{:size :paragraph-2
:style (merge
(get styles :info-text)
{:color (colors/theme-colors
@@ -7,28 +7,28 @@
([]
(render-counter {} nil))
([opts value]
(rtl/render (reagent/as-element [counter/counter opts value]))))
(rtl/render (reagent/as-element [counter/counter opts value]))))
(js/global.test "default render of counter component"
(fn []
(render-counter)
(-> (js/expect (rtl/screen.getByTestId "counter-component"))
(-> (js/expect (rtl/screen.getByTestId "counter-component"))
(.toBeTruthy))))
(js/global.test "renders counter with a string value"
(fn []
(render-counter {} "1")
(-> (js/expect (rtl/screen.getByText "1"))
(-> (js/expect (rtl/screen.getByText "1"))
(.toBeTruthy))))
(js/global.test "renders counter with an integer value"
(fn []
(render-counter {} 1)
(-> (js/expect (rtl/screen.getByText "1"))
(-> (js/expect (rtl/screen.getByText "1"))
(.toBeTruthy))))
(js/global.test "renders counter with value 99+ when the value is greater than 99"
(fn []
(render-counter {} "100")
(-> (js/expect (rtl/screen.getByText "99+"))
(-> (js/expect (rtl/screen.getByText "99+"))
(.toBeTruthy))))
+37 -40
View File
@@ -1,21 +1,20 @@
(ns quo2.components.counter.counter
(:require [quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(:require [quo2.theme :as theme]
[react-native.core :as rn]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]))
(def themes
{:light {:default colors/primary-50
:secondary colors/neutral-80-opa-5
:grey colors/neutral-10
:outline colors/neutral-20}
:dark {:default colors/primary-60
:secondary colors/white-opa-5
:grey colors/neutral-70
:outline colors/neutral-70}})
{:light {:default colors/primary-50
:secondary colors/neutral-80-opa-5
:grey colors/neutral-10
:outline colors/neutral-20}
:dark {:default colors/primary-60
:secondary colors/white-opa-5
:grey colors/neutral-70
:outline colors/neutral-70}})
(defn get-color
[key]
(defn get-color [key]
(get-in themes [(theme/get-theme) key]))
(defn counter
@@ -36,32 +35,30 @@
"99+"
(str value))
width (case (count label)
1 16
2 20
1 16
2 20
28)]
[rn/view
{:test-ID :counter-component
:accessible true
:accessibility-label accessibility-label
:style (cond-> (merge
{:align-items :center
:justify-content :center
:border-radius 6
:width width
:height 16}
style)
(= type :outline)
(merge {:border-width 1
:border-color (get-color type)})
[rn/view {:test-ID :counter-component
:accessible true
:accessibility-label accessibility-label
:style (cond-> (merge
{:align-items :center
:justify-content :center
:border-radius 6
:width width
:height 16}
style)
(= type :outline)
(merge {:border-width 1
:border-color (get-color type)})
(not= type :outline)
(assoc :background-color
(or override-bg-color
(get-color type)))
(not= type :outline)
(assoc :background-color
(or override-bg-color
(get-color type)))
(> value 99)
(assoc :padding-left 0.5))}
[text/text
{:weight :medium
:size :label
:style {:color text-color}} label]]))
(> value 99)
(assoc :padding-left 0.5))}
[text/text {:weight :medium
:size :label
:style {:color text-color}} label]]))
+13 -16
View File
@@ -1,21 +1,18 @@
(ns quo2.components.dividers.date
(:require [quo2.components.markdown.text :as text]
(:require [react-native.core :as rn]
[quo2.components.separator :as separator]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]))
(defn date
[value]
[rn/view
{:margin-vertical 16
:padding-right 8
:padding-left 62}
[text/text
{:weight :medium
:accessibility-label :divider-date-text
:size :label
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
:text-transform :capitalize
:margin-bottom 4}}
(defn date [value]
[rn/view {:margin-vertical 16
:padding-right 8
:padding-left 62}
[text/text {:weight :medium
:accessibility-label :divider-date-text
:size :label
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
:text-transform :capitalize
:margin-bottom 4}}
value]
[separator/separator]])
+30 -36
View File
@@ -1,8 +1,8 @@
(ns quo2.components.dividers.divider-label
(:require [quo2.components.icon :as icons]
(:require [react-native.core :as rn]
[quo2.components.markdown.text :as markdown.text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
[quo2.components.icon :as icons]))
(def chevron-icon-container-width 20)
@@ -15,20 +15,17 @@
increase-padding-top? -> boolean
blur? -> boolean"
[{:keys [label chevron-position counter-value increase-padding-top? blur?]}]
(let [dark? (colors/dark?)
border-and-counter-bg-color (if dark?
(if blur? colors/white-opa-5 colors/neutral-70)
colors/neutral-10)
padding-top (if increase-padding-top? 16 8)
text-and-icon-color (if dark? colors/neutral-40 colors/neutral-50)
counter-text-color (if dark? colors/white colors/neutral-100)]
[rn/view
{:style {:border-top-width 1
:border-top-color border-and-counter-bg-color
:padding-top padding-top
:padding-horizontal 16
:align-items :center
:flex-direction :row}}
(let [dark? (colors/dark?)
border-and-counter-bg-color (if dark? (if blur? colors/white-opa-5 colors/neutral-70) colors/neutral-10)
padding-top (if increase-padding-top? 16 8)
text-and-icon-color (if dark? colors/neutral-40 colors/neutral-50)
counter-text-color (if dark? colors/white colors/neutral-100)]
[rn/view {:style {:border-top-width 1
:border-top-color border-and-counter-bg-color
:padding-top padding-top
:padding-horizontal 16
:align-items :center
:flex-direction :row}}
(when (= chevron-position :left)
[rn/view {:style {:margin-right 4}}
[icons/icon
@@ -36,30 +33,27 @@
{:color text-and-icon-color
:width chevron-icon-container-width
:height chevron-icon-container-height}]])
[markdown.text/text
{:size :paragraph-2
:weight :medium
:style {:color text-and-icon-color
:flex 1}}
[markdown.text/text {:size :paragraph-2
:weight :medium
:style {:color text-and-icon-color
:flex 1}}
label]
(when (= chevron-position :right)
[icons/icon
:main-icons/chevron-down
{:color text-and-icon-color
{:color text-and-icon-color
:size chevron-icon-container-width}])
(when (pos? counter-value)
[rn/view
{:style {:border-radius 6
:height 16
:width (case (count counter-value)
1 16
2 20
28)
:background-color border-and-counter-bg-color
:align-items :center
:justify-content :center}}
[markdown.text/text
{:size :label
:weight :medium
:style {:color counter-text-color}}
[rn/view {:style {:border-radius 6
:height 16
:width (case (count counter-value)
1 16
2 20
28)
:background-color border-and-counter-bg-color
:align-items :center
:justify-content :center}}
[markdown.text/text {:size :label
:weight :medium
:style {:color counter-text-color}}
counter-value]])]))
+7 -10
View File
@@ -1,7 +1,7 @@
(ns quo2.components.dividers.new-messages
(:require [quo2.components.markdown.text :as text]
(:require [react-native.core :as rn]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]))
(defn new-messages
@@ -9,14 +9,11 @@
[{:keys [label color] :or {color :primary}}]
(let [bg-color (colors/custom-color-by-theme color 50 60 5 5)
text-color (colors/custom-color-by-theme color 50 60)]
[linear-gradient/linear-gradient
{:colors [bg-color "rgba(0,0,0,0)"]
:start {:x 0 :y 0}
:end {:x 0 :y 1}}
[rn/view
{:style {:padding-left 60
:padding-vertical 12
:padding-right 24}}
[linear-gradient/linear-gradient {:colors [bg-color "rgba(0,0,0,0)"]
:start {:x 0 :y 0} :end {:x 0 :y 1}}
[rn/view {:style {:padding-left 60
:padding-vertical 12
:padding-right 24}}
[text/text
{:size :paragraph-2
:weight :medium
@@ -5,21 +5,21 @@
(defn render-action-drawer
([options]
(rtl/render (reagent/as-element [action-drawer/action-drawer options]))))
(rtl/render (reagent/as-element [action-drawer/action-drawer options]))))
(js/global.test "action-drawer renders with elements label displaying"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"}]])
(-> (js/expect (rtl/screen.getByText "a sample label"))
(render-action-drawer [[{:icon :i/friend
:label "a sample label"}]])
(-> (js/expect (rtl/screen.getByText "a sample label"))
(.toBeTruthy))))
(js/global.test "action-drawer renders with elements sub-label displaying"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:sub-label "a sample sub label"}]])
(-> (js/expect (rtl/screen.getByText "a sample sub label"))
(-> (js/expect (rtl/screen.getByText "a sample sub label"))
(.toBeTruthy))))
(js/global.test "action-drawer on click action works on element"
@@ -29,14 +29,14 @@
:label "a sample label"
:on-press event}]])
(rtl/fireEvent.press (rtl/screen.getByText "a sample label"))
(-> (js/expect event)
(-> (js/expect event)
(.toHaveBeenCalled)))))
(js/global.test "action-drawer renders two icons when set"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:right-icon :i/friend
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:right-icon :i/friend
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getByLabelText "right-icon-for-action"))
(.toBeTruthy))
@@ -45,19 +45,19 @@
(js/global.test "action-drawer does not render a divider when the add-divider? prop is false"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? false
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? false
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(.not)
(.toBeTruthy))))
(js/global.test "action-drawer renders a divider when the add-divider? prop is true"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? true
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? true
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(.toBeTruthy))))
+47 -57
View File
@@ -1,62 +1,54 @@
(ns quo2.components.drawers.action-drawers
(:require [quo2.components.icon :as icon]
(:require [react-native.core :as rn]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
[quo2.components.icon :as icon]
[quo2.foundations.colors :as colors]))
(defn- get-icon-color
[danger?]
(defn- get-icon-color [danger?]
(if danger?
colors/danger-50
(colors/theme-colors colors/neutral-50 colors/neutral-40)))
(def divider
[rn/view
{:style {:border-top-width 1
:border-top-color (colors/theme-colors
colors/neutral-10
colors/neutral-90)
:margin-top 8
:margin-bottom 7
:align-items :center
:flex-direction :row}
:accessible true
:accessibility-label :divider}])
[rn/view {:style {:border-top-width 1
:border-top-color (colors/theme-colors
colors/neutral-10 colors/neutral-90)
:margin-top 8
:margin-bottom 7
:align-items :center
:flex-direction :row}
:accessible true
:accessibility-label :divider}])
(defn action
[{:keys [icon
label
sub-label
right-icon
danger?
on-press
add-divider?
accessibility-label]
:as action-props}]
(defn action [{:keys [icon
label
sub-label
right-icon
danger?
on-press
add-divider?
accessibility-label] :as action-props}]
(when action-props
[:<> {:key label}
(when add-divider? divider)
[rn/touchable-highlight
{:accessibility-label accessibility-label
:style {:border-radius 12
:height (if sub-label 58 50)
:margin-horizontal 8}
:underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-90)
:on-press on-press}
[rn/view
{:style
{:height (if sub-label 58 50)
:margin-horizontal 12
:flex-direction :row}}
[rn/view
{:accessibility-label :left-icon-for-action
:accessible true
:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:margin-right 12
:width 20}}
[rn/touchable-highlight {:accessibility-label accessibility-label
:style {:border-radius 12
:height (if sub-label 58 50)
:margin-horizontal 8}
:underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-90)
:on-press on-press}
[rn/view {:style
{:height (if sub-label 58 50)
:margin-horizontal 12
:flex-direction :row}}
[rn/view {:accessibility-label :left-icon-for-action
:accessible true
:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:margin-right 12
:width 20}}
[icon/icon icon
{:color (get-icon-color danger?)
:size 20}]]
@@ -78,20 +70,18 @@
(colors/theme-colors colors/neutral-50 colors/neutral-40)}}
sub-label])]
(when right-icon
[rn/view
{:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:width 20}
:accessible true
:accessibility-label :right-icon-for-action}
[rn/view {:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:width 20}
:accessible true
:accessibility-label :right-icon-for-action}
[icon/icon right-icon
{:color (get-icon-color danger?)
:size 20}]])]]]))
(defn action-drawer
[sections]
(defn action-drawer [sections]
[:<>
(doall
(for [actions sections]
+92 -109
View File
@@ -1,108 +1,97 @@
(ns quo2.components.dropdowns.dropdown
(:require [quo2.components.icon :as icons]
(:require [react-native.core :as rn]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]))
(defn apply-anim
[dd-height val]
(defn apply-anim [dd-height val]
(reanimated/animate-shared-value-with-delay dd-height
val
300
:easing1
0))
(def sizes
{:big {:icon-size 20
:font {:font-size :paragraph-1}
:height 40
:padding {:padding-with-icon {:padding-vertical 9
:padding-horizontal 12}
:padding-with-no-icon {:padding-vertical 9
:padding-left 12
:padding-right 8}}}
:medium {:icon-size 20
:font {:font-size :paragraph-1}
:height 32
:padding {:padding-with-icon {:padding-vertical 5
:padding-horizontal 8}
:padding-with-no-icon {:padding-vertical 5
:padding-left 12
:padding-right 8}}}
:small {:icon-size 15
:font {:font-size :paragraph-2}
:height 24
:padding {:padding-with-icon {:padding-vertical 3
:padding-horizontal 6}
:padding-with-no-icon {:padding-vertical 3
:padding-horizontal 6}}}})
(def sizes {:big {:icon-size 20
:font {:font-size :paragraph-1}
:height 40
:padding {:padding-with-icon {:padding-vertical 9
:padding-horizontal 12}
:padding-with-no-icon {:padding-vertical 9
:padding-left 12
:padding-right 8}}}
:medium {:icon-size 20
:font {:font-size :paragraph-1}
:height 32
:padding {:padding-with-icon {:padding-vertical 5
:padding-horizontal 8}
:padding-with-no-icon {:padding-vertical 5
:padding-left 12
:padding-right 8}}}
:small {:icon-size 15
:font {:font-size :paragraph-2}
:height 24
:padding {:padding-with-icon {:padding-vertical 3
:padding-horizontal 6}
:padding-with-no-icon {:padding-vertical 3
:padding-horizontal 6}}}})
(defn color-by-10
[color]
(colors/alpha color 0.6))
(defn dropdown-comp
[{:keys [icon open? dd-height size disabled? dd-color use-border? border-color]}]
(defn dropdown-comp [{:keys [icon open? dd-height size disabled? dd-color use-border? border-color]}]
(let [dark? (colors/dark?)
{:keys [width height width-with-icon padding font icon-size]} (size sizes)
{:keys [padding-with-icon padding-with-no-icon]} padding
font-size (:font-size font)
spacing (case size
:big 4
:big 4
:medium 2
:small 2)]
[rn/touchable-opacity
(cond->
{:on-press (fn []
(if (swap! open? not)
(apply-anim dd-height 120)
(apply-anim dd-height 0)))
:style (cond->
(merge
(if icon
padding-with-icon
padding-with-no-icon)
{:width (if icon
width-with-icon
width)
:height height
:border-radius (case size
:big 12
:medium 10
:small 8)
:flex-direction :row
:align-items :center
:background-color (if @open?
dd-color
(color-by-10 dd-color))})
use-border? (assoc :border-width 1
:border-color (if @open?
border-color
(color-by-10 border-color))))}
disabled? (assoc-in [:style :opacity] 0.3)
disabled? (assoc :disabled true))
:small 2)]
[rn/touchable-opacity (cond-> {:on-press (fn []
(if (swap! open? not)
(apply-anim dd-height 120)
(apply-anim dd-height 0)))
:style (cond-> (merge
(if icon
padding-with-icon
padding-with-no-icon)
{:width (if icon
width-with-icon
width)
:height height
:border-radius (case size
:big 12
:medium 10
:small 8)
:flex-direction :row
:align-items :center
:background-color (if @open?
dd-color
(color-by-10 dd-color))})
use-border? (assoc :border-width 1
:border-color (if @open? border-color (color-by-10 border-color))))}
disabled? (assoc-in [:style :opacity] 0.3)
disabled? (assoc :disabled true))
(when icon
[icons/icon icon
{:no-color true
:size 20
:container-style {:margin-right spacing
:margin-top 1
:width icon-size
:height icon-size}}])
[text/text
{:size font-size
:weight :medium
:font :font-medium
:color :main} "Dropdown"]
[icons/icon
(if @open?
(if dark?
:main-icons/pullup-dark
:main-icons/pullup)
(if dark?
:main-icons/dropdown-dark
:main-icons/dropdown))
[icons/icon icon {:no-color true
:size 20
:container-style {:margin-right spacing
:margin-top 1
:width icon-size
:height icon-size}}])
[text/text {:size font-size
:weight :medium
:font :font-medium
:color :main} "Dropdown"]
[icons/icon (if @open?
(if dark?
:main-icons/pullup-dark
:main-icons/pullup)
(if dark?
:main-icons/dropdown-dark
:main-icons/dropdown))
{:size 20
:no-color true
:container-style {:width (+ icon-size 3)
@@ -116,10 +105,9 @@
(defn items-comp
[{:keys [items on-select]}]
(let [items-count (count items)]
[rn/scroll-view
{:style {:height "100%"}
:horizontal false
:nestedScrollEnabled true}
[rn/scroll-view {:style {:height "100%"}
:horizontal false
:nestedScrollEnabled true}
(doall
(map-indexed (fn [index item]
[rn/touchable-opacity
@@ -133,32 +121,27 @@
colors/white)
:text-align :center}
:on-press #(on-select item)}
[text/text {:style {:text-align :center}} item]])
items))]))
[text/text {:style {:text-align :center}} item]]) items))]))
(defn dropdown
[{:keys [items icon text default-item on-select size disabled? border-color use-border? dd-color]}]
(defn dropdown [{:keys [items icon text default-item on-select size disabled? border-color use-border? dd-color]}]
[:f>
(fn []
(let [open? (reagent/atom false)
dd-height (reanimated/use-shared-value 0)]
[rn/view {:style {:flex-grow 1}}
[dropdown-comp
{:items items
:icon icon
:disabled? disabled?
:size size
:dd-color dd-color
:text text
:border-color (colors/custom-color-by-theme border-color 50 60)
:use-border? use-border?
:default-item default-item
:open? open?
:dd-height dd-height}]
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:height dd-height}
{:height dd-height})}
[items-comp
{:items items
:on-select on-select}]]]))])
[dropdown-comp {:items items
:icon icon
:disabled? disabled?
:size size
:dd-color dd-color
:text text
:border-color (colors/custom-color-by-theme border-color 50 60)
:use-border? use-border?
:default-item default-item
:open? open?
:dd-height dd-height}]
[reanimated/view {:style (reanimated/apply-animations-to-style
{:height dd-height}
{:height dd-height})}
[items-comp {:items items
:on-select on-select}]]]))])
+90 -120
View File
@@ -1,16 +1,15 @@
(ns quo2.components.header
(:require [oops.core :refer [oget]]
[react-native.reanimated :as reanimated]
[quo2.components.buttons.button :as button]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]))
(def header-height 56)
(defn header-wrapper-style
[{:keys [height border-bottom background]}]
(defn header-wrapper-style [{:keys [height border-bottom background]}]
(merge
{:background-color (colors/theme-colors
colors/neutral-5
@@ -24,37 +23,32 @@
colors/neutral-5
colors/neutral-95)})))
(def absolute-fill
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def absolute-fill {:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def content
{:flex 1
:flex-direction :row
:align-items :center
:justify-content :center})
(def content {:flex 1
:flex-direction :row
:align-items :center
:justify-content :center})
(def left
{:position :absolute
:left 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-start})
(def left {:position :absolute
:left 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-start})
(def right
{:position :absolute
:right 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-end})
(def right {:position :absolute
:right 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-end})
(defn title-style
[{:keys [left right]} title-align]
(defn title-style [{:keys [left right]} title-align]
(merge
absolute-fill
(case title-align
@@ -75,66 +69,57 @@
(def header-action-placeholder
{:width 16})
(def element
{:align-items :center
:justify-content :center
:flex 1})
(def element {:align-items :center
:justify-content :center
:flex 1})
(defn header-action
[{:keys [icon label on-press disabled accessibility-label]}]
[button/button
(merge {:on-press on-press
:disabled disabled}
(cond
icon {:type :icon
:theme :icon}
label {:type :secondary})
(when accessibility-label
{:accessibility-label accessibility-label}))
(defn header-action [{:keys [icon label on-press disabled accessibility-label]}]
[button/button (merge {:on-press on-press
:disabled disabled}
(cond
icon {:type :icon
:theme :icon}
label {:type :secondary})
(when accessibility-label
{:accessibility-label accessibility-label}))
(cond
icon icon
label label)])
(defn header-actions
[{:keys [accessories component]}]
(defn header-actions [{:keys [accessories component]}]
[rn/view {:style element}
(cond
(seq accessories)
(into [rn/view {:style header-actions-style}]
(map header-action accessories))
component component
component component
:else
[rn/view {:style header-action-placeholder}])])
(defn header-title
[{:keys [title subtitle component title-align]}]
(defn header-title [{:keys [title subtitle component title-align]}]
[:<>
(cond
component component
component component
(and title subtitle)
[:<>
[text/text
{:weight :medium
:number-of-lines 1}
[text/text {:weight :medium
:number-of-lines 1}
title]
[text/text
{:weight :regular
:color :secondary
:number-of-lines 1}
[text/text {:weight :regular
:color :secondary
:number-of-lines 1}
subtitle]]
title [text/text
{:weight :bold
:number-of-lines 0
:align title-align
:size :large}
title])])
title [text/text {:weight :bold
:number-of-lines 0
:align title-align
:size :large}
title])])
(defn header
[{:keys [left-width right-width]}]
(defn header [{:keys [left-width right-width]}]
(let [layout (reagent/atom {:left {:width (or left-width 8)
:height header-height}
:right {:width (or right-width 8)
@@ -146,61 +131,46 @@
(let [width (oget evt "nativeEvent" "layout" "width")
height (oget evt "nativeEvent" "layout" "height")]
(when get-layout
(get-layout el
{:width width
:height height}))
(swap! layout assoc
el
{:width width
:height height}))))]
(fn
[{:keys [left-accessories left-component border-bottom
right-accessories right-component insets get-layout
title subtitle title-component style title-align
background]
:or {title-align :center
border-bottom false}}]
(get-layout el {:width width
:height height}))
(swap! layout assoc el {:width width
:height height}))))]
(fn [{:keys [left-accessories left-component border-bottom
right-accessories right-component insets get-layout
title subtitle title-component style title-align
background]
:or {title-align :center
border-bottom false}}]
(let [status-bar-height (get insets :top 0)
height (+ header-height status-bar-height)]
[reanimated/view
{:style (header-wrapper-style {:height height
:background background
:border-bottom border-bottom})}
[rn/view
{:pointer-events :box-none
:height status-bar-height}]
[rn/view
{:style (merge {:height header-height}
style)
:pointer-events :box-none}
[rn/view
{:style absolute-fill
:pointer-events :box-none}
[rn/view
{:style content
:pointer-events :box-none}
[rn/view
{:style left
:on-layout (handle-layout :left get-layout)
:pointer-events :box-none}
[header-actions
{:accessories left-accessories
:component left-component}]]
[reanimated/view {:style (header-wrapper-style {:height height
:background background
:border-bottom border-bottom})}
[rn/view {:pointer-events :box-none
:height status-bar-height}]
[rn/view {:style (merge {:height header-height}
style)
:pointer-events :box-none}
[rn/view {:style absolute-fill
:pointer-events :box-none}
[rn/view {:style content
:pointer-events :box-none}
[rn/view {:style left
:on-layout (handle-layout :left get-layout)
:pointer-events :box-none}
[header-actions {:accessories left-accessories
:component left-component}]]
[rn/view
{:style (title-style @layout title-align)
:on-layout (handle-layout :title get-layout)
:pointer-events :box-none}
[header-title
{:title title
:subtitle subtitle
:title-align title-align
:component title-component}]]
[rn/view {:style (title-style @layout title-align)
:on-layout (handle-layout :title get-layout)
:pointer-events :box-none}
[header-title {:title title
:subtitle subtitle
:title-align title-align
:component title-component}]]
[rn/view
{:style right
:on-layout (handle-layout :right get-layout)
:pointer-events :box-none}
[header-actions
{:accessories right-accessories
:component right-component}]]]]]]))))
[rn/view {:style right
:on-layout (handle-layout :right get-layout)
:pointer-events :box-none}
[header-actions {:accessories right-accessories
:component right-component}]]]]]]))))
+8 -8
View File
@@ -1,15 +1,15 @@
(ns quo2.components.icon
(:require [clojure.string :as string]
[quo2.components.icons.icons :as icons]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(:require
[clojure.string :as string]
[react-native.core :as rn]
[quo2.components.icons.icons :as icons]
[quo2.foundations.colors :as colors]))
(defn memo-icon-fn
([icon-name] (memo-icon-fn icon-name nil))
([icon-name
{:keys [color container-style size
accessibility-label no-color]
:or {accessibility-label :icon}}]
([icon-name {:keys [color container-style size
accessibility-label no-color]
:or {accessibility-label :icon}}]
(let [size (or size 20)]
^{:key icon-name}
[rn/image
+5 -10
View File
@@ -4,14 +4,12 @@
(def icon-path "./resources/images/icons2/")
(defn combine-path
[path el]
(defn combine-path [path el]
(if (System/getenv "COMPONENT_TEST")
(str "." path el "@2x.png")
(str "." path el ".png")))
(defn require-icon
[size path]
(defn require-icon [size path]
(fn [el]
(let [s (combine-path path el)
k (-> el
@@ -21,21 +19,18 @@
(str size))]
[k `(js/require ~s)])))
(defn get-files
[path]
(defn get-files [path]
(->> (io/file path)
file-seq
(filter #(string/ends-with? % "png"))
(map #(first (string/split (.getName %) #"@")))
distinct))
(defn get-icons
[size]
(defn get-icons [size]
(let [path (str icon-path size "x" size "/")]
(into {} (map (require-icon size path) (get-files path)))))
(defmacro resolve-icons
[]
(defmacro resolve-icons []
(merge
(get-icons 12)
(get-icons 16)
+1 -2
View File
@@ -4,8 +4,7 @@
(def icons (icons/resolve-icons))
(defn icon-source
[icon]
(defn icon-source [icon]
(if-let [icon (get icons (name icon))]
icon
(do
+24 -28
View File
@@ -1,20 +1,19 @@
(ns quo2.components.info.info-message
(:require [quo2.components.icon :as quo2.icons]
(:require [quo2.theme :as theme]
[react-native.core :as rn]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
[quo2.components.icon :as quo2.icons]))
(def themes
{:light {:default colors/neutral-40
:success colors/success-50
:error colors/danger-50}
:dark {:default colors/neutral-60
:success colors/success-60
:error colors/danger-60}})
{:light {:default colors/neutral-40
:success colors/success-50
:error colors/danger-50}
:dark {:default colors/neutral-60
:success colors/success-60
:error colors/danger-60}})
(defn get-color
[key]
(defn get-color [key]
(get-in themes [(theme/get-theme) key]))
(defn info-message
@@ -27,20 +26,17 @@
:icon-color colors/white ;; icon color override
:no-icon-color? false ;; disable tint color for icon"
[{:keys [type size icon text-color icon-color no-icon-color?]} message]
(let [weight (if (= size :default) :regular :medium)
size (if (= size :default) :paragraph-2 :label)
text-color (or text-color (get-color type))
icon-color (or icon-color text-color)]
[rn/view
{:style {:flex-direction :row
:flex 1}}
[quo2.icons/icon icon
{:color icon-color
:no-color no-icon-color?
:size 12
:container-style {:margin-top 3}}]
[text/text
{:size size
:weight weight
:style {:color text-color
:margin-horizontal 8}} message]]))
(let [weight (if (= size :default) :regular :medium)
size (if (= size :default) :paragraph-2 :label)
text-color (or text-color (get-color type))
icon-color (or icon-color text-color)]
[rn/view {:style {:flex-direction :row
:flex 1}}
[quo2.icons/icon icon {:color icon-color
:no-color no-icon-color?
:size 12
:container-style {:margin-top 3}}]
[text/text {:size size
:weight weight
:style {:color text-color
:margin-horizontal 8}} message]]))
+58 -67
View File
@@ -1,46 +1,44 @@
(ns quo2.components.info.information-box
(:require [clojure.string :as string]
[quo2.components.buttons.button :as quo2.button]
[quo2.components.icon :as quo2.icons]
[quo2.components.info.info-message :as info-message]
(:require [quo2.theme :as theme]
[react-native.core :as rn]
[clojure.string :as string]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
[quo2.components.icon :as quo2.icons]
[quo2.components.buttons.button :as quo2.button]
[quo2.components.info.info-message :as info-message]))
(def themes
{:light {:default {:bg colors/white
:border colors/neutral-20
:icon colors/neutral-50
:text colors/neutral-100}
:informative {:bg colors/primary-50-opa-5
:border colors/primary-50-opa-10
:icon colors/primary-50
:text colors/neutral-100}
:error {:bg colors/danger-50-opa-5
:border colors/danger-50-opa-10
:icon colors/danger-50
:text colors/danger-50}
{:light {:default {:bg colors/white
:border colors/neutral-20
:icon colors/neutral-50
:text colors/neutral-100}
:informative {:bg colors/primary-50-opa-5
:border colors/primary-50-opa-10
:icon colors/primary-50
:text colors/neutral-100}
:error {:bg colors/danger-50-opa-5
:border colors/danger-50-opa-10
:icon colors/danger-50
:text colors/danger-50}
:close-button colors/neutral-100}
:dark {:default {:bg colors/neutral-90
:border colors/neutral-70
:icon colors/neutral-40
:text colors/white}
:informative {:bg colors/primary-50-opa-5
:border colors/primary-50-opa-10
:icon colors/white
:text colors/white}
:error {:bg colors/danger-50-opa-5
:border colors/danger-50-opa-10
:icon colors/danger-50
:text colors/danger-50}
:dark {:default {:bg colors/neutral-90
:border colors/neutral-70
:icon colors/neutral-40
:text colors/white}
:informative {:bg colors/primary-50-opa-5
:border colors/primary-50-opa-10
:icon colors/white
:text colors/white}
:error {:bg colors/danger-50-opa-5
:border colors/danger-50-opa-10
:icon colors/danger-50
:text colors/danger-50}
:close-button colors/white}})
(defn get-color
[key]
(defn get-color [key]
(get-in themes [(theme/get-theme) key]))
(defn get-color-by-type
[type key]
(defn get-color-by-type [type key]
(get-in themes [(theme/get-theme) type key]))
(defn information-box
@@ -56,47 +54,40 @@
:button-label \"PressMe\" ;; add action button with label
:on-button-press action ;; (required for information box with button-label)
:on-close on-close ;; (optional on-close call)"
[{:keys [type closable? closed? id icon style button-label on-button-press on-close no-icon-color?]}
message]
[{:keys [type closable? closed? id icon style button-label on-button-press on-close no-icon-color?]} message]
(let [background-color (get-color-by-type type :bg)
border-color (get-color-by-type type :border)
icon-color (get-color-by-type type :icon)
text-color (get-color-by-type type :text)
include-button? (not (string/blank? button-label))]
(when-not closed?
[rn/view
{:accessibility-label (or id :information-box)
:style (merge {:background-color background-color
:border-color border-color
:border-width 1
:border-radius 12
:padding-top (if include-button? 10 11)
:padding-bottom (if include-button? 12 11)
:padding-horizontal 16}
style)}
[rn/view
{:style {:flex-direction :row
:align-self :flex-start}}
[info-message/info-message
{:size :default
:icon icon
:text-color text-color
:icon-color icon-color
:no-icon-color? no-icon-color?} message]
[rn/view {:accessibility-label (or id :information-box)
:style (merge {:background-color background-color
:border-color border-color
:border-width 1
:border-radius 12
:padding-top (if include-button? 10 11)
:padding-bottom (if include-button? 12 11)
:padding-horizontal 16} style)}
[rn/view {:style {:flex-direction :row
:align-self :flex-start}}
[info-message/info-message {:size :default
:icon icon
:text-color text-color
:icon-color icon-color
:no-icon-color? no-icon-color?} message]
(when closable?
[rn/touchable-opacity
{:on-press on-close
:accessibility-label (str (or id "information-box") "-close-button")}
[quo2.icons/icon :i/close
{:size 12
:color (get-color :close-button)
:container-style {:margin-top 4
:margin-left 8}}]])]
[quo2.icons/icon :i/close {:size 12
:color (get-color :close-button)
:container-style {:margin-top 4
:margin-left 8}}]])]
(when include-button?
[quo2.button/button
{:type :primary
:size 24
:on-press on-button-press
:style {:margin-left 20
:margin-top 8
:align-self :flex-start}} button-label])])))
[quo2.button/button {:type :primary
:size 24
:on-press on-button-press
:style {:margin-left 20
:margin-top 8
:align-self :flex-start}} button-label])])))
+21 -26
View File
@@ -7,28 +7,25 @@
[quo2.theme :as theme]
[react-native.core :as rn]))
(defn list-item
[{:keys [name locked? mentions-count unread-messages?
muted? is-active-channel? emoji channel-color on-press]
:or {channel-color colors/primary-50}}]
(defn list-item [{:keys [name locked? mentions-count unread-messages?
muted? is-active-channel? emoji channel-color on-press]
:or {channel-color colors/primary-50}}]
[rn/touchable-opacity {:on-press on-press}
[rn/view
{:style (merge {:height 48
:display :flex
:border-radius 12
:flex-direction :row
:justify-content :space-between
:align-items :center
:width "100%"
:padding-left 12
:padding-right 12}
(when is-active-channel?
{:background-color (colors/theme-alpha channel-color 0.05 0.05)}))}
[rn/view
{:display :flex
:flex-direction :row
:justify-content :flex-start
:align-items :center}
[rn/view {:style (merge {:height 48
:display :flex
:border-radius 12
:flex-direction :row
:justify-content :space-between
:align-items :center
:width "100%"
:padding-left 12
:padding-right 12}
(when is-active-channel?
{:background-color (colors/theme-alpha channel-color 0.05 0.05)}))}
[rn/view {:display :flex
:flex-direction :row
:justify-content :flex-start
:align-items :center}
[channel-avatar/channel-avatar
{:big? true
:locked? locked?
@@ -38,8 +35,7 @@
{:style (merge {:margin-left 12}
(when (and (not locked?) muted?)
{:color (if (theme/dark?) colors/neutral-60 colors/neutral-40)}))
:weight :medium
:size :paragraph-1} (str "# " name)]]
:weight :medium :size :paragraph-1} (str "# " name)]]
[rn/view {:style {:height 20}}
(when (and (not locked?)
muted?)
@@ -49,9 +45,8 @@
(when (and (not locked?)
(not muted?)
(pos? (int mentions-count)))
[rn/view
{:style {:margin-right 2
:margin-top 2}}
[rn/view {:style {:margin-right 2
:margin-top 2}}
[quo2.counter/counter {:override-bg-color channel-color} mentions-count]])
(when (and (not locked?)
(not muted?)
+28 -32
View File
@@ -1,18 +1,17 @@
(ns quo2.components.list-items.menu-item
(:require [quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors :refer [theme-colors]]
[react-native.core :as rn]))
[quo2.components.markdown.text :as text]
[quo2.components.icon :as icons]))
(defn themes
[type]
(defn themes [type]
(case type
:main {:icon-color (theme-colors colors/neutral-50 colors/neutral-10)
:background (theme-colors colors/white colors/neutral-90)
:text-color (theme-colors colors/neutral-100 colors/white)}
:danger {:icon-color (theme-colors colors/danger-50 colors/danger-60)
:background (theme-colors colors/white colors/neutral-90)
:text-color (theme-colors colors/danger-50 colors/danger-60)}))
:main {:icon-color (theme-colors colors/neutral-50 colors/neutral-10)
:background (theme-colors colors/white colors/neutral-90)
:text-color (theme-colors colors/neutral-100 colors/white)}
:danger {:icon-color (theme-colors colors/danger-50 colors/danger-60)
:background (theme-colors colors/white colors/neutral-90)
:text-color (theme-colors colors/danger-50 colors/danger-60)}))
(defn menu-item
[{:keys [type title accessibility-label icon on-press]
@@ -20,28 +19,25 @@
(let [{:keys [icon-color text-color background]} (themes type)]
[rn/touchable-opacity
(merge {:accessibility-label accessibility-label
:style {:background-color background
:height 48
:flex-direction :row
:align-items :center}}
:style {:background-color background
:height 48
:flex-direction :row
:align-items :center}}
(when on-press
{:on-press on-press}))
[rn/view
{:style {:flex-direction :row
:flex-grow 0
:flex-shrink 1
:padding-horizontal 20}}
[rn/view
{:style {:width 20
:height 20
:align-items :center
:justify-content :center
:margin-right 12}}
[rn/view {:style {:flex-direction :row
:flex-grow 0
:flex-shrink 1
:padding-horizontal 20}}
[rn/view {:style {:width 20
:height 20
:align-items :center
:justify-content :center
:margin-right 12}}
[icons/icon icon {:color icon-color}]]
[text/text
{:weight :medium
:style {:color text-color}
:ellipsize-mode :tail
:number-of-lines 1
:size :paragraph-1}
[text/text {:weight :medium
:style {:color text-color}
:ellipsize-mode :tail
:number-of-lines 1
:size :paragraph-1}
title]]]))
@@ -1,112 +1,97 @@
(ns quo2.components.list-items.preview-list
(:require [quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.icon :as quo2.icons]
[quo2.components.markdown.text :as quo2.text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
(:require [react-native.core :as rn]
[react-native.hole-view :as hole-view]
[react-native.fast-image :as fast-image]
[react-native.hole-view :as hole-view]))
[quo2.foundations.colors :as colors]
[quo2.components.icon :as quo2.icons]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.markdown.text :as quo2.text]))
(def params
{32 {:border-radius {:circular 16 :rounded 10}
:hole-radius {:circular 18 :rounded 12}
:margin-left -8
:hole-size 36
:hole-x 22
:hole-y -2}
24 {:border-radius {:circular 12 :rounded 8}
:hole-radius {:circular 13 :rounded 9}
:margin-left -4
:hole-size 26
:hole-x 19
:hole-y -1}
16 {:border-radius {:circular 8 :rounded 8}
:hole-radius {:circular 9 :rounded 9}
:margin-left -4
:hole-size 18
:hole-x 11
:hole-y -1}})
(def params {32 {:border-radius {:circular 16 :rounded 10}
:hole-radius {:circular 18 :rounded 12}
:margin-left -8
:hole-size 36
:hole-x 22
:hole-y -2}
24 {:border-radius {:circular 12 :rounded 8}
:hole-radius {:circular 13 :rounded 9}
:margin-left -4
:hole-size 26
:hole-x 19
:hole-y -1}
16 {:border-radius {:circular 8 :rounded 8}
:hole-radius {:circular 9 :rounded 9}
:margin-left -4
:hole-size 18
:hole-x 11
:hole-y -1}})
;; TODO - Add avatar components for other types once implemented
(defn avatar
[item type size border-radius]
(defn avatar [item type size border-radius]
(case type
:user [user-avatar/user-avatar
(merge {:ring? false
:status-indicator? false
:size (case size
32 :small
24 :xs
16 :xxxs)}
item)]
(:photo :collectible) [fast-image/fast-image
{:source (:source item)
:style {:width size
:height size
:border-radius border-radius}}]))
:user [user-avatar/user-avatar
(merge {:ring? false
:status-indicator? false
:size (case size 32 :small 24 :xs 16 :xxxs)}
item)]
(:photo :collectible) [fast-image/fast-image {:source (:source item)
:style {:width size
:height size
:border-radius border-radius}}]))
(defn list-item
[index type size item list-size margin-left
hole-size hole-radius hole-x hole-y border-radius]
(defn list-item [index type size item list-size margin-left
hole-size hole-radius hole-x hole-y border-radius]
(let [last-item? (= index (- list-size 1))]
[hole-view/hole-view
{:style {:margin-left (if (= index 0) 0 margin-left)}
:holes (if last-item?
[]
[{:x hole-x
:y hole-y
:width hole-size
:height hole-size
:borderRadius hole-radius}])}
[hole-view/hole-view {:style {:margin-left (if (= index 0) 0 margin-left)}
:holes (if last-item? []
[{:x hole-x
:y hole-y
:width hole-size
:height hole-size
:borderRadius hole-radius}])}
[avatar item type size border-radius]]))
(defn get-overflow-color
[transparent? transparent-color light-color dark-color override-theme]
(defn get-overflow-color [transparent? transparent-color light-color dark-color override-theme]
(if transparent?
transparent-color
(colors/theme-colors light-color dark-color override-theme)))
(defn overflow-label
[label size transparent? border-radius margin-left override-theme more-than-99-label]
[rn/view
{:style {:width size
:height size
:margin-left margin-left
:border-radius border-radius
:justify-content :center
:align-items :center
:background-color (get-overflow-color
transparent?
colors/white-opa-10
colors/neutral-20
colors/neutral-70
override-theme)}}
(defn overflow-label [label size transparent? border-radius margin-left override-theme more-than-99-label]
[rn/view {:style {:width size
:height size
:margin-left margin-left
:border-radius border-radius
:justify-content :center
:align-items :center
:background-color (get-overflow-color
transparent?
colors/white-opa-10
colors/neutral-20
colors/neutral-70
override-theme)}}
(if (= size 16)
[quo2.icons/icon :i/more
{:size 12
:color (get-overflow-color
transparent?
colors/white-opa-70
colors/neutral-50
colors/neutral-40
override-theme)}]
[quo2.text/text
{:size (if (= size 32) :paragraph-2 :label)
:weight :medium
:style {:color (get-overflow-color
transparent?
colors/white-opa-70
colors/neutral-60
colors/neutral-40
override-theme)
:margin-left -2}}
[quo2.icons/icon :i/more {:size 12
:color (get-overflow-color
transparent?
colors/white-opa-70
colors/neutral-50
colors/neutral-40
override-theme)}]
[quo2.text/text {:size (if (= size 32) :paragraph-2 :label)
:weight :medium
:style {:color (get-overflow-color
transparent?
colors/white-opa-70
colors/neutral-60
colors/neutral-40
override-theme)
:margin-left -2}}
;; If overflow label is below 100, show label as +label (ex. +30), else just show 99+
(if (< label 100)
(str "+" label)
more-than-99-label)])])
(defn border-type
[type]
(defn border-type [type]
(case type
(:account :collectible :photo) :rounded
:circular))
@@ -135,5 +120,4 @@
[list-item index type size (get items-arr index) list-size
margin-left hole-size hole-radius hole-x hole-y border-radius])
(when (> list-size 4)
[overflow-label (- list-size 3) size transparent? border-radius margin-left override-theme
more-than-99-label])]))
[overflow-label (- list-size 3) size transparent? border-radius margin-left override-theme more-than-99-label])]))
+49 -60
View File
@@ -1,27 +1,25 @@
(ns quo2.components.loaders.skeleton
(:require [quo2.foundations.colors :as colors]
[react-native.core :as rn]
(:require [react-native.core :as rn]
[react-native.masked-view :as masked-view]
[reagent.core :as reagent]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]))
[quo2.foundations.colors :as colors]))
(def message-skeleton-height 54)
(def avatar-skeleton-size 32)
(def message-content-width
[{:author 80
:message 249}
{:author 124
:message 156}
{:author 96
:message 212}
{:author 112
:message 144}])
(def message-content-width [{:author 80
:message 249}
{:author 124
:message 156}
{:author 96
:message 212}
{:author 112
:message 144}])
;; Standlone message skeleton
(defn message-skeleton
[]
(defn message-skeleton []
[:f>
(fn []
(let [color (colors/theme-colors colors/neutral-5 colors/neutral-70)
@@ -33,56 +31,47 @@
translate-x (reanimated/use-shared-value (- window-width))
animated-gradient-style (reanimated/apply-animations-to-style
{:transform [{:translateX translate-x}]}
{:width window-width
:height "100%"})]
{:width window-width
:height "100%"})]
(reanimated/animate-shared-value-with-repeat translate-x window-width 1000 :linear (- 1) false)
[masked-view/masked-view
{:style {:height message-skeleton-height}
{:style {:height message-skeleton-height}
:maskElement (reagent/as-element
[rn/view
{:style {:height message-skeleton-height
:flex-direction :row
:padding-vertical 11
:background-color :transparent
:padding-left 21}}
[rn/view
{:style {:height avatar-skeleton-size
:width avatar-skeleton-size
:border-radius (/ avatar-skeleton-size 2)
:background-color color
:overflow :hidden}}]
[rn/view
{:style {:padding-left 8
:background-color :transparent}}
[rn/view
{:style {:height 8
:width author-width
:border-radius 6
:background-color color
:margin-bottom 8
:overflow :hidden}}]
[rn/view
{:style {:height 16
:width message-width
:border-radius 6
:overflow :hidden
:background-color color}}]]])}
[rn/view
{:style {:flex 1
:background-color color}}
[reanimated/linear-gradient
{:colors [color color loading-color color color]
:start {:x 0 :y 0}
:end {:x 1 :y 0}
:style animated-gradient-style}]]]))])
[rn/view {:style {:height message-skeleton-height
:flex-direction :row
:padding-vertical 11
:background-color :transparent
:padding-left 21}}
[rn/view {:style {:height avatar-skeleton-size
:width avatar-skeleton-size
:border-radius (/ avatar-skeleton-size 2)
:background-color color
:overflow :hidden}}]
[rn/view {:style {:padding-left 8
:background-color :transparent}}
[rn/view {:style {:height 8
:width author-width
:border-radius 6
:background-color color
:margin-bottom 8
:overflow :hidden}}]
[rn/view {:style {:height 16
:width message-width
:border-radius 6
:overflow :hidden
:background-color color}}]]])}
[rn/view {:style {:flex 1
:background-color color}}
[reanimated/linear-gradient {:colors [color color loading-color color color]
:start {:x 0 :y 0}
:end {:x 1 :y 0}
:style animated-gradient-style}]]]))])
(defn skeleton
[parent-height]
(defn skeleton [parent-height]
(let [number-of-skeletons (int (Math/floor (/ parent-height message-skeleton-height)))]
[rn/view
{:style {:background-color (colors/theme-colors
colors/white
colors/neutral-90)
:flex 1}}
[rn/view {:style {:background-color (colors/theme-colors
colors/white
colors/neutral-90)
:flex 1}}
(for [n (range number-of-skeletons)]
[message-skeleton {:key n}])]))
+7 -10
View File
@@ -1,12 +1,11 @@
(ns quo2.components.markdown.text
(:require [quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]
(:require [react-native.core :as rn]
[quo2.theme :as theme]
[react-native.core :as rn]
[quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]
[reagent.core :as reagent]))
(defn text-style
[{:keys [size align weight style]}]
(defn text-style [{:keys [size align weight style]}]
(merge (case (or weight :regular)
:regular typography/font-regular
:medium typography/font-medium
@@ -27,12 +26,10 @@
style
(assoc style :color (if (= (theme/get-theme) :dark) colors/white colors/neutral-100)))))
(defn text
[]
(defn text []
(let [this (reagent/current-component)
props (reagent/props this)
style (text-style props)]
(into [rn/text
(merge {:style style}
(dissoc props :style :size :align :weight :color))]
(into [rn/text (merge {:style style}
(dissoc props :style :size :align :weight :color))]
(reagent/children this))))
@@ -8,12 +8,10 @@
:flex-direction :row
:align-items :center})
(defn ens-text
[]
(defn ens-text []
{:color (colors/theme-colors colors/neutral-100 colors/white)})
(defn nickname-text
[]
(defn nickname-text []
{:color (colors/theme-colors colors/neutral-100 colors/white)})
(def middle-dot-nickname
@@ -28,8 +26,7 @@
{:color colors/neutral-50
:margin-left 4})
(defn profile-name-text
[nickname?]
(defn profile-name-text [nickname?]
{:color (if nickname?
(colors/theme-colors colors/neutral-60 colors/neutral-40)
(colors/theme-colors colors/neutral-100 colors/white))})
@@ -37,7 +34,6 @@
(def icon-container
{:margin-left 4})
(defn time-text
[ens?]
(defn time-text [ens?]
{:color colors/neutral-50
:margin-left (if ens? 8 4)})
+42 -53
View File
@@ -1,68 +1,60 @@
(ns quo2.components.messages.author.view
(:require [clojure.string :as string]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.components.messages.author.style :as style]
[react-native.core :as rn]
[status-im.utils.utils :as utils]))
(:require
[quo2.components.messages.author.style :as style]
[react-native.core :as rn]
[quo2.components.markdown.text :as text]
[quo2.components.icon :as icons]
[status-im.utils.utils :as utils]
[clojure.string :as string]))
(def middle-dot "·")
(defn display-name
[{:keys [profile-name nickname ens-name text-style]}]
(defn display-name [{:keys [profile-name nickname ens-name text-style]}]
(let [ens? (-> ens-name string/blank? not)
nickname? (-> nickname string/blank? not)]
(if ens?
[text/text
(merge {:weight :semi-bold
:size :paragraph-2
:style (style/ens-text)}
text-style)
[text/text (merge {:weight :semi-bold
:size :paragraph-2
:style (style/ens-text)}
text-style)
ens-name]
[:<>
(if nickname?
[text/text
(merge {:weight :semi-bold
:size :paragraph-2
:style (style/nickname-text)}
text-style)
[text/text (merge {:weight :semi-bold
:size :paragraph-2
:style (style/nickname-text)}
text-style)
nickname]
[text/text
(merge {:weight :semi-bold
:size :paragraph-2
:style (style/profile-name-text nickname?)}
text-style)
[text/text (merge {:weight :semi-bold
:size :paragraph-2
:style (style/profile-name-text nickname?)}
text-style)
profile-name])])))
(defn author
[{:keys [profile-name nickname chat-key ens-name time-str contact? verified? untrustworthy?]}]
(defn author [{:keys [profile-name nickname chat-key ens-name time-str contact? verified? untrustworthy?]}]
[:f>
(fn []
(let [ens? (-> ens-name string/blank? not)
(let [ens? (-> ens-name string/blank? not)
nickname? (-> nickname string/blank? not)]
[rn/view {:style style/container}
(if ens?
[text/text
{:weight :semi-bold
:size :paragraph-2
:style (style/ens-text)}
[text/text {:weight :semi-bold
:size :paragraph-2
:style (style/ens-text)}
ens-name]
[:<>
(when nickname?
[:<>
[text/text
{:weight :semi-bold
:size :paragraph-2
:style (style/nickname-text)}
[text/text {:weight :semi-bold
:size :paragraph-2
:style (style/nickname-text)}
nickname]
[text/text
{:size :paragraph-2
:style style/middle-dot-nickname}
[text/text {:size :paragraph-2
:style style/middle-dot-nickname}
middle-dot]])
[text/text
{:weight (if nickname? :medium :semi-bold)
:size :paragraph-2
:style (style/profile-name-text nickname?)}
[text/text {:weight (if nickname? :medium :semi-bold)
:size :paragraph-2
:style (style/profile-name-text nickname?)}
profile-name]])
(when contact?
[icons/icon :main-icons2/contact
@@ -81,19 +73,16 @@
:no-color true
:container-style style/icon-container}])
(when-not ens?
[text/text
{:monospace true
:size :paragraph-2
:style style/chat-key-text}
[text/text {:monospace true
:size :paragraph-2
:style style/chat-key-text}
(utils/get-shortened-address chat-key)])
(when-not ens?
[text/text
{:monospace true
:size :paragraph-2
:style style/middle-dot-chat-key}
[text/text {:monospace true
:size :paragraph-2
:style style/middle-dot-chat-key}
middle-dot])
[text/text
{:monospace true
:size :paragraph-2
:style (style/time-text ens?)}
[text/text {:monospace true
:size :paragraph-2
:style (style/time-text ens?)}
time-str]]))])
+61 -74
View File
@@ -1,11 +1,12 @@
(ns quo2.components.messages.gap
(:require [oops.core :refer [oget]]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]
[reagent.core :as reagent]))
(:require
[oops.core :refer [oget]]
[react-native.core :as rn]
[quo2.theme :as theme]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[reagent.core :as reagent]))
;;; helpers
(def themes
@@ -16,8 +17,7 @@
:time colors/neutral-40
:background colors/neutral-95}})
(defn get-color
[key]
(defn get-color [key]
(get-in themes [(theme/get-theme) key]))
(def ui-images
@@ -27,51 +27,45 @@
:dark {:horizontal (js/require "../resources/images/ui/message-gap-hborder-dark.png")
:circles (js/require "../resources/images/ui/message-gap-circle-bg-dark.png")}})
(defn get-image
[key]
(defn get-image [key]
(get-in ui-images [(theme/get-theme) key]))
;;; components
;;;; borders
(defn hborder
[{:keys [type style]}]
[rn/image
{:source (get-image :horizontal)
:resize-mode :repeat
:style (merge {:position :absolute
:left 0
:padding-horizontal 4
:overflow :hidden
:width "110%"
:height 8
:margin-left -4}
(if (= type :top)
{:top 0}
{:transform [{:rotateZ "180deg"}]
:bottom 0})
style)}])
(defn hborder [{:keys [type style]}]
[rn/image {:source (get-image :horizontal)
:resize-mode :repeat
:style (merge {:position :absolute
:left 0
:padding-horizontal 4
:overflow :hidden
:width "110%"
:height 8
:margin-left -4}
(if (= type :top)
{:top 0}
{:transform [{:rotateZ "180deg"}]
:bottom 0})
style)}])
(defn vborder
[type body-height]
(defn vborder [type body-height]
(let [height @body-height
img (get-image :vertical)]
(when (and img height)
[rn/image
{:source img
:resize-mode :repeat
:style (merge
{:position :absolute
:top 4
:height (- height 8)
:width 4}
(if (= type :left)
{:left 0}
{:transform [{:rotate "180deg"}]
:right 0}))}])))
[rn/image {:source img
:resize-mode :repeat
:style (merge
{:position :absolute
:top 4
:height (- height 8)
:width 4}
(if (= type :left)
{:left 0}
{:transform [{:rotate "180deg"}]
:right 0}))}])))
;;;; others
(defn circle
[]
(defn circle []
[rn/view
{:width 9
:height 9
@@ -81,40 +75,34 @@
:border-color (get-color :icon)
:border-radius 50}])
(defn timestamp
[str]
[text/text
{:size :label
:style {:text-transform :none
:color (get-color :time)}} str])
(defn timestamp [str]
[text/text {:size :label
:style {:text-transform :none
:color (get-color :time)}} str])
(defn info-button
[on-press]
(defn info-button [on-press]
[rn/touchable-without-feedback
{:on-press on-press}
[icon/icon "message-gap-info" {:size 12 :no-color true :container-style {:padding 4}}]])
;;;; timeline/body
(defn timeline
[]
[rn/view
{:flex 0
:margin-right 20
:align-items :center
:width 9
:justify-content :space-between}
(defn timeline []
[rn/view {:flex 0
:margin-right 20
:align-items :center
:width 9
:justify-content :space-between}
[circle]
[rn/image {:style {:flex 1} :source (get-image :circles) :resize-mode :repeat}]
[circle]])
(defn body
[timestamp-far timestamp-near on-info-button-pressed on-press warning-label]
(defn body [timestamp-far timestamp-near on-info-button-pressed on-press warning-label]
[rn/view {:flex 1}
[rn/view
{:flex-direction :row
:align-items :center
:justify-content :space-between
:margin-right 2}
{:flex-direction :row
:align-items :center
:justify-content :space-between
:margin-right 2}
[timestamp timestamp-far]
(when on-info-button-pressed [info-button on-info-button-pressed])]
@@ -143,14 +131,13 @@
:flex 1}
[hborder {:type :top}]
[hborder {:type :bottom}]
[rn/view
(merge {:width "100%"
:background-color (get-color :background)
:flex-direction :row
:padding 20
:padding-left 31
:margin-vertical 4}
style)
[rn/view (merge {:width "100%"
:background-color (get-color :background)
:flex-direction :row
:padding 20
:padding-left 31
:margin-vertical 4}
style)
[timeline]
[body timestamp-far timestamp-near on-info-button-pressed on-press warning-label]]
+85 -125
View File
@@ -8,10 +8,9 @@
[react-native.reanimated :as reanimated]
[utils.string :as utils]))
(def themes-landed
{:pinned colors/primary-50-opa-5
:added colors/primary-50-opa-5
:deleted colors/danger-50-opa-5})
(def themes-landed {:pinned colors/primary-50-opa-5
:added colors/primary-50-opa-5
:deleted colors/danger-50-opa-5})
(def themes
{:light {:text colors/neutral-100
@@ -25,149 +24,115 @@
:pressed colors/neutral-80
:landed themes-landed}}})
(defn get-color
[& keys]
(defn get-color [& keys]
(reduce (fn [acc k] (get acc k (reduced acc)))
((theme/get-theme) themes)
(vec keys)))
((theme/get-theme) themes) (vec keys)))
(defn sm-timestamp
[timestamp-str]
(defn sm-timestamp [timestamp-str]
[rn/view {:margin-left 6}
[text/text
{:size :label
:style {:color (get-color :time)
:text-transform :none}}
[text/text {:size :label
:style {:color (get-color :time)
:text-transform :none}}
timestamp-str]])
(defn sm-icon
[{:keys [icon color opacity]}]
[rn/view
{:align-items :center
:margin-right 8}
[icon-avatar/icon-avatar
{:size :medium
:icon icon
:color color
:opacity opacity}]])
(defn sm-icon [{:keys [icon color opacity]}]
[rn/view {:align-items :center
:margin-right 8}
[icon-avatar/icon-avatar {:size :medium
:icon icon
:color color
:opacity opacity}]])
(defn sm-user-avatar
[image]
(defn sm-user-avatar [image]
[rn/view {:margin-right 4}
[user-avatar/user-avatar
{:status-indicator? false
:online? false
:size :xxxs
:profile-picture image
:ring? false}]])
[user-avatar/user-avatar {:status-indicator? false
:online? false
:size :xxxs
:profile-picture image
:ring? false}]])
(defmulti sm-render :type)
(defmethod sm-render :deleted
[{:keys [label timestamp-str labels]}]
[rn/view
{:align-items :center
:justify-content :space-between
:flex 1
:flex-direction :row}
[rn/view
{:align-items :center
:flex-direction :row}
[sm-icon
{:icon :main-icons/delete
:color :danger
:opacity 5}]
[text/text
{:size :paragraph-2
:style {:color (get-color :text)
:margin-right 5}}
(defmethod sm-render :deleted [{:keys [label timestamp-str labels]}]
[rn/view {:align-items :center
:justify-content :space-between
:flex 1
:flex-direction :row}
[rn/view {:align-items :center
:flex-direction :row}
[sm-icon {:icon :main-icons/delete
:color :danger
:opacity 5}]
[text/text {:size :paragraph-2
:style {:color (get-color :text)
:margin-right 5}}
(or (get labels label) label (:message-deleted labels))]
[sm-timestamp timestamp-str]]])
(defmethod sm-render :added
[{:keys [state mentions timestamp-str labels]}]
[rn/view
{:align-items :center
:flex-direction :row}
[sm-icon
{:icon :main-icons/add-user
:color :primary
:opacity (if (= state :landed) 0 5)}]
(defmethod sm-render :added [{:keys [state mentions timestamp-str labels]}]
[rn/view {:align-items :center
:flex-direction :row}
[sm-icon {:icon :main-icons/add-user
:color :primary
:opacity (if (= state :landed) 0 5)}]
[sm-user-avatar (:image (first mentions))]
[text/text
{:weight :semi-bold
:size :paragraph-2}
[text/text {:weight :semi-bold
:size :paragraph-2}
(:name (first mentions))]
[text/text
{:size :paragraph-2
:style {:color (get-color :text)
:margin-left 3
:margin-right 3}}
[text/text {:size :paragraph-2
:style {:color (get-color :text)
:margin-left 3
:margin-right 3}}
(:added labels)]
[sm-user-avatar (:image (second mentions))]
[text/text
{:weight :semi-bold
:size :paragraph-2}
[text/text {:weight :semi-bold
:size :paragraph-2}
(:name (second mentions))]
[sm-timestamp timestamp-str]])
(defmethod sm-render :pinned
[{:keys [state pinned-by content timestamp-str labels]}]
[rn/view
{:flex-direction :row
:flex 1
:align-items :center}
[sm-icon
{:icon :main-icons/pin
:color :primary
:opacity (if (= state :landed) 0 5)}]
[rn/view
{:flex-direction :column
:flex 1}
[rn/view
{:align-items :baseline
:flex-direction :row}
[text/text
{:size :paragraph-2
:weight :semi-bold
:style {:color (get-color :text)}}
(defmethod sm-render :pinned [{:keys [state pinned-by content timestamp-str labels]}]
[rn/view {:flex-direction :row
:flex 1
:align-items :center}
[sm-icon {:icon :main-icons/pin
:color :primary
:opacity (if (= state :landed) 0 5)}]
[rn/view {:flex-direction :column
:flex 1}
[rn/view {:align-items :baseline
:flex-direction :row}
[text/text {:size :paragraph-2
:weight :semi-bold
:style {:color (get-color :text)}}
(utils/truncate-str pinned-by 18)]
[rn/view
{:margin-left 4
:margin-right 2}
[text/text
{:size :paragraph-2
:style {:color (get-color :text)}}
[rn/view {:margin-left 4
:margin-right 2}
[text/text {:size :paragraph-2
:style {:color (get-color :text)}}
(:pinned-a-message labels)]]
[sm-timestamp timestamp-str]]
[rn/view {:flex-direction :row}
[rn/view
{:flex-direction :row
:margin-right 4}
[rn/view {:flex-direction :row
:margin-right 4}
[sm-user-avatar (:image (:mentions content))]
[text/text
{:weight :semi-bold
:size :label}
[text/text {:weight :semi-bold
:size :label}
(:name (:mentions content))]]
(when (seq (:text content))
[rn/view
{:margin-right 20
:flex-direction :row
:flex 1}
[text/text
{:size :label
:style {:color (get-color :text)}
:number-of-lines 1
:ellipsize-mode :tail}
[rn/view {:margin-right 20
:flex-direction :row
:flex 1}
[text/text {:size :label
:style {:color (get-color :text)}
:number-of-lines 1
:ellipsize-mode :tail}
(:text content)]])
[rn/view
{:justify-content :flex-end
:flex-direction :row
:min-width 10}
[rn/view {:justify-content :flex-end
:flex-direction :row
:min-width 10}
(when (seq (:info content))
[text/text
{:size :label
:style {:color (get-color :time)}}
[text/text {:size :label
:style {:color (get-color :time)}}
(utils/truncate-str (:info content) 24)])]]]])
(defn system-message
@@ -178,16 +143,11 @@
(get-color :bg (if animate-landing? :landed :default) type))]
(when animate-landing?
(reanimated/animate-shared-value-with-delay
sv-color
(get-color :bg :default type)
0
:linear
1000))
sv-color (get-color :bg :default type) 0 :linear 1000))
[reanimated/touchable-opacity
{:on-press #(when-not non-pressable?
(reanimated/set-shared-value
sv-color
(get-color :bg :pressed type)))
sv-color (get-color :bg :pressed type)))
:style (reanimated/apply-animations-to-style
{:background-color sv-color}
(merge
@@ -1,13 +1,12 @@
(ns quo2.components.navigation.bottom-nav-tab
(:require [quo2.components.counter.counter :as counter]
[quo2.components.icons.icons :as icons]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
(:require [react-native.core :as rn]
[react-native.reanimated :as reanimated]
[react-native.hole-view :as hole-view]
[react-native.reanimated :as reanimated]))
[quo2.foundations.colors :as colors]
[quo2.components.icons.icons :as icons]
[quo2.components.counter.counter :as counter]))
(defn toggle-background-color
[background-color press-out? pass-through?]
(defn toggle-background-color [background-color press-out? pass-through?]
(reanimated/set-shared-value
background-color
(cond
@@ -37,47 +36,44 @@
background-color (reanimated/use-shared-value "transparent")
background-animated-style (reanimated/apply-animations-to-style
{:background-color background-color}
{:width 90
:height 40
:border-radius 10})]
{:width 90
:height 40
:border-radius 10})]
[rn/touchable-without-feedback
{:test-ID test-ID
{:test-ID test-ID
:on-press on-press
:on-press-in #(toggle-background-color background-color false pass-through?)
:on-press-out #(toggle-background-color background-color true pass-through?)
:accessibility-label accessibility-label}
[reanimated/view {:style background-animated-style}
[hole-view/hole-view
{:style {:padding-left 33
:padding-top 8}
:key new-notifications? ;; Key is required to force removal of holes
:holes (cond
(not new-notifications?) ;; No new notifications, remove holes
[]
[hole-view/hole-view {:style {:padding-left 33
:padding-top 8}
:key new-notifications? ;; Key is required to force removal of holes
:holes (cond
(not new-notifications?) ;; No new notifications, remove holes
[]
(= notification-indicator :unread-dot)
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
(= notification-indicator :unread-dot)
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
:else
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
:else
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
[reanimated/image
{:style icon-animated-style
{:style icon-animated-style
:source (icons/icon-source (keyword (str icon 24)))}]]
(when new-notifications?
(if (= notification-indicator :counter)
[counter/counter
{:outline false
:override-text-color colors/white
:override-bg-color colors/primary-50
:style {:position :absolute
:left 48
:top 2}}
[counter/counter {:outline false
:override-text-color colors/white
:override-bg-color colors/primary-50
:style {:position :absolute
:left 48
:top 2}}
counter-label]
[rn/view
{:style {:width 8
:height 8
:border-radius 4
:top 6
:left 51
:position :absolute
:background-color colors/primary-50}}]))]]))])
[rn/view {:style {:width 8
:height 8
:border-radius 4
:top 6
:left 51
:position :absolute
:background-color colors/primary-50}}]))]]))])
@@ -1,10 +1,9 @@
(ns quo2.components.navigation.floating-shell-button
(:require [quo2.components.buttons.dynamic-button :as dynamic-button]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]))
(:require [react-native.core :as rn]
[react-native.reanimated :as reanimated]
[quo2.components.buttons.dynamic-button :as dynamic-button]))
(defn dynamic-button-view
[type dynamic-buttons style]
(defn dynamic-button-view [type dynamic-buttons style]
(when-let [{:keys [count on-press customization-color label]} (get dynamic-buttons type)]
[dynamic-button/dynamic-button
{:type type
@@ -26,27 +25,23 @@
(fn []
(let [original-style (merge {:flex-direction :row
:margin-horizontal 12
:pointer-events :box-none}
style)
:pointer-events :box-none} style)
animated-style (reanimated/apply-animations-to-style
(if opacity-anim
{:opacity opacity-anim}
{})
{:opacity opacity-anim} {})
original-style)]
[reanimated/view {:style animated-style}
;; Left Section
[rn/view {:style {:flex 1}}
[dynamic-button-view :search dynamic-buttons
{:position :absolute
:right 8}]]
[dynamic-button-view :search dynamic-buttons {:position :absolute
:right 8}]]
;; Mid Section (jump-to)
[dynamic-button-view :jump-to dynamic-buttons nil]
;; Right Section
;; Right Section
[rn/view {:style {:flex 1}}
[rn/view
{:style {:position :absolute
:flex-direction :row
:right 0}}
[rn/view {:style {:position :absolute
:flex-direction :row
:right 0}}
[dynamic-button-view :mention dynamic-buttons {:margin-left 8}]
[dynamic-button-view :notification-down dynamic-buttons {:margin-left 8}]
[dynamic-button-view :notification-up dynamic-buttons {:margin-left 8}]
+100 -120
View File
@@ -1,11 +1,11 @@
(ns quo2.components.navigation.page-nav
(:require [clojure.string :as string]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.buttons.button :as button]
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
[clojure.string :as string]
[quo2.components.buttons.button :as button]
[quo2.components.avatars.user-avatar :as user-avatar]))
(def ^:private centrify-style
{:display :flex
@@ -16,8 +16,7 @@
(defn- big? [size] (= size :big))
(defn- icon-props
[color size]
(defn- icon-props [color size]
(merge {:size 20
:container-style {:width (if (big? size)
20
@@ -29,25 +28,22 @@
{:color color}
{:no-color true})))
(defn left-section-view
[{:keys [on-press icon accessibility-label type] :or {type :grey}}
put-middle-section-on-left?]
(defn left-section-view [{:keys [on-press icon accessibility-label type] :or {type :grey}}
put-middle-section-on-left?]
[rn/view {:style (when put-middle-section-on-left? {:margin-right 5})}
[button/button
{:on-press on-press
:icon true
:type type
:size 32
:accessibility-label accessibility-label}
[button/button {:on-press on-press
:icon true
:type type
:size 32
:accessibility-label accessibility-label}
icon]])
(defn- mid-section-comp
[{:keys [description-img description-user-icon horizontal-description?
text-secondary-color align-mid? text-color icon main-text type description]}]
[rn/view
{:style (assoc centrify-style
:flex-direction :row
:margin-horizontal 2)}
[rn/view {:style (assoc centrify-style
:flex-direction :row
:margin-horizontal 2)}
(when (or (and (not horizontal-description?)
align-mid?
(not= :text-with-description type))
@@ -56,98 +52,85 @@
(if description-img
[rn/view {:margin-right 8}
[description-img]]
[rn/image
{:source {:uri description-user-icon}
:style {:width 32
:height 32
:border-radius 32
:margin-right 8}}]))
[rn/view
{:style {:flex-direction (if horizontal-description?
:row
:column)}}
[text/text
{:size :paragraph-1
:weight :semi-bold
:style {:color text-color
:line-height 21}}
[rn/image {:source {:uri description-user-icon}
:style {:width 32
:height 32
:border-radius 32
:margin-right 8}}]))
[rn/view {:style {:flex-direction (if horizontal-description?
:row
:column)}}
[text/text {:size :paragraph-1
:weight :semi-bold
:style {:color text-color
:line-height 21}}
main-text]
(when description
[text/text
{:size :paragraph-2
:weight :medium
:style (cond-> {:padding-right 4
:color text-secondary-color
:line-height 18}
horizontal-description? (assoc :margin-left 4 :margin-top 2))}
[text/text {:size :paragraph-2
:weight :medium
:style (cond-> {:padding-right 4
:color text-secondary-color
:line-height 18}
horizontal-description? (assoc :margin-left 4 :margin-top 2))}
description])]])
(defn- mid-section-view
[{:keys [horizontal-description? one-icon-align-left? type left-align?
main-text right-icon main-text-icon-color left-icon on-press avatar]
:as props}]
(let [text-color (if (colors/dark?) colors/neutral-5 colors/neutral-95)
main-text right-icon main-text-icon-color left-icon on-press avatar] :as props}]
(let [text-color (if (colors/dark?) colors/neutral-5 colors/neutral-95)
text-secondary-color (if (colors/dark?) colors/neutral-40 colors/neutral-50)
component-instance [mid-section-comp (assoc props :text-secondary-color text-secondary-color)]]
component-instance [mid-section-comp (assoc props :text-secondary-color text-secondary-color)]]
[rn/touchable-opacity {:on-press on-press}
[rn/view
{:style (merge
(if left-align?
align-left
centrify-style)
{:flex 1
:margin-left 4
:text-align-vertical :center})}
[rn/view {:style (merge
(if left-align?
align-left
centrify-style)
{:flex 1
:margin-left 4
:text-align-vertical :center})}
(case type
:text-only [text/text
{:size :paragraph-1
:weight :semi-bold
:style {:color text-color}}
main-text]
:user-avatar [rn/view {:style (assoc centrify-style :flex-direction :row)}
[user-avatar/user-avatar avatar]
[text/text
{:size :paragraph-1
:text-only [text/text {:size :paragraph-1
:weight :semi-bold
:style {:color text-color}}
main-text]
:user-avatar [rn/view {:style (assoc centrify-style :flex-direction :row)}
[user-avatar/user-avatar avatar]
[text/text {:size :paragraph-1
:weight :semi-bold
:style {:padding-horizontal 4
:color text-color}}
main-text]]
:text-with-two-icons [rn/view {:style (assoc centrify-style :flex-direction :row)}
[icons/icon left-icon
(icon-props main-text-icon-color :big)]
[text/text
{:size :paragraph-1
:weight :semi-bold
:style {:padding-horizontal 4
:color text-color}}
main-text]
[icons/icon right-icon
(icon-props main-text-icon-color :big)]]
:text-with-one-icon [rn/view {:style {:flex-direction :row}}
(if one-icon-align-left?
[rn/view
{:style {:flex-direction :row
:align-items :center}}
(when horizontal-description?
[icons/icon left-icon
(icon-props main-text-icon-color :big)])
component-instance]
[rn/view
{:style {:flex-direction :row
:align-items :center}}
component-instance
(when horizontal-description?
[icons/icon left-icon
(icon-props main-text-icon-color :big)])])]
main-text]]
:text-with-two-icons [rn/view {:style (assoc centrify-style :flex-direction :row)}
[icons/icon left-icon
(icon-props main-text-icon-color :big)]
[text/text {:size :paragraph-1
:weight :semi-bold
:style {:padding-horizontal 4
:color text-color}}
main-text]
[icons/icon right-icon
(icon-props main-text-icon-color :big)]]
:text-with-one-icon [rn/view {:style {:flex-direction :row}}
(if one-icon-align-left?
[rn/view {:style {:flex-direction :row
:align-items :center}}
(when horizontal-description?
[icons/icon left-icon
(icon-props main-text-icon-color :big)])
component-instance]
[rn/view {:style {:flex-direction :row
:align-items :center}}
component-instance
(when horizontal-description?
[icons/icon left-icon
(icon-props main-text-icon-color :big)])])]
:text-with-description component-instance)]]))
(defn- right-section-view
[right-section-buttons]
[rn/view
{:style (assoc centrify-style
:flex-direction :row
:flex 1
:justify-content :flex-end)}
(defn- right-section-view [right-section-buttons]
[rn/view {:style (assoc centrify-style
:flex-direction :row
:flex 1
:justify-content :flex-end)}
(let [last-icon-index (-> right-section-buttons count dec)]
(map-indexed (fn [index {:keys [icon on-press type] :or {type :grey}}]
^{:key index}
@@ -211,30 +194,27 @@
:icon (:icon mid-section)
:left-icon (:left-icon mid-section)
:avatar (:avatar mid-section)}]
[rn/view
{:style (cond-> {:display :flex
:flex-direction :row
;; iPhone 11 Pro's height in Figma divided by Component height 56/1125
:align-items :center
:padding-horizontal 20
:height 56
:justify-content :space-between}
page-nav-background-uri (assoc :background-color page-nav-color)
page-nav-color (assoc :background page-nav-background-uri))}
[rn/view
{:style {:flex 1
:flex-direction :row
:align-items :center}}
[rn/view {:style (cond-> {:display :flex
:flex-direction :row
;; iPhone 11 Pro's height in Figma divided by Component height 56/1125
:align-items :center
:padding-horizontal 20
:height 56
:justify-content :space-between}
page-nav-background-uri (assoc :background-color page-nav-color)
page-nav-color (assoc :background page-nav-background-uri))}
[rn/view {:style {:flex 1
:flex-direction :row
:align-items :center}}
[left-section-view left-section put-middle-section-on-left?]
(when put-middle-section-on-left?
[mid-section-view
(assoc mid-section-props
:left-align? true
:description (:description mid-section)
:description-color (:description-color mid-section)
:description-icon (:description-icon mid-section)
:align-mid? align-mid?
:description-user-icon (:description-user-icon mid-section))])]
[mid-section-view (assoc mid-section-props
:left-align? true
:description (:description mid-section)
:description-color (:description-color mid-section)
:description-icon (:description-icon mid-section)
:align-mid? align-mid?
:description-user-icon (:description-user-icon mid-section))])]
(when-not put-middle-section-on-left?
[mid-section-view mid-section-props])
[right-section-view right-section-buttons]]))
@@ -43,8 +43,7 @@
:align-items :flex-start
:flex 1})
(defn title
[replying?]
(defn title [replying?]
{:color colors/white
:flex-shrink 1
:max-width (when-not replying? "60%")})
@@ -4,9 +4,9 @@
[quo2.components.buttons.button :as button]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.components.notifications.activity-log.style :as style]
[quo2.components.tags.status-tags :as status-tags]
[quo2.foundations.colors :as colors]
[quo2.components.notifications.activity-log.style :as style]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.i18n.i18n :as i18n]))
@@ -21,20 +21,17 @@
(defn- activity-reply-text-input
[reply-input on-update-reply]
[rn/view
[rn/view
{:style {:margin-top 16
:margin-bottom 8
:flex-direction :row}}
[text/text
{:weight :medium
:style {:flex-grow 1
:color colors/neutral-40}}
[rn/view {:style {:margin-top 16
:margin-bottom 8
:flex-direction :row}}
[text/text {:weight :medium
:style {:flex-grow 1
:color colors/neutral-40}}
(i18n/label :t/your-answer)]
[text/text
{:style {:flex-shrink 1
:color (if (valid-reply? @reply-input)
colors/neutral-40
colors/danger-60)}}
[text/text {:style {:flex-shrink 1
:color (if (valid-reply? @reply-input)
colors/neutral-40
colors/danger-60)}}
(str (count @reply-input) "/" max-reply-length)]]
[rn/view
;; TODO(@ilmotta): Replace with quo2 component when available.
@@ -61,39 +58,33 @@
(let [first-line-offset (if replying? 4 0)
gap-between-lines 4]
(into [rn/view {:style (assoc style/context-container :margin-top first-line-offset)}]
(mapcat
(fn [detail]
^{:key (hash detail)}
(if (string? detail)
(map (fn [s]
[rn/view
{:style {:margin-right 4
:margin-top 0}}
[text/text
{:size :paragraph-2
:style {:color colors/white}}
s]])
(string/split detail #"\s+"))
[[rn/view
{:margin-right 4
:margin-top gap-between-lines}
detail]]))
context))))
(mapcat (fn [detail]
^{:key (hash detail)}
(if (string? detail)
(map (fn [s]
[rn/view {:style {:margin-right 4
:margin-top 0}}
[text/text {:size :paragraph-2
:style {:color colors/white}}
s]])
(string/split detail #"\s+"))
[[rn/view {:margin-right 4
:margin-top gap-between-lines}
detail]]))
context))))
(defn- activity-message
[{:keys [title body]}]
[rn/view {:style style/message-container}
(when title
[text/text
{:size :paragraph-2
:accessibility-label :activity-message-title
:style style/message-title}
[text/text {:size :paragraph-2
:accessibility-label :activity-message-title
:style style/message-title}
title])
(if (string? body)
[text/text
{:style style/message-body
:accessibility-label :activity-message-body
:size :paragraph-1}
[text/text {:style style/message-body
:accessibility-label :activity-message-body
:size :paragraph-1}
body]
body)])
@@ -106,51 +97,44 @@
:flex-basis 0})]
[rn/view style/buttons-container
(when button-1
[button/button
(-> button-1
(assoc :size size)
(update :style merge common-style {:margin-right 8}))
[button/button (-> button-1
(assoc :size size)
(update :style merge common-style {:margin-right 8}))
(:label button-1)])
(when button-2
[button/button
(-> button-2
(assoc :size size)
(assoc :disabled (and replying? (not (valid-reply? @reply-input))))
(update :style merge common-style))
[button/button (-> button-2
(assoc :size size)
(assoc :disabled (and replying? (not (valid-reply? @reply-input))))
(update :style merge common-style))
(:label button-2)])]))
(defn- activity-status
[status]
[rn/view
{:style style/status
:accessibility-label :activity-status}
[status-tags/status-tag
{:size :small
:label (:label status)
:status status}]])
[rn/view {:style style/status
:accessibility-label :activity-status}
[status-tags/status-tag {:size :small
:label (:label status)
:status status}]])
(defn- activity-title
[title replying?]
[text/text
{:weight :semi-bold
:accessibility-label :activity-title
:style (style/title replying?)
:size (if replying? :heading-2 :paragraph-1)}
[text/text {:weight :semi-bold
:accessibility-label :activity-title
:style (style/title replying?)
:size (if replying? :heading-2 :paragraph-1)}
title])
(defn- activity-timestamp
[timestamp]
[text/text
{:size :label
:accessibility-label :activity-timestamp
:style style/timestamp}
[text/text {:size :label
:accessibility-label :activity-timestamp
:style style/timestamp}
timestamp])
(defn- activity-unread-dot
[]
[rn/view
{:accessibility-label :activity-unread-indicator
:style style/unread-dot-container}
[rn/view {:accessibility-label :activity-unread-indicator
:style style/unread-dot-container}
[rn/view {:style style/unread-dot}]])
(defn- footer
@@ -175,14 +159,12 @@
replying?
unread?]
:as props}]
[rn/view
{:accessibility-label :activity
:style style/container}
[rn/view {:accessibility-label :activity
:style style/container}
(when-not replying?
[activity-icon icon])
[rn/view
{:style {:padding-left (when-not replying? 8)
:flex 1}}
[rn/view {:style {:padding-left (when-not replying? 8)
:flex 1}}
[rn/view
[rn/view {:style style/top-section-container}
[activity-title title replying?]
@@ -0,0 +1,106 @@
(ns quo2.components.notifications.count-down-circle
(:require
[goog.string :as gstring]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]
[react-native.svg :as svg]
[reagent.core :as reagent]))
(defn- get-path-props
[size stroke-width rotation]
(let [half-size (/ size 2)
half-stroke-width (/ stroke-width 2)
arc-radius (- half-size half-stroke-width)
arc-diameter (* arc-radius 2)
rotation-indicator (if (= rotation :clockwise) "1,0" "0,1")
path-length (* js/Math.PI arc-diameter)
path (gstring/format
"m %s,%s a %s,%s 0 %s 0,%s a %s,%s 0 %s 0 ,-%s"
half-size
half-stroke-width
arc-radius
arc-radius
rotation-indicator
arc-diameter
arc-radius
arc-radius
rotation-indicator
arc-diameter)]
{:path path
:path-length path-length}))
(defn- linear-ease
[time start goal duration]
(if (zero? duration)
start
(-> time
(/ duration)
(* goal)
(+ start))))
(defn- get-start-at
[duration initial-remaining-time]
(cond (or (zero? duration) (= duration initial-remaining-time)) 0
(number? initial-remaining-time) (- duration initial-remaining-time)
:else 0))
(def ^:private themes
{:color {:light colors/neutral-80-opa-40
:dark colors/white-opa-40}})
(defn circle-timer
[{:keys [color duration size stroke-width trail-color rotation initial-remaining-time]}]
(let [rotation (or rotation :clockwise)
duration (or duration 4)
stroke-width (or stroke-width 1)
size (or size 9)
max-stroke-width (max stroke-width 0)
{:keys [path path-length]} (get-path-props size max-stroke-width rotation)
start-at (get-start-at duration initial-remaining-time)
elapsed-time (reagent/atom 0)
prev-frame-time (reagent/atom nil)
frame-request (reagent/atom nil)
display-time (reagent/atom start-at)
;; get elapsed frame time
swap-elapsed-time-each-frame (fn swap-elapsed-time-each-frame [frame-time]
(if (nil? @prev-frame-time)
(do (reset! prev-frame-time frame-time)
(reset! frame-request (js/requestAnimationFrame
swap-elapsed-time-each-frame)))
(let [delta (- (/ frame-time 1000)
(/ @prev-frame-time 1000))
current-elapsed (swap! elapsed-time + delta)
current-display-time (+ start-at current-elapsed)
completed? (>= current-display-time duration)]
(reset! display-time (if completed?
duration
current-display-time))
(when-not completed?
(reset! prev-frame-time frame-time)
(reset! frame-request (js/requestAnimationFrame
swap-elapsed-time-each-frame))))))]
(reagent/create-class
{:component-will-unmount #(js/cancelAnimationFrame @frame-request)
:reagent-render
(fn []
(reset! frame-request (js/requestAnimationFrame swap-elapsed-time-each-frame))
[rn/view
{:style {:position :relative
:width size
:height size}}
[svg/svg
{:view-box (str "0 0 " size " " size)
:width size
:height size}
[svg/path
{:d path :fill :none :stroke (or trail-color :transparent) :stroke-width stroke-width}]
(when-not (= @display-time duration)
[svg/path
{:d path
:fill :none
:stroke (or color (get-in themes [:color (theme/get-theme)]))
:stroke-linecap :square
:stroke-width stroke-width
:stroke-dasharray path-length
:stroke-dashoffset (linear-ease @display-time 0 path-length duration)}])]])})))

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