Compare commits

..
Author SHA1 Message Date
Ajay Sivan a0540d2396 show enable biometric screen in sync flow 2023-05-02 16:01:43 +05:30
119 changed files with 2917 additions and 2525 deletions
@@ -72,7 +72,7 @@ public class MainApplication extends NavigationApplication {
OkHttpClientProvider.setOkHttpClientFactory(new StatusOkHttpClientFactory());
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG_WEBVIEW == "1");
//initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
+467 -62
View File
@@ -1859,6 +1859,21 @@
}
},
{
"path": "com/android/databinding/baseLibrary/3.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"baseLibrary-3.1.4.pom": {
"sha1": "b48baeaef73d7763fa92b847c4140f0e0ff71d99",
"sha256": "16c6wpphs4b8373w7jdiynv48lgl8sj8sy5gp16cf3qgqgmai4qv"
},
"baseLibrary-3.1.4.jar": {
"sha1": "df1e7638ba852152abe964e86f60fa4ce12e6a6a",
"sha256": "0cn50bas33hndr7snlgwpj4vgr66dazkg7ybb04hgc0czdgl9adb"
}
}
},
{
"path": "com/android/databinding/baseLibrary/3.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -1949,6 +1964,21 @@
}
},
{
"path": "com/android/databinding/compilerCommon/3.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"compilerCommon-3.1.4.pom": {
"sha1": "0fd23d31fe5e40ac6d203734dcc6b16328ba714c",
"sha256": "0sb36alvbmxv05amnc02csm73cg6aib8s16fipjlgqri58cn5yd9"
},
"compilerCommon-3.1.4.jar": {
"sha1": "11005423fee93309c0cd512a8783647702c20c27",
"sha256": "0kxd2js2yk7bisiiy1j5i5zgd9h124qac10dan68y8swiddd4hlb"
}
}
},
{
"path": "com/android/signflinger/4.1.0",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -2354,6 +2384,21 @@
}
},
{
"path": "com/android/tools/analytics-library/protos/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"protos-26.1.4.pom": {
"sha1": "0c9e7fe6c7c2b0156a87aa626f5463af34669f99",
"sha256": "11wnpnlb0ixck525g1qi6lgrjcsca9281kpmwkm3m3r8y1fnl86c"
},
"protos-26.1.4.jar": {
"sha1": "dc5876bb894d87d2088b0a4447b13fdc13ad82fc",
"sha256": "1dy9pd8sjkhhip8xqz681i9q66m43iigmnwhdkmk6xm33j0d6cf7"
}
}
},
{
"path": "com/android/tools/analytics-library/protos/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -2444,6 +2489,21 @@
}
},
{
"path": "com/android/tools/analytics-library/shared/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"shared-26.1.4.pom": {
"sha1": "18ef89a0e8844559433bf6c085b4b86faecb87b6",
"sha256": "1m4g32q62mjwmg0z23fvzf40gaf4dgm0jb5cbcv4j36jpgp0yrmm"
},
"shared-26.1.4.jar": {
"sha1": "02a285c67f4b421145e47849d4114f7534707156",
"sha256": "08h7w0fwky1mr5j7v4yb4afa3kvvw56bnj4926m4zi62p1qfdxsn"
}
}
},
{
"path": "com/android/tools/analytics-library/shared/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -2534,6 +2594,21 @@
}
},
{
"path": "com/android/tools/analytics-library/tracker/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"tracker-26.1.4.pom": {
"sha1": "0949ee22e49c32374aa3227b76efdf6b69a85cb0",
"sha256": "0077291y8qj555nwqg1ssykdg7cjqgf8866hz9y7rw6sj95h9flw"
},
"tracker-26.1.4.jar": {
"sha1": "fd48066d6c78b78abf5baa335dc2124060e8371c",
"sha256": "1yggkx8f5a3w3z924rmc01y06vn4ppz5541s88y6qkd0xbxbdmb6"
}
}
},
{
"path": "com/android/tools/analytics-library/tracker/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -2624,6 +2699,21 @@
}
},
{
"path": "com/android/tools/annotations/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"annotations-26.1.4.pom": {
"sha1": "d5e8ea57c56cd1f5b6e1e676ab0d489d88b59826",
"sha256": "1i6qhh6b5nppvbc465hzjd29ip8j698cs1873yv42dhakc325cyz"
},
"annotations-26.1.4.jar": {
"sha1": "817d5238d9ea07b4b26d04e29e9bada2aacb7245",
"sha256": "102r2z2wh3pmaar7f0d3nxksk265m5plbd8cjjvbgll9jkw972k0"
}
}
},
{
"path": "com/android/tools/annotations/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -2684,6 +2774,21 @@
}
},
{
"path": "com/android/tools/build/aapt2-proto/0.1.0",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"aapt2-proto-0.1.0.pom": {
"sha1": "d3492c6cfb99d5d556cf4840c2efd63493ce2ac1",
"sha256": "1h9vlambl1k51nx86qm0yfyavpf9qfrh7gja1q3qsqj10jinb8kc"
},
"aapt2-proto-0.1.0.jar": {
"sha1": "d1eb93a21a8d3590c3bfac574a8b6dffb2dbd21c",
"sha256": "1x69zbk977hyc97hpc3bxmh19yiiq3hkl3hadjmwps08z4g13990"
}
}
},
{
"path": "com/android/tools/build/aapt2-proto/0.3.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -2789,6 +2894,21 @@
}
},
{
"path": "com/android/tools/build/apksig/3.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"apksig-3.1.4.pom": {
"sha1": "a0d953c40691088ad3336fca80b5e2cd3fbd05ec",
"sha256": "1jfg6giv1mkg7j72g3w7h5gjvbmpalb03ra7lbdwk350lz76jq23"
},
"apksig-3.1.4.jar": {
"sha1": "e0c4d0059e68e9f64606dfa01e066f86dd07c6cb",
"sha256": "0xa5hzkmfymz5pqnrgw7bx9h13rbfnwy3h7nmmnqcrbrlqvc7bar"
}
}
},
{
"path": "com/android/tools/build/apksig/3.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -2939,6 +3059,21 @@
}
},
{
"path": "com/android/tools/build/builder-model/3.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"builder-model-3.1.4.pom": {
"sha1": "2cd8e9360a4114089d33a0ef98927f14d36c8562",
"sha256": "0in1yb8n7m28ppj91k5hxclq60qc9141lwm4hyllqsjl2qdns5ys"
},
"builder-model-3.1.4.jar": {
"sha1": "f026c31b8228a84b62bb2fe0ac0544143e9ab27d",
"sha256": "0gqa2immwkl4kvg4c233ndxp2bg33ypdbmp6jah5ab0c5azqr3bf"
}
}
},
{
"path": "com/android/tools/build/builder-model/3.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -3029,6 +3164,21 @@
}
},
{
"path": "com/android/tools/build/builder-test-api/3.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"builder-test-api-3.1.4.pom": {
"sha1": "e9843c965bd3ec945ad5e94feebbfe1d47aa5a3b",
"sha256": "0adip5p8lvbhcqxhqrpahws9ia9f9q12dkzchf5kj44vgl0r7mbf"
},
"builder-test-api-3.1.4.jar": {
"sha1": "e48338f64f1c8fbef8329eb6d899fb6378fca604",
"sha256": "1qjgvj3jmwa7gxc910vfa56rijzin33zymqpb4ikfpsvhk7g2kqz"
}
}
},
{
"path": "com/android/tools/build/builder-test-api/3.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -3119,6 +3269,21 @@
}
},
{
"path": "com/android/tools/build/builder/3.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"builder-3.1.4.pom": {
"sha1": "d33573f1d53514989682eaf85d2c2b98f37aeae2",
"sha256": "064y4z05s3r7x74rlb26c8m7aq5pharw5l1yk1ca76h9k0lafslp"
},
"builder-3.1.4.jar": {
"sha1": "afbcd4b7002c61fe898b1b4c50ed9e62386125d8",
"sha256": "02wmahn5ibgnxqrk7nqzwdgc4j80iml01g17lmlghdhdmks3y9zy"
}
}
},
{
"path": "com/android/tools/build/builder/3.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -3179,6 +3344,21 @@
}
},
{
"path": "com/android/tools/build/bundletool/0.1.0-alpha01",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"bundletool-0.1.0-alpha01.pom": {
"sha1": "834b735a36a399e2bb0276842ce97c55f6d6c072",
"sha256": "1271sgv8jxdgsf60chx8yjc0jsfs38xd2jhdmdv8d8h4iwrpcrvc"
},
"bundletool-0.1.0-alpha01.jar": {
"sha1": "f7c303e37818223bd98566fcbea29aa0964c4d06",
"sha256": "05bgc6sh1cz4l8mbw6jyx78pq275szq011z5cgpjdkdbpzq5dnrv"
}
}
},
{
"path": "com/android/tools/build/bundletool/0.5.0",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -3269,6 +3449,21 @@
}
},
{
"path": "com/android/tools/build/gradle-api/3.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"gradle-api-3.1.4.pom": {
"sha1": "e18ab0b7eee6fc63e65158b4f88bf5c7ce207524",
"sha256": "0rjnchkvy8j8k3f12l060p4yg9c784l8j7ngdbphy6jwipvxfz2j"
},
"gradle-api-3.1.4.jar": {
"sha1": "eb41dfb5596afd8933c804595ca8596952fad450",
"sha256": "0qqsqjrgnamvzll97ylzw34f75hcp3cmh65dzksqhxgwcfcp9sni"
}
}
},
{
"path": "com/android/tools/build/gradle-api/3.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -3359,6 +3554,21 @@
}
},
{
"path": "com/android/tools/build/gradle-core/3.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"gradle-core-3.1.4.pom": {
"sha1": "38efeb43831cda5d758c52cd62817c3c53ac4722",
"sha256": "1kiij87ybp97q5m3w81pr9gy94fpf5jm59jp67ia9h7cz9dj0kv1"
},
"gradle-core-3.1.4.jar": {
"sha1": "4c846d065331c2a11ed605619613833a842a7b8d",
"sha256": "1nhx6sjfxfqq63yr59i5j09yvkm383fim93fcfkp6s711ma54pma"
}
}
},
{
"path": "com/android/tools/build/gradle/3.0.0",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -3389,6 +3599,21 @@
}
},
{
"path": "com/android/tools/build/gradle/3.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"gradle-3.1.4.pom": {
"sha1": "920f69e56d872f11136254d329ccc6825ced29a1",
"sha256": "1cs4sqfpwg0fhdhyqdpzq70gkyq9cy9xm11bmvpanfls2wcwyqi5"
},
"gradle-3.1.4.jar": {
"sha1": "08f9a726f69c0c8fa3f447566717a21e6b394ed9",
"sha256": "1dxp32ncbrvi8qd9pij9r0mzcn3l6qm7jr0rk86nq5xi0k06ak0g"
}
}
},
{
"path": "com/android/tools/build/gradle/3.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -3599,6 +3824,21 @@
}
},
{
"path": "com/android/tools/build/manifest-merger/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"manifest-merger-26.1.4.pom": {
"sha1": "8b025ca5a54d8e27600470f841f87a77d0a93f56",
"sha256": "0xxjj1frb9fqyv6d6kmfhxsy7xqjas8nkv7y0hpk1726q0kqi81v"
},
"manifest-merger-26.1.4.jar": {
"sha1": "ddb4dcb3bb44c7053fa583967dfa9030f43f1c01",
"sha256": "1cccgadv0varzj48cpwbk3c0y7hpkl5nxa7v7sds9xd2smkv8q9x"
}
}
},
{
"path": "com/android/tools/build/manifest-merger/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -3689,6 +3929,21 @@
}
},
{
"path": "com/android/tools/common/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"common-26.1.4.pom": {
"sha1": "0ba42432d38a56a205e7a5f39a67701277c6d818",
"sha256": "0xdgznbm17brc3x99p9y4jc082ql0svvxcm36yzlr840iywcl5iy"
},
"common-26.1.4.jar": {
"sha1": "b70b6ef7188fe5a6ad6186f0efb22e536e483b9b",
"sha256": "0irdsr5kppk7mw0wjnfpj8h5lixh2p7v4v1ly8z0np95wd6mfnrx"
}
}
},
{
"path": "com/android/tools/common/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -3779,6 +4034,21 @@
}
},
{
"path": "com/android/tools/ddms/ddmlib/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"ddmlib-26.1.4.pom": {
"sha1": "e18f8f2897b429a9246e9a622608a8b347f73b94",
"sha256": "0bq9rr7zwfssz1m6f9qs39s88chqy3siq3hgbfp7f7jc4hgfi1mr"
},
"ddmlib-26.1.4.jar": {
"sha1": "c11fa14b583412aaa9262461299318207439156d",
"sha256": "0zzzhjjsrry77yksjdvv5k3a1nhccpnwh9k6vfv51i8mh9mi58x3"
}
}
},
{
"path": "com/android/tools/ddms/ddmlib/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -3869,6 +4139,21 @@
}
},
{
"path": "com/android/tools/dvlib/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"dvlib-26.1.4.pom": {
"sha1": "49d0672a46075d55b83d2e59e8a7c4af90c4e9f0",
"sha256": "1xxdi7fb5ixv850zfjd340ww8gx8vkmlx9jm18ib9w5gj77w4w2s"
},
"dvlib-26.1.4.jar": {
"sha1": "5d443b51ad041e9599cfdb278cd2f6c9d2ccdf28",
"sha256": "1kh85j1spdpbd5bwcf2155hjyv7psn3b9qxry7lw0i4cldi862n5"
}
}
},
{
"path": "com/android/tools/dvlib/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -4064,6 +4349,21 @@
}
},
{
"path": "com/android/tools/layoutlib/layoutlib-api/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"layoutlib-api-26.1.4.pom": {
"sha1": "de645341e6b3b77554bd2510f13fd04513d66324",
"sha256": "09229jd3448xaci0xh740v902w1g8xgzrkq1py6949jidzfm814n"
},
"layoutlib-api-26.1.4.jar": {
"sha1": "dad1158a9fc38b711339960b82fd9dcf32009e46",
"sha256": "1a5zqp4y43jlqbdnqjw7sis2f4sqa3iixkcj31fq9s72gknwj4fr"
}
}
},
{
"path": "com/android/tools/layoutlib/layoutlib-api/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -4214,6 +4514,21 @@
}
},
{
"path": "com/android/tools/lint/lint-gradle-api/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"lint-gradle-api-26.1.4.pom": {
"sha1": "8bc04a18da7f891ab4c13a010d422a14f99d8d6d",
"sha256": "0b8z7fsfpba2bhx2m0jaca297jgyasy5zhfsx61s40dcp80j89ff"
},
"lint-gradle-api-26.1.4.jar": {
"sha1": "cbc00782604b7d0ad50e9c50b84b074af79394f0",
"sha256": "0nipwpq92wvzxnn2wzdff69v8ps9rznwcrj0xagwqfwd85hdkil6"
}
}
},
{
"path": "com/android/tools/lint/lint-gradle-api/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -4379,6 +4694,21 @@
}
},
{
"path": "com/android/tools/repository/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"repository-26.1.4.pom": {
"sha1": "1b4c88b0253140647c018cce242b688d2ff98cc3",
"sha256": "1yinybbnmqlkcw43bqpxi6pq5lwr7ppj8xxfwkx264jqni5phipa"
},
"repository-26.1.4.jar": {
"sha1": "349dfc72ae53dedc32a17f5d47440838fe2527f1",
"sha256": "0mskx9a3qm6499415mn0nzir9jvn807pnfwaf8wpymz4gsbfh45l"
}
}
},
{
"path": "com/android/tools/repository/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -4469,6 +4799,21 @@
}
},
{
"path": "com/android/tools/sdklib/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"sdklib-26.1.4.pom": {
"sha1": "0e7ef3a225517e60891160d63467bc3572de7e58",
"sha256": "00bgvi6pq59js519i0xi4zsz3pvvfn1l6dmi50b6k428jhdcljzh"
},
"sdklib-26.1.4.jar": {
"sha1": "7424640f2bd3ca3faccb6f656e29547430cd464a",
"sha256": "02p8gsmqk7azzvvnmjnhpc8n49v83yf3pfia2py89j1h5fy7x2bs"
}
}
},
{
"path": "com/android/tools/sdklib/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -4559,6 +4904,21 @@
}
},
{
"path": "com/android/tools/sdk-common/26.1.4",
"repo": "https://dl.google.com/dl/android/maven2",
"files": {
"sdk-common-26.1.4.pom": {
"sha1": "03b3158f1e0d5eb281d5a6a67435b97dc2d76c21",
"sha256": "0a6jjz7fcbky2w2bv0v5qksppgr51nd7lhawr9lva3wg2gpk36mv"
},
"sdk-common-26.1.4.jar": {
"sha1": "5dbdefb2dc6cb5ba1b4b059bf11c964a830c5755",
"sha256": "1awy7i7q22avjs8lxys3i0k3b6qdi0kzvs03p2pxvwbm7lckv7bq"
}
}
},
{
"path": "com/android/tools/sdk-common/26.2.1",
"repo": "https://dl.google.com/dl/android/maven2",
@@ -7085,6 +7445,21 @@
}
},
{
"path": "com/google/errorprone/error_prone_annotations/2.1.2",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"error_prone_annotations-2.1.2.pom": {
"sha1": "44f5d3b9781c31822991ff9f948d6cbfb8cdf055",
"sha256": "04894gp1inj1cpgp68cjh0rzpv0svfzhv2spak2prvdlp25mh2hq"
},
"error_prone_annotations-2.1.2.jar": {
"sha1": "6dcc08f90f678ac33e5ef78c3c752b6f59e63e0c",
"sha256": "0akq6zj7fd6i0abyz5c0q5aikp7lldrbind4lbvn0q60blxqfg6y"
}
}
},
{
"path": "com/google/errorprone/error_prone_annotations/2.2.0",
"repo": "https://repo.maven.apache.org/maven2",
@@ -7156,6 +7531,17 @@
}
},
{
"path": "com/google/errorprone/error_prone_parent/2.1.2",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"error_prone_parent-2.1.2.pom": {
"sha1": "ae2dd256d508d2ffc9d77fbec0414ee695973a8a",
"sha256": "0hx1cf07n7kwcl2pwiab628fm8fmymgy7bwdkvr3nswgvcfxddn6"
}
}
},
{
"path": "com/google/errorprone/error_prone_parent/2.2.0",
"repo": "https://repo.maven.apache.org/maven2",
@@ -8088,16 +8474,16 @@
},
{
"path": "com/sun/istack/istack-commons-runtime/4.2.0",
"path": "com/sun/istack/istack-commons-runtime/4.1.1",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"istack-commons-runtime-4.2.0.pom": {
"sha1": "e00df2d2db8d5459e86a5ef542269ed56c278c66",
"sha256": "0wx5n7595af1n4a8rf5xcqmf6nwyhck0sq17yy94pr1f7pmrp20v"
"istack-commons-runtime-4.1.1.pom": {
"sha1": "35c136c863b9591c4d5374b76c724e871cbc57aa",
"sha256": "17nvihq7f24xrvlqkzxq23qkb4g5f9sl0pq7wsvspl78gbv4ciq1"
},
"istack-commons-runtime-4.2.0.jar": {
"sha1": "131351109ff27c4aa0958a988bb273e879687232",
"sha256": "1xjzfa470birdrq8myd0b57c580gk7jip735vrs3zybf15x5n0i1"
"istack-commons-runtime-4.1.1.jar": {
"sha1": "9b3769c76235bc283b060da4fae2318c6d53f07e",
"sha256": "1lyw30wh3w1xazp0kzb4j5zvz01fz1rihk2kqkwfcnjxpz2li0by"
}
}
},
@@ -8125,12 +8511,12 @@
},
{
"path": "com/sun/istack/istack-commons/4.2.0",
"path": "com/sun/istack/istack-commons/4.1.1",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"istack-commons-4.2.0.pom": {
"sha1": "ddebb110777b1340dae08ba3779042ea5b45144e",
"sha256": "0z29v0lwwyy5gibffcb91ppjmikvrw90pg76m9l7pidjvcw6cwv6"
"istack-commons-4.1.1.pom": {
"sha1": "645608b459500510bd8d75686cd4b3a9e9ee36a0",
"sha256": "0303sa2xi5ln0ypnwpqizs6zrni8s0dr7wj0d41nkbrg6152fxnf"
}
}
},
@@ -8477,16 +8863,16 @@
},
{
"path": "jakarta/activation/jakarta.activation-api/2.1.2",
"path": "jakarta/activation/jakarta.activation-api/2.1.1",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"jakarta.activation-api-2.1.2.pom": {
"sha1": "233dabc7d6b1784679925cbbfebd2e3dec692591",
"sha256": "1phfr3r5gy10si0k7r750fbkvg1rdhxg62c3xv5mscfri87b189c"
"jakarta.activation-api-2.1.1.pom": {
"sha1": "0c6e1dbee7b7faf7c9c8b7274a8b6279415cac6a",
"sha256": "006awx3sv5lc6ngsnrvgij6m9dhhrxqwy1gkkf3p9agdxzw5jck9"
},
"jakarta.activation-api-2.1.2.jar": {
"sha1": "640c0d5aff45dbff1e1a1bc09673ff3a02b1ba12",
"sha256": "1fwz95v2iygy0bcmvffw4d0zparqkb2ibqm4jp0p0hgbs26mfgzm"
"jakarta.activation-api-2.1.1.jar": {
"sha1": "88c774ab863a21fb2fc4219af95379fafe499a31",
"sha256": "010s9784kcs97whlpyy7pd2dp71m62hh5n41zix5mfrdy7qf7fik"
}
}
},
@@ -10001,16 +10387,16 @@
},
{
"path": "org/checkerframework/checker-qual/3.34.0",
"path": "org/checkerframework/checker-qual/3.32.0",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"checker-qual-3.34.0.pom": {
"sha1": "0e3a886c77f8027c96dcbf9516356a42d5b95dad",
"sha256": "0dg54xq0mjd4q2kxyg0z3kfi4d7bjnb7pf870r38qf7kqj8yjnca"
"checker-qual-3.32.0.pom": {
"sha1": "69d7d01436d892d55b202ccec8a7066f0ac41f98",
"sha256": "1kvxly5fpws3prsar57rmjqq6hcy2a0b5fpx60fgmpkf59jqhnkr"
},
"checker-qual-3.34.0.jar": {
"sha1": "fd620da2920cdb0bfcbcde20e309f75940198ccf",
"sha256": "1p96cdclxdvsxgd2bv8lscwqc1cd2ak6mvg7llb7mgbmwf1kxl2x"
"checker-qual-3.32.0.jar": {
"sha1": "54ebd61f46b58b862c779e034073a93489ab3faf",
"sha256": "1k9wsy7wvh5304aaqb51hlgpfciahg5avzgmnzq8bgx6l1fh4vmn"
}
}
},
@@ -10257,27 +10643,27 @@
},
{
"path": "org/eclipse/angus/angus-activation-project/2.0.1",
"path": "org/eclipse/angus/angus-activation-project/2.0.0",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"angus-activation-project-2.0.1.pom": {
"sha1": "24682c09b455ad140ee080bd86752f0bf1957c55",
"sha256": "1ddiingflsp4wdyaw1az6fm02iacn63imnkg82xnxh8ximcns5y3"
"angus-activation-project-2.0.0.pom": {
"sha1": "6fafee47a73b1ee5dc0f533bcaebc55743bbee84",
"sha256": "17ffihwphiw6vzwxb3gkrxbjcsby640wmadj43rbcfamn6icwrjx"
}
}
},
{
"path": "org/eclipse/angus/angus-activation/2.0.1",
"path": "org/eclipse/angus/angus-activation/2.0.0",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"angus-activation-2.0.1.pom": {
"sha1": "b0b59e5f8caaf5afe94cde78d4e98aa1c0a50505",
"sha256": "1vs0w1m3hqa0piqq144x3d9jl3bywzzdl0lg34dfl9gmyw7ypib7"
"angus-activation-2.0.0.pom": {
"sha1": "87500cdbfdcb4758ece7ca2267469a590e2d9fb9",
"sha256": "1sb92gyb9fk3fqysrsr021wdan191i6wgq845gkf4lnk4rbgcqsr"
},
"angus-activation-2.0.1.jar": {
"sha1": "eaafaf4eb71b400e4136fc3a286f50e34a68ecb7",
"sha256": "0cf1nhrkjqz0nihp0l484y627asl5mnixmy1cj4xz3l62lc7c9mj"
"angus-activation-2.0.0.jar": {
"sha1": "72369f4e2314d38de2dcbb277141ef0226f73151",
"sha256": "0iwi0y1ksir12z47gc0q7y7bfxnylf9yxdprix2sjnpkl0hx64is"
}
}
},
@@ -10555,12 +10941,12 @@
},
{
"path": "org/jacoco/org.jacoco.build/0.8.10",
"path": "org/jacoco/org.jacoco.build/0.8.8",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"org.jacoco.build-0.8.10.pom": {
"sha1": "a129ae2c1a5138a8bdfcfaecde3d1c74cb6d4f23",
"sha256": "0fvcpmk09b92985xsry4pdz2ii8jja0h68iw759i9apbipcv1mz3"
"org.jacoco.build-0.8.8.pom": {
"sha1": "b1ec2ce6fa11aa065c5c8cea04be7fa745c9fcbc",
"sha256": "1vrarvwmzb8gg8k7fmpxx3fh8n9ik1r8ahigfxngq97xhl90pkpl"
}
}
},
@@ -10581,16 +10967,16 @@
},
{
"path": "org/jacoco/org.jacoco.core/0.8.10",
"path": "org/jacoco/org.jacoco.core/0.8.8",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"org.jacoco.core-0.8.10.pom": {
"sha1": "e4acd57d7b22eab888cf7a5bac94a2798f6a23cb",
"sha256": "12gaf5yl7p4328hvncsgph4ni40lg60k9gplca7b7vww2kayv84k"
"org.jacoco.core-0.8.8.pom": {
"sha1": "887670d63e8ac0302fde83422824b5e39736e262",
"sha256": "09g2zsc854m5qxy2byqfh20rpk384c1jcl531qybh8zqinjbbypm"
},
"org.jacoco.core-0.8.10.jar": {
"sha1": "669a338279c3f40b154a64c624bab625664a00e6",
"sha256": "020fv0bdhl459nr9jqjl7gdcq6wspkxy92lpxqw3jl57jdynd2g7"
"org.jacoco.core-0.8.8.jar": {
"sha1": "fb1257ce77ec2fe326aad639b2253e76f481ea2e",
"sha256": "0hqnc5jh8ga60d44hxp59f80ycwxnyng1nyz2d3r524xh0pphk27"
}
}
},
@@ -11795,6 +12181,36 @@
}
},
{
"path": "org/jetbrains/kotlin/kotlin-stdlib-jre7/1.2.0",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"kotlin-stdlib-jre7-1.2.0.pom": {
"sha1": "52d689e2aecf9c5ee2ca40c9512069ea77b4eb06",
"sha256": "0j3ka2lfxqxb37dmx4nzm07q88a77yf5wf1mrqs7gayk49y42xr0"
},
"kotlin-stdlib-jre7-1.2.0.jar": {
"sha1": "ec8b969e26fbcf2265a4d1a1539c4d1d4c5af380",
"sha256": "09i91z5r1ynll65sa6v61hqkz875q7vaz049zrx7jdyla6whz8n7"
}
}
},
{
"path": "org/jetbrains/kotlin/kotlin-stdlib-jre8/1.2.0",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"kotlin-stdlib-jre8-1.2.0.pom": {
"sha1": "dd37e31bef41f48317214e93134ff54372bad406",
"sha256": "0f5c4jjw8rs5jgyjb2d9j6h1nf5nqcj6aq61s7p5cw6546z78ykb"
},
"kotlin-stdlib-jre8-1.2.0.jar": {
"sha1": "505f55b9619bbc5f5e26c77427dd24a6a441eef1",
"sha256": "0an8j2wwpx0mapmpl179xqf242kvjbipxaqxrgvl26ggwvp28db3"
}
}
},
{
"path": "org/jetbrains/kotlin/kotlin-stdlib/1.1.3-2",
"repo": "https://repo.maven.apache.org/maven2",
@@ -12156,12 +12572,12 @@
},
{
"path": "org/junit/junit-bom/5.9.3",
"path": "org/junit/junit-bom/5.9.2",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"junit-bom-5.9.3.pom": {
"sha1": "b1874b6a66656e4f5e4b492ab321249bcb749dc7",
"sha256": "1mrf6kf0y6bic98n30w8s9gnxykgzv5j8mx1bh745wkjkv6jj0sd"
"junit-bom-5.9.2.pom": {
"sha1": "645a08cbe455cad14d8bfb25a35d7f594c53cafd",
"sha256": "01gp6mpz7c4vcyrmm0drb05rvdan82d6qiw6d8rzacaihijpvl1f"
}
}
},
@@ -12297,17 +12713,6 @@
}
},
{
"path": "org/ow2/asm/asm-bom/9.5",
"repo": "https://repo.maven.apache.org/maven2",
"files": {
"asm-bom-9.5.pom": {
"sha1": "2b5d6f54150ef4cbef69c3b09b281353f42e1366",
"sha256": "1grm2s0pqc0s2kl79mhbv3l5zcwbgq3z803gh0wbczc3vlsnnxbi"
}
}
},
{
"path": "org/ow2/asm/asm-commons/5.1",
"repo": "https://repo.maven.apache.org/maven2",
+29
View File
@@ -118,6 +118,7 @@ com.airbnb.android:lottie:3.4.4
com.android.databinding:baseLibrary:1.0-rc5
com.android.databinding:baseLibrary:3.0.0
com.android.databinding:baseLibrary:3.0.1
com.android.databinding:baseLibrary:3.1.4
com.android.databinding:baseLibrary:3.2.1
com.android.databinding:baseLibrary:3.3.1
com.android.databinding:baseLibrary:3.5.4
@@ -125,24 +126,28 @@ com.android.databinding:baseLibrary:4.1.0
com.android.databinding:compilerCommon:1.0-rc5
com.android.databinding:compilerCommon:3.0.0
com.android.databinding:compilerCommon:3.0.1
com.android.databinding:compilerCommon:3.1.4
com.android.tools.analytics-library:crash:26.2.1
com.android.tools.analytics-library:crash:26.3.1
com.android.tools.analytics-library:crash:26.5.4
com.android.tools.analytics-library:crash:27.1.0
com.android.tools.analytics-library:protos:26.0.0
com.android.tools.analytics-library:protos:26.0.1
com.android.tools.analytics-library:protos:26.1.4
com.android.tools.analytics-library:protos:26.2.1
com.android.tools.analytics-library:protos:26.3.1
com.android.tools.analytics-library:protos:26.5.4
com.android.tools.analytics-library:protos:27.1.0
com.android.tools.analytics-library:shared:26.0.0
com.android.tools.analytics-library:shared:26.0.1
com.android.tools.analytics-library:shared:26.1.4
com.android.tools.analytics-library:shared:26.2.1
com.android.tools.analytics-library:shared:26.3.1
com.android.tools.analytics-library:shared:26.5.4
com.android.tools.analytics-library:shared:27.1.0
com.android.tools.analytics-library:tracker:26.0.0
com.android.tools.analytics-library:tracker:26.0.1
com.android.tools.analytics-library:tracker:26.1.4
com.android.tools.analytics-library:tracker:26.2.1
com.android.tools.analytics-library:tracker:26.3.1
com.android.tools.analytics-library:tracker:26.5.4
@@ -155,6 +160,7 @@ com.android.tools.build.jetifier:jetifier-processor:1.0.0-alpha10
com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta02
com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta04
com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta09
com.android.tools.build:aapt2-proto:0.1.0
com.android.tools.build:aapt2-proto:0.3.1
com.android.tools.build:aapt2-proto:0.4.0
com.android.tools.build:aapt2-proto:4.1.0-6503028
@@ -162,6 +168,7 @@ com.android.tools.build:aapt2-proto:4.1.0-alpha01-6193524
com.android.tools.build:aaptcompiler:4.1.0
com.android.tools.build:apksig:3.0.0
com.android.tools.build:apksig:3.0.1
com.android.tools.build:apksig:3.1.4
com.android.tools.build:apksig:3.2.1
com.android.tools.build:apksig:3.3.1
com.android.tools.build:apksig:3.5.4
@@ -175,6 +182,7 @@ com.android.tools.build:builder-model:1.3.1
com.android.tools.build:builder-model:1.5.0
com.android.tools.build:builder-model:3.0.0
com.android.tools.build:builder-model:3.0.1
com.android.tools.build:builder-model:3.1.4
com.android.tools.build:builder-model:3.2.1
com.android.tools.build:builder-model:3.3.1
com.android.tools.build:builder-model:3.5.4
@@ -184,6 +192,7 @@ com.android.tools.build:builder-test-api:1.3.1
com.android.tools.build:builder-test-api:1.5.0
com.android.tools.build:builder-test-api:3.0.0
com.android.tools.build:builder-test-api:3.0.1
com.android.tools.build:builder-test-api:3.1.4
com.android.tools.build:builder-test-api:3.2.1
com.android.tools.build:builder-test-api:3.3.1
com.android.tools.build:builder-test-api:3.5.4
@@ -193,16 +202,19 @@ com.android.tools.build:builder:1.3.1
com.android.tools.build:builder:1.5.0
com.android.tools.build:builder:3.0.0
com.android.tools.build:builder:3.0.1
com.android.tools.build:builder:3.1.4
com.android.tools.build:builder:3.2.1
com.android.tools.build:builder:3.3.1
com.android.tools.build:builder:3.5.4
com.android.tools.build:builder:4.1.0
com.android.tools.build:bundletool:0.1.0-alpha01
com.android.tools.build:bundletool:0.5.0
com.android.tools.build:bundletool:0.6.0
com.android.tools.build:bundletool:0.9.0
com.android.tools.build:bundletool:0.14.0
com.android.tools.build:gradle-api:3.0.0
com.android.tools.build:gradle-api:3.0.1
com.android.tools.build:gradle-api:3.1.4
com.android.tools.build:gradle-api:3.2.1
com.android.tools.build:gradle-api:3.3.1
com.android.tools.build:gradle-api:3.5.4
@@ -212,11 +224,13 @@ com.android.tools.build:gradle-core:1.3.1
com.android.tools.build:gradle-core:1.5.0
com.android.tools.build:gradle-core:3.0.0
com.android.tools.build:gradle-core:3.0.1
com.android.tools.build:gradle-core:3.1.4
com.android.tools.build:gradle:1.1.3
com.android.tools.build:gradle:1.3.1
com.android.tools.build:gradle:1.5.0
com.android.tools.build:gradle:3.0.0
com.android.tools.build:gradle:3.0.1
com.android.tools.build:gradle:3.1.4
com.android.tools.build:gradle:3.2.1
com.android.tools.build:gradle:3.3.1
com.android.tools.build:gradle:3.5.4
@@ -226,6 +240,7 @@ com.android.tools.build:manifest-merger:24.3.1
com.android.tools.build:manifest-merger:24.5.0
com.android.tools.build:manifest-merger:26.0.0
com.android.tools.build:manifest-merger:26.0.1
com.android.tools.build:manifest-merger:26.1.4
com.android.tools.build:manifest-merger:26.2.1
com.android.tools.build:manifest-merger:26.3.1
com.android.tools.build:manifest-merger:26.5.4
@@ -237,6 +252,7 @@ com.android.tools.ddms:ddmlib:24.3.1
com.android.tools.ddms:ddmlib:24.5.0
com.android.tools.ddms:ddmlib:26.0.0
com.android.tools.ddms:ddmlib:26.0.1
com.android.tools.ddms:ddmlib:26.1.4
com.android.tools.ddms:ddmlib:26.2.1
com.android.tools.ddms:ddmlib:26.3.1
com.android.tools.ddms:ddmlib:26.5.4
@@ -256,6 +272,7 @@ com.android.tools.layoutlib:layoutlib-api:24.3.1
com.android.tools.layoutlib:layoutlib-api:24.5.0
com.android.tools.layoutlib:layoutlib-api:26.0.0
com.android.tools.layoutlib:layoutlib-api:26.0.1
com.android.tools.layoutlib:layoutlib-api:26.1.4
com.android.tools.layoutlib:layoutlib-api:26.2.1
com.android.tools.layoutlib:layoutlib-api:26.3.1
com.android.tools.layoutlib:layoutlib-api:26.5.4
@@ -272,6 +289,7 @@ com.android.tools.lint:lint-checks:24.5.0
com.android.tools.lint:lint-checks:26.0.0
com.android.tools.lint:lint-checks:26.0.1
com.android.tools.lint:lint-checks:27.1.0
com.android.tools.lint:lint-gradle-api:26.1.4
com.android.tools.lint:lint-gradle-api:26.2.1
com.android.tools.lint:lint-gradle-api:26.3.1
com.android.tools.lint:lint-gradle-api:26.5.4
@@ -289,6 +307,7 @@ com.android.tools:annotations:24.3.1
com.android.tools:annotations:24.5.0
com.android.tools:annotations:26.0.0
com.android.tools:annotations:26.0.1
com.android.tools:annotations:26.1.4
com.android.tools:annotations:26.2.1
com.android.tools:annotations:26.3.1
com.android.tools:annotations:26.5.4
@@ -298,6 +317,7 @@ com.android.tools:common:24.3.1
com.android.tools:common:24.5.0
com.android.tools:common:26.0.0
com.android.tools:common:26.0.1
com.android.tools:common:26.1.4
com.android.tools:common:26.2.1
com.android.tools:common:26.3.1
com.android.tools:common:26.5.4
@@ -307,12 +327,14 @@ com.android.tools:dvlib:24.3.1
com.android.tools:dvlib:24.5.0
com.android.tools:dvlib:26.0.0
com.android.tools:dvlib:26.0.1
com.android.tools:dvlib:26.1.4
com.android.tools:dvlib:26.2.1
com.android.tools:dvlib:26.3.1
com.android.tools:dvlib:26.5.4
com.android.tools:dvlib:27.1.0
com.android.tools:repository:26.0.0
com.android.tools:repository:26.0.1
com.android.tools:repository:26.1.4
com.android.tools:repository:26.2.1
com.android.tools:repository:26.3.1
com.android.tools:repository:26.5.4
@@ -322,6 +344,7 @@ com.android.tools:sdklib:24.3.1
com.android.tools:sdklib:24.5.0
com.android.tools:sdklib:26.0.0
com.android.tools:sdklib:26.0.1
com.android.tools:sdklib:26.1.4
com.android.tools:sdklib:26.2.1
com.android.tools:sdklib:26.3.1
com.android.tools:sdklib:26.5.4
@@ -331,6 +354,7 @@ com.android.tools:sdk-common:24.3.1
com.android.tools:sdk-common:24.5.0
com.android.tools:sdk-common:26.0.0
com.android.tools:sdk-common:26.0.1
com.android.tools:sdk-common:26.1.4
com.android.tools:sdk-common:26.2.1
com.android.tools:sdk-common:26.3.1
com.android.tools:sdk-common:26.5.4
@@ -391,11 +415,13 @@ com.google.code.findbugs:jsr305:1.3.9
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.2.4
com.google.code.gson:gson:2.3
com.google.code.gson:gson:2.7
com.google.code.gson:gson:2.8.0
com.google.code.gson:gson:2.8.5
com.google.code.gson:gson:2.8.6
com.google.crypto.tink:tink:1.3.0-rc2
com.google.errorprone:error_prone_annotations:2.0.18
com.google.errorprone:error_prone_annotations:2.1.2
com.google.errorprone:error_prone_annotations:2.2.0
com.google.errorprone:error_prone_annotations:2.3.1
com.google.errorprone:error_prone_annotations:2.3.2
@@ -569,7 +595,10 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30
org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0
org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0
org.jetbrains.kotlin:kotlin-stdlib:1.1.3-2
org.jetbrains.kotlin:kotlin-stdlib:1.2.0
org.jetbrains.kotlin:kotlin-stdlib:1.2.71
org.jetbrains.kotlin:kotlin-stdlib:1.3.20
org.jetbrains.kotlin:kotlin-stdlib:1.3.50
+37 -10
View File
@@ -122,12 +122,14 @@ https://dl.google.com/dl/android/maven2/android/arch/lifecycle/common/1.0.0/comm
https://dl.google.com/dl/android/maven2/android/arch/lifecycle/runtime/1.0.0/runtime-1.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/databinding/baseLibrary/3.0.0/baseLibrary-3.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/databinding/baseLibrary/3.0.1/baseLibrary-3.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/databinding/baseLibrary/3.1.4/baseLibrary-3.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/databinding/baseLibrary/3.2.1/baseLibrary-3.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/databinding/baseLibrary/3.3.1/baseLibrary-3.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/databinding/baseLibrary/3.5.4/baseLibrary-3.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/databinding/baseLibrary/4.1.0/baseLibrary-4.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/databinding/compilerCommon/3.0.0/compilerCommon-3.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/databinding/compilerCommon/3.0.1/compilerCommon-3.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/databinding/compilerCommon/3.1.4/compilerCommon-3.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/signflinger/4.1.0/signflinger-4.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/support/animated-vector-drawable/26.0.2/animated-vector-drawable-26.0.2.pom
https://dl.google.com/dl/android/maven2/com/android/support/animated-vector-drawable/27.0.1/animated-vector-drawable-27.0.1.pom
@@ -155,28 +157,33 @@ https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/cras
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/crash/27.1.0/crash-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/protos/26.0.0/protos-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/protos/26.0.1/protos-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/protos/26.1.4/protos-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/protos/26.2.1/protos-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/protos/26.3.1/protos-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/protos/26.5.4/protos-26.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/protos/27.1.0/protos-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/shared/26.0.0/shared-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/shared/26.0.1/shared-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/shared/26.1.4/shared-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/shared/26.2.1/shared-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/shared/26.3.1/shared-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/shared/26.5.4/shared-26.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/shared/27.1.0/shared-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/tracker/26.0.0/tracker-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/tracker/26.0.1/tracker-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/tracker/26.1.4/tracker-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/tracker/26.2.1/tracker-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/tracker/26.3.1/tracker-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/tracker/26.5.4/tracker-26.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/analytics-library/tracker/27.1.0/tracker-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/annotations/26.0.0/annotations-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/annotations/26.0.1/annotations-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/annotations/26.1.4/annotations-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/annotations/26.2.1/annotations-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/annotations/26.3.1/annotations-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/annotations/26.5.4/annotations-26.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/annotations/27.1.0/annotations-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/aapt2-proto/0.1.0/aapt2-proto-0.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/aapt2-proto/0.3.1/aapt2-proto-0.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/aapt2-proto/0.4.0/aapt2-proto-0.4.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/aapt2-proto/4.1.0-6503028/aapt2-proto-4.1.0-6503028.pom
@@ -184,6 +191,7 @@ https://dl.google.com/dl/android/maven2/com/android/tools/build/aapt2-proto/4.1.
https://dl.google.com/dl/android/maven2/com/android/tools/build/aaptcompiler/4.1.0/aaptcompiler-4.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/apksig/3.0.0/apksig-3.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/apksig/3.0.1/apksig-3.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/apksig/3.1.4/apksig-3.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/apksig/3.2.1/apksig-3.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/apksig/3.3.1/apksig-3.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/apksig/3.5.4/apksig-3.5.4.pom
@@ -194,36 +202,43 @@ https://dl.google.com/dl/android/maven2/com/android/tools/build/apkzlib/3.5.4/ap
https://dl.google.com/dl/android/maven2/com/android/tools/build/apkzlib/4.1.0/apkzlib-4.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-model/3.0.0/builder-model-3.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-model/3.0.1/builder-model-3.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-model/3.1.4/builder-model-3.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-model/3.2.1/builder-model-3.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-model/3.3.1/builder-model-3.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-model/3.5.4/builder-model-3.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-model/4.1.0/builder-model-4.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-test-api/3.0.0/builder-test-api-3.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-test-api/3.0.1/builder-test-api-3.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-test-api/3.1.4/builder-test-api-3.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-test-api/3.2.1/builder-test-api-3.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-test-api/3.3.1/builder-test-api-3.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-test-api/3.5.4/builder-test-api-3.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder-test-api/4.1.0/builder-test-api-4.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder/3.0.0/builder-3.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder/3.0.1/builder-3.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder/3.1.4/builder-3.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder/3.2.1/builder-3.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder/3.3.1/builder-3.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder/3.5.4/builder-3.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/builder/4.1.0/builder-4.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/bundletool/0.1.0-alpha01/bundletool-0.1.0-alpha01.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/bundletool/0.5.0/bundletool-0.5.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/bundletool/0.6.0/bundletool-0.6.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/bundletool/0.9.0/bundletool-0.9.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/bundletool/0.14.0/bundletool-0.14.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-api/3.0.0/gradle-api-3.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-api/3.0.1/gradle-api-3.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-api/3.1.4/gradle-api-3.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-api/3.2.1/gradle-api-3.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-api/3.3.1/gradle-api-3.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-api/3.5.4/gradle-api-3.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-api/4.1.0/gradle-api-4.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-core/3.0.0/gradle-core-3.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-core/3.0.1/gradle-core-3.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle-core/3.1.4/gradle-core-3.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.0.0/gradle-3.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.0.1/gradle-3.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.1.4/gradle-3.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.2.1/gradle-3.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.3.1/gradle-3.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.5.4/gradle-3.5.4.pom
@@ -238,24 +253,28 @@ https://dl.google.com/dl/android/maven2/com/android/tools/build/jetifier/jetifie
https://dl.google.com/dl/android/maven2/com/android/tools/build/jetifier/jetifier-processor/1.0.0-beta09/jetifier-processor-1.0.0-beta09.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/manifest-merger/26.0.0/manifest-merger-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/manifest-merger/26.0.1/manifest-merger-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/manifest-merger/26.1.4/manifest-merger-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/manifest-merger/26.2.1/manifest-merger-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/manifest-merger/26.3.1/manifest-merger-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/manifest-merger/26.5.4/manifest-merger-26.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/build/manifest-merger/27.1.0/manifest-merger-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/common/26.0.0/common-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/common/26.0.1/common-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/common/26.1.4/common-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/common/26.2.1/common-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/common/26.3.1/common-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/common/26.5.4/common-26.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/common/27.1.0/common-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/ddms/ddmlib/26.0.0/ddmlib-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/ddms/ddmlib/26.0.1/ddmlib-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/ddms/ddmlib/26.1.4/ddmlib-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/ddms/ddmlib/26.2.1/ddmlib-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/ddms/ddmlib/26.3.1/ddmlib-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/ddms/ddmlib/26.5.4/ddmlib-26.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/ddms/ddmlib/27.1.0/ddmlib-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/dvlib/26.0.0/dvlib-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/dvlib/26.0.1/dvlib-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/dvlib/26.1.4/dvlib-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/dvlib/26.2.1/dvlib-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/dvlib/26.3.1/dvlib-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/dvlib/26.5.4/dvlib-26.5.4.pom
@@ -269,6 +288,7 @@ https://dl.google.com/dl/android/maven2/com/android/tools/external/org-jetbrains
https://dl.google.com/dl/android/maven2/com/android/tools/external/org-jetbrains/uast/27.1.0/uast-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/layoutlib/layoutlib-api/26.0.0/layoutlib-api-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/layoutlib/layoutlib-api/26.0.1/layoutlib-api-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/layoutlib/layoutlib-api/26.1.4/layoutlib-api-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/layoutlib/layoutlib-api/26.2.1/layoutlib-api-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/layoutlib/layoutlib-api/26.3.1/layoutlib-api-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/layoutlib/layoutlib-api/26.5.4/layoutlib-api-26.5.4.pom
@@ -279,6 +299,7 @@ https://dl.google.com/dl/android/maven2/com/android/tools/lint/lint-api/27.1.0/l
https://dl.google.com/dl/android/maven2/com/android/tools/lint/lint-checks/26.0.0/lint-checks-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/lint/lint-checks/26.0.1/lint-checks-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/lint/lint-checks/27.1.0/lint-checks-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/lint/lint-gradle-api/26.1.4/lint-gradle-api-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/lint/lint-gradle-api/26.2.1/lint-gradle-api-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/lint/lint-gradle-api/26.3.1/lint-gradle-api-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/lint/lint-gradle-api/26.5.4/lint-gradle-api-26.5.4.pom
@@ -290,18 +311,21 @@ https://dl.google.com/dl/android/maven2/com/android/tools/lint/lint/26.0.1/lint-
https://dl.google.com/dl/android/maven2/com/android/tools/lint/lint/27.1.0/lint-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/repository/26.0.0/repository-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/repository/26.0.1/repository-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/repository/26.1.4/repository-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/repository/26.2.1/repository-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/repository/26.3.1/repository-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/repository/26.5.4/repository-26.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/repository/27.1.0/repository-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdklib/26.0.0/sdklib-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdklib/26.0.1/sdklib-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdklib/26.1.4/sdklib-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdklib/26.2.1/sdklib-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdklib/26.3.1/sdklib-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdklib/26.5.4/sdklib-26.5.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdklib/27.1.0/sdklib-27.1.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdk-common/26.0.0/sdk-common-26.0.0.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdk-common/26.0.1/sdk-common-26.0.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdk-common/26.1.4/sdk-common-26.1.4.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdk-common/26.2.1/sdk-common-26.2.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdk-common/26.3.1/sdk-common-26.3.1.pom
https://dl.google.com/dl/android/maven2/com/android/tools/sdk-common/26.5.4/sdk-common-26.5.4.pom
@@ -472,11 +496,13 @@ https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.8.5/gson-2.8.5.
https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.8.6/gson-2.8.6.pom
https://repo.maven.apache.org/maven2/com/google/crypto/tink/tink/1.3.0-rc2/tink-1.3.0-rc2.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.0.18/error_prone_annotations-2.0.18.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.1.2/error_prone_annotations-2.1.2.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.2.0/error_prone_annotations-2.2.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.3.1/error_prone_annotations-2.3.1.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.3.2/error_prone_annotations-2.3.2.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.18.0/error_prone_annotations-2.18.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.0.18/error_prone_parent-2.0.18.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.1.2/error_prone_parent-2.1.2.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.2.0/error_prone_parent-2.2.0.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.3.1/error_prone_parent-2.3.1.pom
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.3.2/error_prone_parent-2.3.2.pom
@@ -546,10 +572,10 @@ https://repo.maven.apache.org/maven2/com/sun/activation/all/1.2.0/all-1.2.0.pom
https://repo.maven.apache.org/maven2/com/sun/activation/javax.activation/1.2.0/javax.activation-1.2.0.pom
https://repo.maven.apache.org/maven2/com/sun/istack/istack-commons-runtime/2.21/istack-commons-runtime-2.21.pom
https://repo.maven.apache.org/maven2/com/sun/istack/istack-commons-runtime/3.0.7/istack-commons-runtime-3.0.7.pom
https://repo.maven.apache.org/maven2/com/sun/istack/istack-commons-runtime/4.2.0/istack-commons-runtime-4.2.0.pom
https://repo.maven.apache.org/maven2/com/sun/istack/istack-commons-runtime/4.1.1/istack-commons-runtime-4.1.1.pom
https://repo.maven.apache.org/maven2/com/sun/istack/istack-commons/2.21/istack-commons-2.21.pom
https://repo.maven.apache.org/maven2/com/sun/istack/istack-commons/3.0.7/istack-commons-3.0.7.pom
https://repo.maven.apache.org/maven2/com/sun/istack/istack-commons/4.2.0/istack-commons-4.2.0.pom
https://repo.maven.apache.org/maven2/com/sun/istack/istack-commons/4.1.1/istack-commons-4.1.1.pom
https://repo.maven.apache.org/maven2/com/sun/xml/bind/jaxb-bom-ext/2.2.11/jaxb-bom-ext-2.2.11.pom
https://repo.maven.apache.org/maven2/com/sun/xml/bind/jaxb-bom-ext/2.3.1/jaxb-bom-ext-2.3.1.pom
https://repo.maven.apache.org/maven2/com/sun/xml/bind/jaxb-bom-ext/4.0.2/jaxb-bom-ext-4.0.2.pom
@@ -577,7 +603,7 @@ https://repo.maven.apache.org/maven2/com/tunnelvisionlabs/antlr4/4.5/antlr4-4.5.
https://repo.maven.apache.org/maven2/de/undercouch/gradle-download-task/3.4.3/gradle-download-task-3.4.3.pom
https://repo.maven.apache.org/maven2/de/undercouch/gradle-download-task/4.0.2/gradle-download-task-4.0.2.pom
https://repo.maven.apache.org/maven2/it/unimi/dsi/fastutil/7.2.0/fastutil-7.2.0.pom
https://repo.maven.apache.org/maven2/jakarta/activation/jakarta.activation-api/2.1.2/jakarta.activation-api-2.1.2.pom
https://repo.maven.apache.org/maven2/jakarta/activation/jakarta.activation-api/2.1.1/jakarta.activation-api-2.1.1.pom
https://repo.maven.apache.org/maven2/jakarta/xml/bind/jakarta.xml.bind-api-parent/4.0.0/jakarta.xml.bind-api-parent-4.0.0.pom
https://repo.maven.apache.org/maven2/jakarta/xml/bind/jakarta.xml.bind-api/4.0.0/jakarta.xml.bind-api-4.0.0.pom
https://repo.maven.apache.org/maven2/javax/activation/activation/1.1/activation-1.1.pom
@@ -693,7 +719,7 @@ https://repo.maven.apache.org/maven2/org/bouncycastle/bcprov-jdk15on/1.60/bcprov
https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/2.5.2/checker-qual-2.5.2.pom
https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/2.5.8/checker-qual-2.5.8.pom
https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/2.8.1/checker-qual-2.8.1.pom
https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/3.34.0/checker-qual-3.34.0.pom
https://repo.maven.apache.org/maven2/org/checkerframework/checker-qual/3.32.0/checker-qual-3.32.0.pom
https://repo.maven.apache.org/maven2/org/codehaus/codehaus-parent/4/codehaus-parent-4.pom
https://repo.maven.apache.org/maven2/org/codehaus/groovy/groovy-all/2.4.15/groovy-all-2.4.15.pom
https://repo.maven.apache.org/maven2/org/codehaus/mojo/animal-sniffer-annotations/1.14/animal-sniffer-annotations-1.14.pom
@@ -713,8 +739,8 @@ https://repo.maven.apache.org/maven2/org/easymock/easymockclassextension/3.2/eas
https://repo.maven.apache.org/maven2/org/easymock/easymock-parent/3.2/easymock-parent-3.2.pom
https://repo.maven.apache.org/maven2/org/easymock/easymock-parent/5.1.0/easymock-parent-5.1.0.pom
https://repo.maven.apache.org/maven2/org/easymock/easymock/5.1.0/easymock-5.1.0.pom
https://repo.maven.apache.org/maven2/org/eclipse/angus/angus-activation-project/2.0.1/angus-activation-project-2.0.1.pom
https://repo.maven.apache.org/maven2/org/eclipse/angus/angus-activation/2.0.1/angus-activation-2.0.1.pom
https://repo.maven.apache.org/maven2/org/eclipse/angus/angus-activation-project/2.0.0/angus-activation-project-2.0.0.pom
https://repo.maven.apache.org/maven2/org/eclipse/angus/angus-activation/2.0.0/angus-activation-2.0.0.pom
https://repo.maven.apache.org/maven2/org/eclipse/ee4j/project/1.0.7/project-1.0.7.pom
https://repo.maven.apache.org/maven2/org/eclipse/ee4j/project/1.0.8/project-1.0.8.pom
https://repo.maven.apache.org/maven2/org/eclipse/jdt/core/compiler/ecj/4.4.2/ecj-4.4.2.pom
@@ -735,9 +761,9 @@ https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/2.2/hamcrest-cor
https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom
https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest/2.2/hamcrest-2.2.pom
https://repo.maven.apache.org/maven2/org/jacoco/org.jacoco.build/0.7.4.201502262128/org.jacoco.build-0.7.4.201502262128.pom
https://repo.maven.apache.org/maven2/org/jacoco/org.jacoco.build/0.8.10/org.jacoco.build-0.8.10.pom
https://repo.maven.apache.org/maven2/org/jacoco/org.jacoco.build/0.8.8/org.jacoco.build-0.8.8.pom
https://repo.maven.apache.org/maven2/org/jacoco/org.jacoco.core/0.7.4.201502262128/org.jacoco.core-0.7.4.201502262128.pom
https://repo.maven.apache.org/maven2/org/jacoco/org.jacoco.core/0.8.10/org.jacoco.core-0.8.10.pom
https://repo.maven.apache.org/maven2/org/jacoco/org.jacoco.core/0.8.8/org.jacoco.core-0.8.8.pom
https://repo.maven.apache.org/maven2/org/jacoco/org.jacoco.report/0.7.4.201502262128/org.jacoco.report-0.7.4.201502262128.pom
https://repo.maven.apache.org/maven2/org/jdom/jdom2/2.0.6/jdom2-2.0.6.pom
https://repo.maven.apache.org/maven2/org/jetbrains/annotations/13.0/annotations-13.0.pom
@@ -818,6 +844,8 @@ https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.3
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.4.10/kotlin-stdlib-jdk8-1.4.10.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5.30/kotlin-stdlib-jdk8-1.5.30.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.0/kotlin-stdlib-jdk8-1.8.0.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jre7/1.2.0/kotlin-stdlib-jre7-1.2.0.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jre8/1.2.0/kotlin-stdlib-jre8-1.2.0.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.1.3-2/kotlin-stdlib-1.1.3-2.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.0/kotlin-stdlib-1.2.0.pom
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.20/kotlin-stdlib-1.2.20.pom
@@ -842,7 +870,7 @@ https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-util-klib/1.4.3
https://repo.maven.apache.org/maven2/org/jetbrains/trove4j/trove4j/20160824/trove4j-20160824.pom
https://repo.maven.apache.org/maven2/org/json/json/20180813/json-20180813.pom
https://repo.maven.apache.org/maven2/org/json/json/20230227/json-20230227.pom
https://repo.maven.apache.org/maven2/org/junit/junit-bom/5.9.3/junit-bom-5.9.3.pom
https://repo.maven.apache.org/maven2/org/junit/junit-bom/5.9.2/junit-bom-5.9.2.pom
https://repo.maven.apache.org/maven2/org/jvnet/staxex/stax-ex/1.7.7/stax-ex-1.7.7.pom
https://repo.maven.apache.org/maven2/org/jvnet/staxex/stax-ex/1.8/stax-ex-1.8.pom
https://repo.maven.apache.org/maven2/org/jvnet/staxex/stax-ex/2.1.0/stax-ex-2.1.0.pom
@@ -852,7 +880,6 @@ https://repo.maven.apache.org/maven2/org/ow2/asm/asm-analysis/5.0.3/asm-analysis
https://repo.maven.apache.org/maven2/org/ow2/asm/asm-analysis/5.1/asm-analysis-5.1.pom
https://repo.maven.apache.org/maven2/org/ow2/asm/asm-analysis/6.0/asm-analysis-6.0.pom
https://repo.maven.apache.org/maven2/org/ow2/asm/asm-analysis/7.0/asm-analysis-7.0.pom
https://repo.maven.apache.org/maven2/org/ow2/asm/asm-bom/9.5/asm-bom-9.5.pom
https://repo.maven.apache.org/maven2/org/ow2/asm/asm-commons/5.1/asm-commons-5.1.pom
https://repo.maven.apache.org/maven2/org/ow2/asm/asm-commons/6.0/asm-commons-6.0.pom
https://repo.maven.apache.org/maven2/org/ow2/asm/asm-commons/7.0/asm-commons-7.0.pom
+1 -1
View File
@@ -30,9 +30,9 @@ react-native-orientation-locker
react-native-permissions
react-native-randombytes
react-native-reanimated
react-native-safe-area-context
react-native-shake
react-native-share
react-native-static-safe-area-insets
react-native-status
react-native-status-keycard
react-native-svg
+2 -6
View File
@@ -46,7 +46,7 @@ function fetch_and_template_file() {
OBJ_URL="${REPO_URL}/${PKG_PATH}/${FILENAME}"
if ! OBJ_NIX_FETCH_OUT=$(nix_fetch "${OBJ_URL}"); then
echo " ! Failed to fetch: ${OBJ_URL}" >&2
return 1
exit 1
fi
OBJ_NAME="${FILENAME}"
@@ -58,10 +58,6 @@ function fetch_and_template_file() {
}"
}
function fetch_and_template_file_no_fail() {
fetch_and_template_file "${1}" 2>/dev/null || true
}
if [[ -z "${1}" ]]; then
echo "Required POM URL argument not given!" >&2
exit 1
@@ -115,7 +111,7 @@ echo -ne "
}"
# Some deps are just POMs, in which case there is no JAR to fetch.
[[ "${OBJ_TYPE}" == "" ]] && fetch_and_template_file_no_fail "${PKG_NAME}.jar"
[[ "${OBJ_TYPE}" == "" ]] && fetch_and_template_file "${PKG_NAME}.jar"
[[ "${OBJ_TYPE}" == "jar" ]] && fetch_and_template_file "${PKG_NAME}.jar"
[[ "${OBJ_TYPE}" == "bundle" ]] && fetch_and_template_file "${PKG_NAME}.jar"
[[ "${OBJ_TYPE}" =~ aar* ]] && fetch_and_template_file "${PKG_NAME}.aar"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 B

@@ -11,7 +11,7 @@
:inner 44
:status-indicator 12
:status-indicator-border 2
:font-size :heading-2}
:font-size :paragraph-1}
:small {:outer 32
:inner 28
:status-indicator 12
@@ -1,20 +0,0 @@
(ns quo2.components.buttons.predictive-keyboard.component-spec
(:require [quo2.components.buttons.predictive-keyboard.view :as predictive-keyboard]
[test-helpers.component :as h]))
(h/describe "predictive-keyboard"
(h/test "basic render"
(h/render [predictive-keyboard/view {:type :error :text "Error message"}])
(h/is-truthy (h/get-by-label-text :predictive-keyboard)))
(h/test "rendered with correct text"
(h/render [predictive-keyboard/view {:type :info :text "Error message"}])
(h/is-truthy (h/get-by-text "Error message")))
(h/test "rendered with correct words"
(h/render [predictive-keyboard/view {:type :words :words ["lab" "label"]}])
(h/is-truthy (h/get-by-text "lab"))
(h/is-truthy (h/get-by-text "label")))
(h/test "word press event"
(let [event (h/mock-fn)]
(h/render [predictive-keyboard/view {:type :words :words ["lab" "label"] :on-press #(event %)}])
(h/fire-event :press (h/get-by-text "lab"))
(h/was-called event))))
@@ -1,11 +0,0 @@
(ns quo2.components.buttons.predictive-keyboard.style)
(defn wrapper
[type]
{:flex 1
:min-height 48
:padding-vertical 8
:justify-content :center
:padding-horizontal (if (= type :words) 0 20)})
(def word-list {:align-items :center :padding-horizontal 20})
@@ -1,71 +0,0 @@
(ns quo2.components.buttons.predictive-keyboard.view
(:require [react-native.core :as rn]
[quo2.components.buttons.predictive-keyboard.style :as style]
[quo2.components.info.info-message :as info-message]
[react-native.linear-gradient :as linear-gradient]
[quo2.foundations.colors :as colors]
[quo2.components.buttons.button :as button]))
(def ^:private gradients
{:light [(colors/alpha colors/neutral-40 0.05) (colors/alpha colors/neutral-40 0)]
:dark [(colors/alpha colors/neutral-80 0.7) (colors/alpha colors/neutral-80 0)]
:blur [colors/white-opa-5 colors/white-opa-0]})
(defn- word-component
[word _ _ {:keys [blur? on-press]}]
[button/button
(merge {:type :blur-bg
:size 32
:on-press #(on-press word)}
(when blur? {:override-theme :dark}))
word])
(defn- separator
[]
[rn/view {:style {:width 8}}])
(defn view
"Options
- `type` `:words`/`:error`/`:info`/`:empty`.
- `blur?` Boolean to enable blur background support.
- `text` error/info text.
- `words` List of words to display in the keyboard.
- `on-press` Callback called when a word is pressed `(fn [word])`."
[{:keys [type blur? text words on-press]}]
[linear-gradient/linear-gradient
{:style {:flex-direction :row}
:accessibility-label :predictive-keyboard
:colors (if blur?
(gradients :blur)
(colors/theme-colors (gradients :light) (gradients :dark)))}
[rn/view {:style (style/wrapper type)}
(case type
:words
[rn/flat-list
{:data words
:content-container-style style/word-list
:render-fn word-component
:render-data {:blur? blur?
:on-press on-press}
:shows-horizontal-scroll-indicator false
:separator [separator]
:horizontal true
:key-fn str}]
:error
[info-message/info-message
{:icon :i/info
:size :default
:type :error}
text]
:info
[info-message/info-message
(merge {:icon :i/info
:size :default
:type (if (= type :error) :error :default)}
(when blur?
{:text-color colors/white-opa-70
:icon-color colors/white-opa-70}))
text]
nil)]])
@@ -1,30 +0,0 @@
(ns quo2.components.drawers.documentation-drawers.component-spec
(:require [quo2.components.drawers.documentation-drawers.view :as documentation-drawers]
[test-helpers.component :as h]))
(h/describe "Documentation drawers component"
(h/test "render component without button"
(h/render [documentation-drawers/view {:title "Documentation"} "Content"])
(-> (h/expect (h/get-by-label-text :documentation-drawer-title))
(.toBeTruthy))
(-> (h/expect (h/get-by-label-text :documentation-drawer-content))
(.toBeTruthy)))
(h/test "render component with button"
(h/render [documentation-drawers/view
{:title "Documentation" :show-button? true :button-label "Read more"} "Content"])
(-> (h/expect (h/get-by-label-text :documentation-drawer-title))
(.toBeTruthy))
(-> (h/expect (h/get-by-label-text :documentation-drawer-content))
(.toBeTruthy))
(-> (h/expect (h/get-by-label-text :documentation-drawer-button))
(.toBeTruthy)))
(h/test "button is pressed"
(let [event (h/mock-fn)]
(h/render
[documentation-drawers/view
{:title "Documentation" :show-button? true :button-label "Read more" :on-press-button event}
"Content"])
(h/fire-event :press (h/get-by-label-text :documentation-drawer-button))
(-> (h/expect event)
(.toHaveBeenCalled)))))
@@ -1,10 +0,0 @@
(ns quo2.components.drawers.documentation-drawers.style)
(def container
{:align-items :flex-start
:padding 20})
(def content
{:margin-top 12
:margin-bottom 16})
@@ -1,40 +0,0 @@
(ns quo2.components.drawers.documentation-drawers.view
(:require [quo2.components.markdown.text :as text]
[quo2.components.drawers.documentation-drawers.style :as style]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[quo2.components.buttons.button :as button]
[quo2.foundations.colors :as colors]))
(defn view
"Options
- `title` Title text
- `show-button?` Show button
- `button-label` button label
- `button-icon` button icon
- `on-press-button` On press handler for the button
- `shell?` use shell theme
`content` Content of the drawer
"
[{:keys [title show-button? on-press-button button-label button-icon shell?]} content]
[gesture/scroll-view
[rn/view {:style style/container}
[text/text
{:size :heading-2
:accessibility-label :documentation-drawer-title
:style {:color (colors/theme-colors colors/neutral-100
colors/white
(when shell? :dark))}
:weight :semi-bold} title]
[rn/view {:style style/content :accessibility-label :documentation-drawer-content}
content]
(when show-button?
[button/button
(merge {:size 24
:type (if shell? :blur-bg-outline :outline)
:on-press on-press-button
:accessibility-label :documentation-drawer-button
:after button-icon}
(when shell? {:override-theme :dark})) button-label])]])
+8 -10
View File
@@ -6,10 +6,10 @@
[react-native.core :as rn]))
(def themes
{:light {:default colors/neutral-50
{:light {:default colors/neutral-40
:success colors/success-50
:error colors/danger-50}
:dark {:default colors/neutral-40
:dark {:default colors/neutral-60
:success colors/success-60
:error colors/danger-60}})
@@ -27,20 +27,18 @@
: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? style]} message]
(let [weight (if (= size :default) :regular :medium)
icon-size (if (= size :default) 16 12)
icon-margin-top (if (= size :default) 2 3)
size (if (= size :default) :paragraph-2 :label)
text-color (or text-color (get-color type))
icon-color (or icon-color text-color)]
(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 (merge {:flex-direction :row}
style)}
[quo2.icons/icon icon
{:color icon-color
:no-color no-icon-color?
:size icon-size
:container-style {:margin-top icon-margin-top}}]
:size 12
:container-style {:margin-top 3}}]
[text/text
{:size size
:weight weight
@@ -1,6 +1,5 @@
(ns quo2.components.inputs.title-input.style
(:require [quo2.foundations.colors :as colors]
[react-native.platform :as platform]))
(:require [quo2.foundations.colors :as colors]))
(defn get-focused-placeholder-color
[blur? override-theme]
@@ -29,11 +28,9 @@
(defn get-selection-color
[customization-color blur? override-theme]
(colors/alpha (if blur?
(colors/theme-colors colors/neutral-100 colors/white override-theme)
(colors/custom-color customization-color
(if (or (= :dark override-theme) colors/dark?) 60 50)))
(if platform/ios? 1 0.2)))
(if blur?
(colors/theme-colors colors/neutral-100 colors/white override-theme)
(colors/custom-color customization-color (if (or (= :dark override-theme) (colors/dark?)) 60 50))))
(def text-input-container {:flex 1})
@@ -154,12 +154,12 @@
ready-to-send? (reagent/atom false)
ready-to-lock? (reagent/atom false)
ready-to-delete? (reagent/atom false)
reviewing-audio? (reagent/atom (some? audio-file))
reviewing-audio? (reagent/atom false)
playing-audio? (reagent/atom false)
recording-length-ms (reagent/atom 0)
audio-current-time-ms (reagent/atom 0)
seeking-audio? (reagent/atom false)
force-show-controls? (reagent/atom (some? audio-file))
force-show-controls? (reagent/atom false)
clear-timeout (atom nil)
record-button-at-initial-position? (atom true)
record-button-is-animating? (atom false)
@@ -169,7 +169,7 @@
touch-active? (atom false)
recording-timer (atom nil)
playing-timer (atom nil)
output-file (atom audio-file)
output-file (atom nil)
reached-max-duration? (atom false)
touch-timestamp (atom nil)
disabled? (atom false)
@@ -512,7 +512,10 @@
(on-init reset-recorder))
(when audio-file
(let [filename (last (string/split audio-file "/"))]
(reload-player filename)))))
(reload-player filename)
(reset! output-file audio-file)
(reset! reviewing-audio? true)
(reset! force-show-controls? true)))))
[rn/view
{:style style/bar-container
:pointer-events :box-none}
@@ -522,7 +525,7 @@
seeking-audio?]
[:f> soundtrack/f-soundtrack
{:audio-current-time-ms audio-current-time-ms
:player-ref @player-ref
:player-ref player-ref
:seeking-audio? seeking-audio?}]])
(when (or @recording? @reviewing-audio?)
[:f> f-time-counter @recording? @recording-length-ms @ready-to-delete? @reviewing-audio?
@@ -19,7 +19,7 @@
(let [player-ref (reagent/atom {})
audio-current-time-ms (reagent/atom 0)]
(h/render [:f> soundtrack/f-soundtrack
{:player-ref @player-ref
{:player-ref player-ref
:audio-current-time-ms audio-current-time-ms}])
(-> (h/expect (h/get-by-test-id "soundtrack"))
(.toBeTruthy)))))
@@ -31,7 +31,7 @@
audio-current-time-ms (reagent/atom 0)]
(h/render [:f> soundtrack/f-soundtrack
{:seeking-audio? seeking-audio?
:player-ref @player-ref
:player-ref player-ref
:audio-current-time-ms audio-current-time-ms}])
(h/fire-event
:on-sliding-start
@@ -47,7 +47,7 @@
audio-current-time-ms (reagent/atom 0)]
(h/render [:f> soundtrack/f-soundtrack
{:seeking-audio? seeking-audio?
:player-ref @player-ref
:player-ref player-ref
:audio-current-time-ms audio-current-time-ms}])
(h/fire-event
:on-sliding-start
@@ -69,7 +69,7 @@
audio-current-time-ms (reagent/atom 0)]
(h/render [:f> soundtrack/f-soundtrack
{:seeking-audio? seeking-audio?
:player-ref @player-ref
:player-ref player-ref
:audio-current-time-ms audio-current-time-ms}])
(h/fire-event
:on-sliding-start
@@ -10,14 +10,12 @@
(def ^:private thumb-dark (js/require "../resources/images/icons2/12x12/thumb-dark.png"))
(defn f-soundtrack
[{:keys [audio-current-time-ms player-ref style seeking-audio?]}]
(let [audio-duration-ms (audio/get-player-duration player-ref)]
[{:keys [audio-current-time-ms player-ref seeking-audio?]}]
(let [audio-duration-ms (audio/get-player-duration @player-ref)]
[:<>
[slider/slider
{:test-ID "soundtrack"
:style (merge
(style/player-slider-container)
(or style {}))
:style (style/player-slider-container)
:minimum-value 0
:maximum-value audio-duration-ms
:value @audio-current-time-ms
@@ -25,7 +23,7 @@
:on-sliding-complete (fn [seek-time]
(reset! seeking-audio? false)
(audio/seek-player
player-ref
@player-ref
seek-time
#(log/debug "[record-audio] on seek - seek time: " seek-time)
#(log/error "[record-audio] on seek - error: " %)))
+2 -10
View File
@@ -10,7 +10,6 @@
quo2.components.banners.banner.view
quo2.components.buttons.button
quo2.components.buttons.dynamic-button
quo2.components.buttons.predictive-keyboard.view
quo2.components.colors.color-picker.view
quo2.components.community.community-card-view
quo2.components.community.community-list-view
@@ -25,7 +24,6 @@
quo2.components.dividers.new-messages
quo2.components.dividers.strength-divider.view
quo2.components.drawers.action-drawers.view
quo2.components.drawers.documentation-drawers.view
quo2.components.drawers.drawer-buttons.view
quo2.components.drawers.permission-context.view
quo2.components.dropdowns.dropdown
@@ -64,7 +62,6 @@
quo2.components.profile.select-profile.view
quo2.components.reactions.reaction
quo2.components.record-audio.record-audio.view
quo2.components.record-audio.soundtrack.view
quo2.components.selectors.disclaimer.view
quo2.components.selectors.filter.view
quo2.components.selectors.selectors.view
@@ -84,6 +81,8 @@
quo2.components.tags.token-tag
quo2.components.text-combinations.title.view))
(def button quo2.components.buttons.button/button)
(def dynamic-button quo2.components.buttons.dynamic-button/dynamic-button)
(def text quo2.components.markdown.text/text)
(def icon quo2.components.icon/icon)
(def separator quo2.components.separator/separator)
@@ -119,11 +118,6 @@
;;;; BANNER
(def banner quo2.components.banners.banner.view/banner)
;;;; BUTTONS
(def button quo2.components.buttons.button/button)
(def dynamic-button quo2.components.buttons.dynamic-button/dynamic-button)
(def predictive-keyboard quo2.components.buttons.predictive-keyboard.view/view)
;;;; CARDS
(def small-option-card quo2.components.onboarding.small-option-card.view/small-option-card)
@@ -157,7 +151,6 @@
;;;; DRAWERS
(def action-drawer quo2.components.drawers.action-drawers.view/action-drawer)
(def documentation-drawers quo2.components.drawers.documentation-drawers.view/view)
(def drawer-buttons quo2.components.drawers.drawer-buttons.view/view)
(def permission-context quo2.components.drawers.permission-context.view/view)
@@ -191,7 +184,6 @@
;;;; RECORD AUDIO
(def record-audio quo2.components.record-audio.record-audio.view/record-audio)
(def soundtrack quo2.components.record-audio.soundtrack.view/f-soundtrack)
;;;; SETTINGS
(def privacy-option quo2.components.settings.privacy-option/card)
-2
View File
@@ -3,14 +3,12 @@
[quo2.components.avatars.user-avatar.component-spec]
[quo2.components.banners.banner.component-spec]
[quo2.components.buttons.--tests--.buttons-component-spec]
[quo2.components.buttons.predictive-keyboard.component-spec]
[quo2.components.colors.color-picker.component-spec]
[quo2.components.counter.--tests--.counter-component-spec]
[quo2.components.counter.step.component-spec]
[quo2.components.dividers.--tests--.divider-label-component-spec]
[quo2.components.dividers.strength-divider.component-spec]
[quo2.components.drawers.action-drawers.component-spec]
[quo2.components.drawers.documentation-drawers.component-spec]
[quo2.components.drawers.drawer-buttons.component-spec]
[quo2.components.drawers.permission-context.component-spec]
[quo2.components.inputs.input.component-spec]
+2 -5
View File
@@ -130,9 +130,6 @@
(when (exists? (.-NativeModules ^js react-native))
(.-RNSelectableTextInputManager ^js (.-NativeModules ^js react-native))))
;; TODO: iOS native implementation https://github.com/status-im/status-mobile/issues/14137
(defonce selectable-text-input
(if platform/android?
(reagent/adapt-react-class
(.requireNativeComponent ^js react-native "RNSelectableTextInput"))
view))
(reagent/adapt-react-class
(.requireNativeComponent ^js react-native "RNSelectableTextInput")))
+11 -13
View File
@@ -6,6 +6,7 @@
[status-im.chat.models.mentions :as mentions]
[status-im.chat.models.message :as chat.message]
[status-im.chat.models.message-content :as message-content]
[status-im2.config :as config]
[status-im2.constants :as constants]
[utils.re-frame :as rf]
[utils.i18n :as i18n]
@@ -34,12 +35,9 @@
"Set input text for current-chat. Takes db and input text and cofx
as arguments and returns new fx. Always clear all validation messages."
{:events [:chat.ui/set-chat-input-text]}
[{:keys [db] :as cofx} new-input chat-id]
[{db :db} new-input chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db))]
(rf/merge cofx
{:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))}
(when (empty? new-input)
(mentions/clear-mentions)))))
{:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))}))
(rf/defn set-input-content-height
{:events [:chat.ui/set-input-content-height]}
@@ -59,12 +57,6 @@
(let [current-chat-id (or chat-id (:current-chat-id db))]
{:db (assoc-in db [:chat/inputs current-chat-id :focused?] focused?)}))
(rf/defn set-input-audio
{:events [:chat.ui/set-input-audio]}
[{db :db} audio chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db))]
{:db (assoc-in db [:chat/inputs current-chat-id :audio] audio)}))
(rf/defn select-mention
{:events [:chat.ui/select-mention]}
[{:keys [db] :as cofx} text-input-ref {:keys [primary-name searched-text match public-key] :as user}]
@@ -113,7 +105,8 @@
(update-in [:chat/inputs current-chat-id :metadata]
dissoc
:sending-image))
:dispatch [:mention/to-input-field text current-chat-id]}))
:dispatch (when-not config/new-composer-enabled?
[:mention/to-input-field text current-chat-id])}))
(rf/defn show-contact-request-input
"Sets reference to previous chat message and focuses on input"
@@ -165,7 +158,10 @@
:image-path (utils/safe-replace resized-uri #"file://" "")
:image-width width
:image-height height
:text input-text
;; TODO: message not received if text field is
;; nil or empty, issue:
;; https://github.com/status-im/status-mobile/issues/14754
:text (or input-text "placeholder")
:response-to message-id})
images)))
@@ -185,6 +181,8 @@
[{:keys [db] :as cofx}]
(let [current-chat-id (:current-chat-id db)]
(rf/merge cofx
(when-not config/new-composer-enabled?
{:set-text-input-value [current-chat-id ""]})
(clean-input current-chat-id)
(mentions/clear-mentions))))
+18 -6
View File
@@ -98,9 +98,10 @@
[{:keys [db]} result]
(log/debug "[mentions] on-to-input-field-success" {:result result})
(let [{:keys [input-segments state chat-id new-text]} (transfer-mention-result result)]
{:db (-> db
(assoc-in [:chats/mentions chat-id :mentions] state)
(assoc-in [:chat/inputs-with-mentions chat-id] input-segments))}))
{:set-text-input-value [chat-id new-text]
:db (-> db
(assoc-in [:chats/mentions chat-id :mentions] state)
(assoc-in [:chat/inputs-with-mentions chat-id] input-segments))}))
(rf/defn on-change-text
{:events [:mention/on-change-text]}
@@ -161,11 +162,22 @@
:match match
:searched-text searched-text
:public-key public-key})
(let [{:keys [new-text chat-id]} (transfer-mention-result result)]
(let [{:keys [new-text state chat-id]} (transfer-mention-result result)
{:keys [at-sign-idx]} state
cursor (+ at-sign-idx (count primary-name) 2)]
(rf/merge
cofx
{:db (assoc-in db [:chats/mention-suggestions chat-id] nil)
:dispatch [:chat.ui/set-chat-input-text new-text chat-id]}
{:db (-> db
(assoc-in [:chats/mention-suggestions chat-id] nil))
:set-text-input-value [chat-id new-text text-input-ref]
:dispatch [:chat.ui/set-chat-input-text new-text chat-id]}
;; NOTE(rasom): Some keyboards do not react on selection property passed to
;; text input (specifically Samsung keyboard with predictive text set on).
;; In this case, if the user continues typing after the programmatic change,
;; the new text is added to the last known cursor position before
;; programmatic change. By calling `reset-text-input-cursor` we force the
;; keyboard's cursor position to be changed before the next input.
(reset-text-input-cursor text-input-ref cursor)
(recheck-at-idxs public-key))))
(rf/defn clear-suggestions
+4 -10
View File
@@ -91,16 +91,10 @@
:chat-type (.-chatType chat)
:unviewed-messages-count (.-unviewedMessagesCount chat)
:unviewed-mentions-count (.-unviewedMentionsCount chat)
:last-message {:content {:text (.-text chat)
:parsed-text (types/js->clj (.-parsedText chat))
:response-to (.-responseTo chat)}
:content-type (.-contentType chat)
:community-id (.-contentCommunityId chat)
:outgoing (boolean (.-outgoingStatus chat))
:album-images-count (.-albumImagesCount chat)
:from (.-from chat)
:deleted? (.-deleted chat)
:deleted-for-me? (.-deletedForMe chat)}
:last-message {:content {:text (.-text chat)
:parsed-text (types/js->clj (.-parsedText chat))}
:content-type (.-contentType chat)
:community-id (.-contentCommunityId chat)}
:last-clock-value (.-lastClockValue chat)
:profile-public-key (.-profile chat)
:highlight (.-highlight chat)
+1 -2
View File
@@ -42,8 +42,7 @@
:imageWidth :image-width
:imageHeight :image-height
:new :new?
:albumImagesCount :album-images-count
:displayName :display-name})
:albumImagesCount :album-images-count})
(update :quoted-message
set/rename-keys
+2 -4
View File
@@ -148,8 +148,7 @@
(rf/defn on-return-from-background
[{:keys [db now] :as cofx}]
(let [new-account? (get db :onboarding-2/new-account?)
app-in-background-since (get db :app-in-background-since)
(let [app-in-background-since (get db :app-in-background-since)
signed-up? (get-in db [:multiaccount :signed-up?])
biometric-auth? (= (:auth-method db) "biometric")
requires-bio-auth (and
@@ -162,8 +161,7 @@
{:db (dissoc db :app-in-background-since)}
(mailserver/process-next-messages-request)
(wallet/restart-wallet-service-after-background app-in-background-since)
(when-not new-account?
(universal-links/process-stored-event))
(universal-links/process-stored-event)
#(when-let [chat-id (:current-chat-id db)]
{:dispatch [:chat/mark-all-as-read chat-id]})
#(when requires-bio-auth
+8 -10
View File
@@ -45,13 +45,11 @@
{:events [::messenger-started]}
[{:keys [db] :as cofx} {:keys [mailservers] :as response}]
(log/info "Messenger started")
(let [new-account? (get db :onboarding-2/new-account?)]
(rf/merge cofx
{:db (-> db
(assoc :messenger/started? true)
(add-mailservers mailservers))}
(fetch-node-info-fx)
(pairing/init)
(stickers/load-packs)
(when-not new-account?
(universal-links/process-stored-event)))))
(rf/merge cofx
{:db (-> db
(assoc :messenger/started? true)
(add-mailservers mailservers))}
(fetch-node-info-fx)
(pairing/init)
(stickers/load-packs)
(universal-links/process-stored-event)))
+1 -1
View File
@@ -195,7 +195,7 @@
;;CHAT
{:name :start-a-new-chat
:options {:sheet? true}
:options {:sheet? true :insets {:top? true}}
:component new-chat-aio/contact-selection-list}
{:name :group-chat-profile
@@ -0,0 +1,25 @@
(ns status-im.ui2.screens.chat.components.edit.style)
(def container
{:flex-direction :row
:height 24})
(def content-container
{:padding-horizontal 10
:flex 1
:flex-direction :row})
(def icon-container
{:position :absolute
:left 10
:bottom -4
:width 16
:height 16})
(def text-container
{:position :absolute
:left 36
:right 54
:top 3
:flex-direction :row
:align-items :center})
@@ -0,0 +1,33 @@
(ns status-im.ui2.screens.chat.components.edit.view
(:require [utils.i18n :as i18n]
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
[re-frame.core :as rf]
[react-native.core :as rn]
[status-im.ui2.screens.chat.components.edit.style :as style]))
(defn edit-message
[on-cancel]
[rn/view
{:style style/container
:accessibility-label :edit-message}
[rn/view {:style style/content-container}
[quo/icon :i/connector
{:size 16
:color (colors/theme-colors colors/neutral-40 colors/neutral-60)
:container-style style/icon-container}]
[rn/view {:style style/text-container}
[quo/text
{:weight :medium
:size :paragraph-2}
(i18n/label :t/editing-message)]]]
[quo/button
{:width 24
:size 24
:accessibility-label :reply-cancel-button
:on-press #(do (on-cancel)
(rf/dispatch [:chat.ui/cancel-message-edit]))
:type :outline}
[quo/icon :i/close
{:size 16
:color (colors/theme-colors colors/neutral-100 colors/neutral-40)}]]])
@@ -0,0 +1,37 @@
(ns status-im.ui2.screens.chat.components.reply.style)
(defn reply-content
[pin?]
{:padding-horizontal (when-not pin? 10)
:flex 1
:flex-direction :row})
(defn quoted-message
[pin? in-chat-input?]
(merge {:flex-direction :row
:flex 1
:align-items :center
:width (if in-chat-input? "100%" "45%")}
(when-not pin?
{:left 22
:margin-right 22})))
(def reply-from
{:flex-direction :row
:align-items :center})
(def message-author-text
{:margin-left 4})
(def message-text
{:text-transform :none
:margin-left 4
:margin-top 2
:flex 1})
(def gradient
{:position :absolute
:right 0
:top 0
:bottom 0
:width "50%"})
@@ -0,0 +1,142 @@
(ns status-im.ui2.screens.chat.components.reply.view
(:require [clojure.string :as string]
[utils.i18n :as i18n]
[quo.react-native :as rn]
[quo2.components.buttons.button :as quo2.button]
[quo2.components.icon :as quo2.icon]
[quo2.components.markdown.text :as quo2.text]
[quo2.foundations.colors :as colors]
[status-im2.constants :as constants]
[status-im.ethereum.stateofus :as stateofus]
[status-im.ui.components.icons.icons :as icons]
[status-im2.contexts.chat.messages.avatar.view :as avatar]
[utils.re-frame :as rf]
[status-im.ui2.screens.chat.components.reply.style :as style]
[react-native.linear-gradient :as linear-gradient]))
(defn get-quoted-text-with-mentions
[parsed-text]
(string/join
(mapv (fn [{:keys [type literal children]}]
(cond
(= type "paragraph")
(get-quoted-text-with-mentions children)
(= type "mention")
(rf/sub [:messages/resolve-mention literal])
(seq children)
(get-quoted-text-with-mentions children)
:else
literal))
parsed-text)))
(defn format-author
[contact-name]
(let [author (if (or (= (aget contact-name 0) "@")
;; in case of replies
(= (aget contact-name 1) "@"))
(or (stateofus/username contact-name)
(subs contact-name 0 81))
contact-name)]
author))
(defn format-reply-author
[from username current-public-key]
(or (and (= from current-public-key)
(i18n/label :t/You))
(when username (format-author username))))
(defn reply-deleted-message
[]
[rn/view
{:style {:flex-direction :row
:align-items :center}}
[quo2.icon/icon :sad-face {:size 16}]
[quo2.text/text
{:number-of-lines 1
:size :label
:weight :regular
:accessibility-label :quoted-message
:style {:text-transform :none
:margin-left 4
:margin-top 2}}
(i18n/label :t/message-deleted)]])
(defn reply-from
[{:keys [from contact-name current-public-key]}]
[rn/view {:style style/reply-from}
[avatar/avatar from :xxxs]
[quo2.text/text
{:weight :semi-bold
:size :paragraph-2
:number-of-lines 1
:style style/message-author-text}
(format-reply-author from contact-name current-public-key)]])
(defn reply-message
[{:keys [from identicon content-type contentType parsed-text content deleted? deleted-for-me?
album-images-count]}
in-chat-input? pin? recording-audio?]
(let [contact-name (rf/sub [:contacts/contact-name-by-identity from])
current-public-key (rf/sub [:multiaccount/public-key])
content-type (or content-type contentType)]
[rn/view
{:style {:flex-direction :row
:height (when-not pin? 24)
:accessibility-label :reply-message}}
[rn/view {:style (style/reply-content pin?)}
(when-not pin?
;;TODO quo2 icon should be used
[icons/icon :main-icons/connector
{:color (colors/theme-colors colors/neutral-40 colors/neutral-60)
:container-style {:position :absolute :left 10 :bottom -4 :width 16 :height 16}}])
(if (or deleted? deleted-for-me?)
[rn/view {:style (style/quoted-message pin? in-chat-input?)}
[reply-deleted-message]]
[rn/view {:style (style/quoted-message pin? in-chat-input?)}
[reply-from
{:from from
:identicon identicon
:contact-name contact-name
:current-public-key current-public-key}]
[quo2.text/text
{:number-of-lines 1
:size :label
:weight :regular
:accessibility-label :quoted-message
:ellipsize-mode :tail
:style (merge
style/message-text
(when (or (= constants/content-type-image content-type)
(= constants/content-type-sticker content-type)
(= constants/content-type-audio content-type))
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}))}
(case (or content-type contentType)
constants/content-type-image (if album-images-count
(i18n/label :t/images-albums-count
{:album-images-count album-images-count})
(i18n/label :t/image))
constants/content-type-sticker (i18n/label :t/sticker)
constants/content-type-audio (i18n/label :t/audio)
(get-quoted-text-with-mentions (or parsed-text (:parsed-text content))))]])]
(when (and in-chat-input? (not recording-audio?))
[quo2.button/button
{:width 24
:size 24
:type :outline
:accessibility-label :reply-cancel-button
:on-press #(rf/dispatch [:chat.ui/cancel-message-reply])}
;;TODO quo2 icon should be used
[icons/icon :main-icons/close
{:width 16
:height 16
:color (colors/theme-colors colors/neutral-100 colors/neutral-40)}]])
(when (and in-chat-input? recording-audio?)
[linear-gradient/linear-gradient
{:colors [(colors/theme-colors colors/white-opa-0 colors/neutral-90-opa-0)
(colors/theme-colors colors/white colors/neutral-90)]
:start {:x 0 :y 0}
:end {:x 0.7 :y 0}
:style style/gradient}])]))
@@ -0,0 +1,5 @@
(ns status-im.ui2.screens.chat.composer.edit.style)
(def container
{:padding-horizontal 15
:padding-vertical 8})
@@ -0,0 +1,23 @@
(ns status-im.ui2.screens.chat.composer.edit.view
(:require [react-native.core :as rn]
[status-im.ui2.screens.chat.components.edit.view :as edit]
[status-im.ui2.screens.chat.composer.edit.style :as style]
[status-im.ui2.screens.chat.composer.input :as input]))
(defn- focus-input-on-edit
[edit had-edit text-input-ref]
;;when we show edit we focus input
(when-not (= edit @had-edit)
(reset! had-edit edit)
(when edit
;; A setTimeout of 0 is necessary to ensure the statement is enqueued and will get executed ASAP.
(js/setTimeout #(input/input-focus text-input-ref) 0))))
(defn edit-message-auto-focus-wrapper
[text-input-ref _]
(let [had-edit (atom nil)]
(fn [_ edit on-cancel]
(focus-input-on-edit edit had-edit text-input-ref)
(when edit
[rn/view {:style style/container}
[edit/edit-message on-cancel]]))))
@@ -0,0 +1,11 @@
(ns status-im.ui2.screens.chat.composer.images.style)
(def remove-photo-container
{:position :absolute
:top 5
:right 5})
(def small-image
{:width 56
:height 56
:border-radius 8})
@@ -0,0 +1,35 @@
(ns status-im.ui2.screens.chat.composer.images.view
(:require [quo2.core :as quo]
[quo2.foundations.colors :as colors]
[re-frame.core :as rf]
[react-native.core :as rn]
[status-im.ui2.screens.chat.composer.images.style :as style]))
(defn image
[item]
;; there is a bug on Android https://github.com/facebook/react-native/issues/12534
;; so we need some magic here with paddings so close button isn't cut
[rn/view {:padding-top 12 :padding-bottom 8 :padding-right 12}
[rn/image
{:source {:uri (:resized-uri (val item))}
:style style/small-image}]
[rn/touchable-opacity
{:on-press (fn [] (rf/dispatch [:chat.ui/image-unselected (val item)]))
:style style/remove-photo-container
:hit-slop {:right 5
:left 5
:top 10
:bottom 10}}
[quo/icon :i/clear
{:size 20
:color (colors/theme-colors colors/neutral-50 colors/neutral-60)}]]])
(defn images-list
[images]
(when (seq images)
[rn/flat-list
{:key-fn first
:render-fn image
:data images
:horizontal true
:keyboard-should-persist-taps :handled}]))
@@ -0,0 +1,383 @@
(ns status-im.ui2.screens.chat.composer.input
(:require [clojure.string :as string]
[oops.core :as oops]
[quo2.foundations.colors :as colors]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im2.constants :as chat.constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.transforms :as transforms]
[quo2.foundations.typography :as typography]
[react-native.background-timer :as background-timer]
[react-native.platform :as platform]
[react-native.core :as rn]
[react-native.clipboard :as clipboard]
[quo.react :as quo.react]))
(defonce input-texts (atom {}))
(defonce input-text-content-heights (atom {}))
(defonce mentions-enabled? (reagent/atom {}))
(defonce chat-input-key (reagent/atom 1))
(defonce text-input-ref (reagent/atom nil))
(defonce recording-audio? (reagent/atom false))
(defonce reviewing-audio? (reagent/atom false))
(defonce reviewing-audio-filepath (atom {}))
(defonce record-audio-permission-granted (reagent/atom false))
(defonce record-audio-reset-fn (atom nil))
(declare selectable-text-input)
(re-frame/reg-fx
:chat.ui/clear-inputs
(fn []
(reset! input-texts {})
(reset! input-text-content-heights {})
(reset! mentions-enabled? {})
(reset! chat-input-key 1)))
(defn input-focus
[text-input-ref]
(some-> ^js (quo.react/current-ref text-input-ref)
.focus))
(defn show-send
[{:keys [actions-ref send-ref sticker-ref record-ref]} chat-id]
(when (and (or @recording-audio?
(get @reviewing-audio-filepath chat-id))
@record-audio-reset-fn)
(@record-audio-reset-fn)
(swap! reviewing-audio-filepath dissoc chat-id))
(when actions-ref
(quo.react/set-native-props actions-ref #js {:width 0 :left -88}))
(quo.react/set-native-props send-ref #js {:width nil :right nil})
(quo.react/set-native-props record-ref #js {:right nil :left -1000})
(when sticker-ref
(quo.react/set-native-props sticker-ref #js {:width 0 :right -100})))
(defn hide-send
[{:keys [actions-ref send-ref sticker-ref record-ref]}]
(when actions-ref
(quo.react/set-native-props actions-ref #js {:width nil :left nil}))
(quo.react/set-native-props send-ref #js {:width 0 :right -100})
(quo.react/set-native-props record-ref #js {:left 0 :right 0})
(when sticker-ref
(quo.react/set-native-props sticker-ref #js {:width nil :right nil})))
(defn reset-input
[refs chat-id]
(some-> ^js (quo.react/current-ref (:text-input-ref refs))
.clear)
(swap! mentions-enabled? update :render not)
(swap! input-texts dissoc chat-id)
(swap! input-text-content-heights dissoc chat-id))
(defn clear-input
[chat-id refs]
(hide-send refs)
(if (get @mentions-enabled? chat-id)
(do
(swap! mentions-enabled? dissoc chat-id)
;;we need this timeout, because if we clear text input and first index was a mention object with
;;blue color,
;;after clearing text will be typed with this blue color, so we render white text first and then
;;clear it
(js/setTimeout #(reset-input refs chat-id) 50))
(reset-input refs chat-id)))
(defn on-text-change
[val chat-id]
(swap! input-texts assoc chat-id val)
;;we still store it in app-db for mentions, we don't have reactions in views
(rf/dispatch [:chat.ui/set-chat-input-text val]))
(defn on-selection-change
[timeout-id last-text-change args]
(let [selection (.-selection ^js (.-nativeEvent ^js args))
start (.-start selection)
end (.-end selection)]
;; NOTE(rasom): on iOS we do not dispatch this event immediately
;; because it is needed only in case if selection is changed without
;; typing. Timeout might be canceled on `on-change`.
(when platform/ios?
(reset!
timeout-id
(background-timer/set-timeout
#(rf/dispatch [:mention/on-selection-change
{:start start
:end end}])
50)))
;; NOTE(rasom): on Android we dispatch event only in case if there
;; was no text changes during last 50ms. `on-selection-change` is
;; dispatched after `on-change`, that's why there is no another way
;; to know whether selection was changed without typing.
(when (and platform/android?
(or (not @last-text-change)
(< 50 (- (js/Date.now) @last-text-change))))
(rf/dispatch [:mention/on-selection-change
{:start start
:end end}]))))
(defn on-change
[last-text-change timeout-id refs chat-id sending-image args]
(let [text (.-text ^js (.-nativeEvent ^js args))
prev-text (get @input-texts chat-id)]
(when (and (seq prev-text) (empty? text) (not sending-image))
(hide-send refs))
(when (and (empty? prev-text) (or (seq text) sending-image))
(show-send refs chat-id)
(reset! recording-audio? false)
(swap! reviewing-audio-filepath dissoc chat-id))
(when (and (not (get @mentions-enabled? chat-id)) (string/index-of text "@"))
(swap! mentions-enabled? assoc chat-id true))
;; NOTE(rasom): on iOS `on-selection-change` is canceled in case if it
;; happens during typing because it is not needed for mention
;; suggestions calculation
(when (and platform/ios? @timeout-id)
(background-timer/clear-timeout @timeout-id))
(when platform/android?
(reset! last-text-change (js/Date.now)))
(on-text-change text chat-id)
(rf/dispatch [:mention/on-change-text text])))
(defn on-text-input
[chat-id args]
(let [native-event (.-nativeEvent ^js args)
text (.-text ^js native-event)]
(when (and (not (get @mentions-enabled? chat-id)) (string/index-of text "@"))
(swap! mentions-enabled? assoc chat-id true))))
(defn text-input-style
[chat-id]
(merge typography/font-regular
typography/paragraph-1
{:flex 1
:min-height 34
:margin 0
:flex-shrink 1
:color (colors/theme-colors colors/neutral-100 colors/white)
:margin-horizontal 20}
(if platform/android?
{:padding-vertical 8
:text-align-vertical :top}
{:margin-top 8
:margin-bottom 8})
(when (or @recording-audio? (get @reviewing-audio-filepath chat-id))
{:display :none})))
(defn text-input
[{:keys [refs chat-id sending-image on-content-size-change]}]
(let [cooldown-enabled? (rf/sub [:chats/current-chat-cooldown-enabled?])
timeout-id (reagent/atom nil)
last-text-change (reagent/atom nil)
mentions-enabled? (get @mentions-enabled? chat-id)
props
{:style (text-input-style chat-id)
:ref (:text-input-ref refs)
:max-font-size-multiplier 1
:accessibility-label :chat-message-input
:text-align-vertical :center
:multiline true
:editable (not cooldown-enabled?)
:blur-on-submit false
:auto-focus false
:max-length chat.constants/max-text-size
:placeholder-text-color (colors/theme-colors colors/neutral-40 colors/white-opa-30)
:placeholder (if cooldown-enabled?
(i18n/label :cooldown/text-input-disabled)
(i18n/label :t/type-a-message))
:default-value (get @input-texts chat-id)
:underline-color-android :transparent
:auto-capitalize :sentences
:auto-correct false
:spell-check false
:on-content-size-change on-content-size-change
:on-selection-change (partial on-selection-change
timeout-id
last-text-change)
:on-change
(partial on-change last-text-change timeout-id refs chat-id sending-image)
:on-text-input (partial on-text-input chat-id)}
input-with-mentions (rf/sub [:chat/input-with-mentions])
children (fn []
(if mentions-enabled?
(map-indexed
(fn [index [mention-type text]]
^{:key (str index "_" mention-type "_" text)}
[rn/text (when (= mention-type :mention) {:style {:color colors/primary-50}})
text])
input-with-mentions)
(get @input-texts chat-id)))]
(reset! text-input-ref (:text-input-ref refs))
;when ios implementation for selectable-text-input is ready, we need remove this condition and use
;selectable-text-input directly.
(if platform/android?
[selectable-text-input chat-id props children]
[rn/text-input props
children])))
(declare first-level-menu-items second-level-menu-items)
(defn update-input-text
[{:keys [text-input chat-id]} text]
(on-text-change text chat-id)
(.setNativeProps ^js text-input (clj->js {:text text})))
(re-frame/reg-fx
:set-text-input-value
(fn [[chat-id text local-text-input-ref]]
(when local-text-input-ref
(reset! text-input-ref local-text-input-ref))
(on-text-change text chat-id)
(if platform/ios?
(.setNativeProps ^js (quo.react/current-ref @text-input-ref) (clj->js {:text text}))
(if (string/blank? text)
(.clear ^js (quo.react/current-ref @text-input-ref))
(.setNativeProps ^js (quo.react/current-ref @text-input-ref) (clj->js {:text text}))))))
(defn calculate-input-text
[{:keys [full-text selection-start selection-end]} content]
(let [head (subs full-text 0 selection-start)
tail (subs full-text selection-end)]
(str head content tail)))
(defn update-selection
[text-input-handle selection-start selection-end]
;to avoid something disgusting like this
;https://lightrun.com/answers/facebook-react-native-textinput-controlled-selection-broken-on-both-ios-and-android
;use native invoke instead! do not use setNativeProps! e.g. (.setNativeProps ^js text-input (clj->js
;{:selection {:start selection-start :end selection-end}}))
(let [manager (rn/selectable-text-input-manager)]
(oops/ocall manager :setSelection text-input-handle selection-start selection-end)))
(def first-level-menus
{:cut (fn [{:keys [content] :as params}]
(let [new-text (calculate-input-text params "")]
(clipboard/set-string content)
(update-input-text params new-text)))
:copy-to-clipboard (fn [{:keys [content]}]
(clipboard/set-string content))
:paste (fn [params]
(let [callback (fn [paste-content]
(let [content (string/trim paste-content)
new-text (calculate-input-text params content)]
(update-input-text params new-text)))]
(clipboard/get-string callback)))
:biu (fn [{:keys [first-level text-input-handle menu-items selection-start
selection-end]}]
(reset! first-level false)
(reset! menu-items second-level-menu-items)
(update-selection text-input-handle selection-start selection-end))})
(def first-level-menu-items (map i18n/label (keys first-level-menus)))
(defn reset-to-first-level-menu
[first-level menu-items]
(reset! first-level true)
(reset! menu-items first-level-menu-items))
(defn append-markdown-char
[{:keys [first-level menu-items content selection-start selection-end text-input-handle
selection-event]
:as params} wrap-chars]
(let [content (str wrap-chars content wrap-chars)
new-text (calculate-input-text params content)
len-wrap-chars (count wrap-chars)
selection-start (+ selection-start len-wrap-chars)
selection-end (+ selection-end len-wrap-chars)]
;don't update selection directly here, process it within on-selection-change instead
;so that we can avoid java.lang.IndexOutOfBoundsException: setSpan..
(reset! selection-event {:start selection-start
:end selection-end
:text-input-handle text-input-handle})
(update-input-text params new-text)
(reset-to-first-level-menu first-level menu-items)))
(def second-level-menus
{:bold #(append-markdown-char % "**")
:italic #(append-markdown-char % "*")
:strikethrough #(append-markdown-char % "~~")})
(def second-level-menu-items (map i18n/label (keys second-level-menus)))
(defn on-menu-item-touched
[{:keys [first-level event-type] :as params}]
(let [menus (if @first-level first-level-menus second-level-menus)
menu-item-key (nth (keys menus) event-type)
action (get menus menu-item-key)]
(action params)))
(defn selectable-text-input
[_ _ _]
(let [text-input-ref (reagent/atom nil)
menu-items (reagent/atom first-level-menu-items)
first-level (reagent/atom true)
selection-event (atom nil)
manager (rn/selectable-text-input-manager)]
(reagent/create-class
{:component-did-mount
(fn [this]
(when @text-input-ref
(let [selectable-text-input-handle (rn/find-node-handle this)
text-input-handle (rn/find-node-handle @text-input-ref)]
(oops/ocall manager :setupMenuItems selectable-text-input-handle text-input-handle))))
:component-did-update (fn [_ _ _ _]
(when (not @first-level)
(let [text-input-handle (rn/find-node-handle @text-input-ref)]
(oops/ocall manager :startActionMode text-input-handle))))
:reagent-render
(fn [chat-id {:keys [style ref on-selection-change] :as props} children]
(let [ref #(do (reset! text-input-ref %)
(when ref
(quo.react/set-ref-val! ref %)))
on-selection-change (fn [args]
(let [selection (.-selection ^js (.-nativeEvent ^js args))
start (.-start selection)
end (.-end selection)
no-selection (<= (- end start) 0)]
(when (and no-selection (not @first-level))
(oops/ocall manager :hideLastActionMode)
(reset-to-first-level-menu first-level menu-items)))
(when on-selection-change
(on-selection-change args))
(when @selection-event
(let [{:keys [start end text-input-handle]} @selection-event]
(update-selection text-input-handle start end)
(reset! selection-event nil))))
on-selection
(fn [^js event]
(let [native-event (.-nativeEvent event)
native-event (transforms/js->clj native-event)
{:keys [eventType content selectionStart
selectionEnd]} native-event
full-text (:input-text (rf/sub [:chats/current-chat-input]))]
(on-menu-item-touched {:first-level first-level
:event-type eventType
:content content
:selection-start selectionStart
:selection-end selectionEnd
:text-input @text-input-ref
:text-input-handle (rn/find-node-handle @text-input-ref)
:full-text full-text
:menu-items menu-items
:chat-id chat-id
:selection-event selection-event})))
props (merge props
{:ref ref
:style (dissoc style :margin-horizontal)
:on-selection-change on-selection-change
:on-selection on-selection})]
[rn/selectable-text-input {:menuItems @menu-items :style style}
[rn/text-input props
children]]))})))
@@ -0,0 +1,2 @@
(ns status-im.ui2.screens.chat.composer.mentions)
@@ -0,0 +1,37 @@
(ns status-im.ui2.screens.chat.composer.reply
(:require [quo.react-native :as rn]
[status-im.ui2.screens.chat.components.reply.view :as reply]
[status-im.ui2.screens.chat.composer.input :as input]))
(defn focus-input-on-reply
[reply had-reply text-input-ref]
;;when we show reply we focus input
(when-not (= reply @had-reply)
(reset! had-reply reply)
(when reply
;; A setTimeout of 0 is necessary to ensure the statement is enqueued and will get executed ASAP.
(js/setTimeout #(input/input-focus text-input-ref) 0))))
(defn reply-message-auto-focus-wrapper
[text-input-ref _]
(let [had-reply (atom nil)]
(fn [_ reply]
(focus-input-on-reply reply had-reply text-input-ref)
(when reply
[rn/view
{:style (merge
{:padding-horizontal 15
:padding-vertical 8}
(when @input/recording-audio?
{:position :absolute
:top 12
:left 0
:right 0
;;When recording an audio and replying at the same time,
;;text input is overlapped by the reply component but
;;text input still have priority over touches, so we need
;;to force the reply component to receive the touches in this
;;scenario, thus we increase its z-index
:z-index 1}))}
[reply/reply-message reply true false
(and @input/recording-audio? (not @input/reviewing-audio?))]]))))
@@ -18,6 +18,7 @@
[status-im.ui.screens.chat.styles.message.message :as style]
[status-im.ui.screens.chat.utils :as chat.utils]
[status-im.ui.screens.communities.icon :as communities.icon]
[status-im.ui2.screens.chat.components.reply.view :as components.reply]
[status-im.utils.utils :as utils]
[status-im2.constants :as constants]
[status-im2.contexts.chat.home.chat-list-item.view :as home.chat-list-item]
@@ -143,8 +144,9 @@
(:parsed-text content))))
(defn quoted-message
[_ pin?]
[rn/view {:style (when-not pin? (style/quoted-message-container))}])
[quoted-message pin?]
[rn/view {:style (when-not pin? (style/quoted-message-container))}
[components.reply/reply-message quoted-message false pin?]])
(defn message-not-sent-text
[chat-id message-id]
@@ -184,7 +184,6 @@
(rf/defn process-stored-event
"Return an event description for processing a url if in the database"
{:events [:universal-links/process-stored-event]}
[{:keys [db] :as cofx}]
(when-let [url (:universal-links/url db)]
(rf/merge cofx
+2 -13
View File
@@ -12,7 +12,7 @@
:margin-vertical 8})
(defn sheet
[{:keys [top bottom]} window-height override-theme shell?]
[{:keys [top bottom]} window-height override-theme]
{:position :absolute
:max-height (- window-height top 20)
:z-index 1
@@ -21,20 +21,9 @@
:right 0
:border-top-left-radius 20
:border-top-right-radius 20
:overflow :hidden
:flex 1
:padding-bottom (max 20 bottom)
:background-color (if shell?
:transparent
(colors/theme-colors colors/white colors/neutral-90 override-theme))})
(def shell-bg
{:position :absolute
:background-color colors/white-opa-5
:left 0
:right 0
:top 0
:bottom 0})
:background-color (colors/theme-colors colors/white colors/neutral-90 override-theme)})
(defn selected-item
[override-theme]
+3 -8
View File
@@ -6,8 +6,7 @@
[status-im2.common.bottom-sheet.styles :as styles]
[react-native.gesture :as gesture]
[oops.core :as oops]
[react-native.hooks :as hooks]
[react-native.blur :as blur]))
[react-native.hooks :as hooks]))
(def duration 450)
(def timing-options #js {:duration duration})
@@ -50,7 +49,7 @@
(hide translate-y bg-opacity window-height))))))
(defn view
[{:keys [hide? insets]} {:keys [content override-theme selected-item shell?]}]
[{:keys [hide? insets]} {:keys [content override-theme selected-item]}]
(let [{window-height :height} (rn/get-window)
bg-opacity (reanimated/use-shared-value 0)
translate-y (reanimated/use-shared-value window-height)
@@ -70,11 +69,7 @@
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]}
(styles/sheet insets window-height override-theme shell?))}
(when shell?
[blur/view
{:style styles/shell-bg}])
(styles/sheet insets window-height override-theme))}
(when selected-item
[rn/view
+2
View File
@@ -158,3 +158,5 @@
["enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@test.waku.nodes.status.im"]})
(def default-kdf-iterations 3200)
(def ^:const new-composer-enabled? false)
+1
View File
@@ -263,6 +263,7 @@
2 5000
3 10000})
(def ^:const max-text-size 4096)
;; any message that comes after this amount of ms will be grouped separately
(def ^:const group-ms 300000)
@@ -1,8 +1,8 @@
(ns status-im2.contexts.chat.composer.actions.style
(ns status-im2.contexts.chat.bottom-sheet-composer.actions.style
(:require
[quo2.foundations.colors :as colors]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.composer.constants :as constants]))
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]))
(def actions-container
{:height constants/actions-container-height
@@ -18,17 +18,4 @@
{:position :absolute
:right 0
:z-index z-index
:background-color (colors/theme-colors colors/white colors/neutral-95)
:padding-vertical 3
:padding-left 2}))
(defn record-audio-container
[]
{:align-items :center
:background-color :transparent
:flex-direction :row
:position :absolute
:left -20
:right -20
:bottom 0
:height constants/composer-default-height})
:background-color (colors/theme-colors colors/white colors/neutral-95)}))
@@ -0,0 +1,140 @@
(ns status-im2.contexts.chat.bottom-sheet-composer.actions.view
(:require
[quo2.core :as quo]
[react-native.core :as rn]
[react-native.permissions :as permissions]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[status-im2.common.alert.events :as alert]
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]
[status-im2.contexts.chat.messages.list.view :as messages.list]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[status-im2.contexts.chat.bottom-sheet-composer.actions.style :as style]))
(defn send-message
[{:keys [text-value focused? maximized?]}
{:keys [height saved-height last-height opacity background-y container-opacity]}
window-height]
(reanimated/animate height constants/input-height)
(reanimated/set-shared-value saved-height constants/input-height)
(reanimated/set-shared-value last-height constants/input-height)
(reanimated/animate opacity 0)
(when-not @focused?
(js/setTimeout #(reanimated/animate container-opacity constants/empty-opacity) 300))
(js/setTimeout #(reanimated/set-shared-value background-y
(- window-height))
300)
(rf/dispatch [:chat.ui/send-current-message])
(rf/dispatch [:chat.ui/set-input-maximized false])
(rf/dispatch [:chat.ui/set-input-content-height constants/input-height])
(rf/dispatch [:chat.ui/set-chat-input-text nil])
(reset! maximized? false)
(reset! text-value "")
(messages.list/scroll-to-bottom))
(defn send-button
[{:keys [text-value] :as state}
animations
window-height
images?]
[:f>
(fn []
(let [btn-opacity (reanimated/use-shared-value 0)
z-index (reagent/atom 0)]
[:f>
(fn []
(rn/use-effect (fn []
(if (or (not-empty @text-value) images?)
(when-not (= @z-index 1)
(reset! z-index 1)
(js/setTimeout #(reanimated/animate btn-opacity 1) 50))
(when-not (= @z-index 0)
(reanimated/animate btn-opacity 0)
(js/setTimeout #(reset! z-index 0) 300))))
[(and (empty? @text-value) (not images?))])
[reanimated/view
{:style (style/send-button btn-opacity @z-index)}
[quo/button
{:icon true
:size 32
:accessibility-label :send-message-button
:on-press #(send-message state animations window-height)}
:i/arrow-up]])]))])
(defn audio-button
[]
[quo/button
{:on-press #(js/alert "to be added")
:icon true
:type :outline
:size 32}
:i/audio])
(defn camera-button
[]
[quo/button
{:on-press #(js/alert "to be implemented")
:icon true
:type :outline
:size 32
:style {:margin-right 12}}
:i/camera])
(defn open-photo-selector
[{:keys [input-ref]}
{:keys [height]}
insets]
(permissions/request-permissions
{:permissions [:read-external-storage :write-external-storage]
:on-allowed (fn []
(when (and platform/android? @input-ref)
(.blur ^js @input-ref))
(rf/dispatch [:chat.ui/set-input-content-height
(reanimated/get-shared-value height)])
(rf/dispatch [:open-modal :photo-selector {:insets insets}]))
:on-denied (fn []
(alert/show-popup (i18n/label :t/error)
(i18n/label
:t/external-storage-denied)))}))
(defn image-button
[props animations insets]
[quo/button
{:on-press #(open-photo-selector props animations insets)
:icon true
:type :outline
:size 32
:style {:margin-right 12}}
:i/image])
(defn reaction-button
[]
[quo/button
{:on-press #(js/alert "to be implemented")
:icon true
:type :outline
:size 32
:style {:margin-right 12}}
:i/reaction])
(defn format-button
[]
[quo/button
{:on-press #(js/alert "to be implemented")
:icon true
:type :outline
:size 32}
:i/format])
(defn view
[props state animations window-height insets images?]
[rn/view {:style style/actions-container}
[rn/view {:style {:flex-direction :row}}
[camera-button]
[image-button props animations insets]
[reaction-button]
[format-button]]
[send-button state animations window-height images?]
[audio-button]])
@@ -1,4 +1,4 @@
(ns status-im2.contexts.chat.composer.constants
(ns status-im2.contexts.chat.bottom-sheet-composer.constants
(:require
[quo2.foundations.typography :as typography]
[react-native.platform :as platform]))
@@ -21,18 +21,14 @@
(def ^:const edit-container-height 32)
(def ^:const mentions-max-height 240)
(def ^:const extra-content-offset (if platform/ios? 6 0))
(def ^:const content-change-threshold 10)
(def ^:const drag-threshold 30)
(def ^:const velocity-threshold (if platform/ios? -1000 -500))
(def ^:const velocity-threshold -1000)
(def ^:const background-threshold 0.75)
(def ^:const line-height (:line-height typography/paragraph-1))
(def ^:const max-text-size 4096)
@@ -1,4 +1,4 @@
(ns status-im2.contexts.chat.composer.edit.style)
(ns status-im2.contexts.chat.bottom-sheet-composer.edit.style)
(def container
{:flex-direction :row
@@ -1,17 +1,16 @@
(ns status-im2.contexts.chat.composer.edit.view
(ns status-im2.contexts.chat.bottom-sheet-composer.edit.view
(:require
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.composer.constants :as constants]
[status-im2.contexts.chat.composer.utils :as utils]
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]
[utils.i18n :as i18n]
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
[re-frame.core :as rf]
[react-native.core :as rn]
[status-im2.contexts.chat.composer.edit.style :as style]
[utils.re-frame :as rf]))
[status-im2.contexts.chat.bottom-sheet-composer.edit.style :as style]))
(defn edit-message
[state]
[cancel]
[rn/view
{:style style/container
:accessibility-label :edit-message}
@@ -31,7 +30,7 @@
:size 24
:accessibility-label :edit-cancel-button
:on-press (fn []
(utils/cancel-edit-message state)
(cancel)
(rf/dispatch [:chat.ui/cancel-message-edit]))
:type :outline}
[quo/icon :i/close
@@ -39,13 +38,12 @@
:color (colors/theme-colors colors/neutral-100 colors/neutral-40)}]]])
(defn- f-view
[state]
(let [edit (rf/sub [:chats/edit-message])
height (reanimated/use-shared-value (if edit constants/edit-container-height 0))]
[edit cancel]
(let [height (reanimated/use-shared-value (if edit constants/edit-container-height 0))]
(rn/use-effect #(reanimated/animate height (if edit constants/edit-container-height 0)) [edit])
[reanimated/view {:style (reanimated/apply-animations-to-style {:height height} {})}
(when edit [edit-message state])]))
(when edit [edit-message cancel])]))
(defn view
[state]
[:f> f-view state])
[edit cancel]
[:f> f-view edit cancel])
@@ -0,0 +1,110 @@
(ns status-im2.contexts.chat.bottom-sheet-composer.effects
(:require
[status-im.async-storage.core :as async-storage]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]
[status-im2.contexts.chat.bottom-sheet-composer.keyboard :as kb]
[utils.number :as utils.number]))
(defn reenter-screen-effect
[{:keys [text-value saved-cursor-position maximized?]}
{:keys [content-height]}
{:keys [input-content-height input-text input-maximized?]}]
(when (and (empty? @text-value) (not= input-text nil))
(reset! text-value input-text)
(reset! content-height input-content-height)
(reset! saved-cursor-position (count input-text)))
(when input-maximized?
(reset! maximized? true)))
(defn maximized-effect
[{:keys [maximized?]}
{:keys [height saved-height last-height]}
{:keys [max-height]}
{:keys [input-content-height]}]
(when (or @maximized? (>= input-content-height max-height))
(reanimated/animate height max-height)
(reanimated/set-shared-value saved-height max-height)
(reanimated/set-shared-value last-height max-height)))
(defn layout-effect
[{:keys [lock-layout?]}]
(when-not @lock-layout?
(js/setTimeout #(reset! lock-layout? true) 500)))
(defn kb-default-height-effect
[{:keys [kb-default-height]}]
(when-not @kb-default-height
(async-storage/get-item :kb-default-height
#(reset! kb-default-height (utils.number/parse-int % nil)))))
(defn background-effect
[{:keys [maximized?]}
{:keys [opacity background-y]}
{:keys [max-height]}
{:keys [input-content-height]}]
(when (or @maximized? (>= input-content-height (* max-height constants/background-threshold)))
(reanimated/set-shared-value background-y 0)
(reanimated/animate opacity 1)))
(defn images-or-reply-effect
[{:keys [container-opacity]}
{:keys [replying? sending-images? input-ref]}
images? reply?]
(when (or images? reply?)
(reanimated/animate container-opacity 1))
(when (and (not @sending-images?) images? @input-ref)
(.focus ^js @input-ref)
(reset! sending-images? true))
(when (and (not @replying?) reply? @input-ref)
(.focus ^js @input-ref)
(reset! replying? true))
(when-not images?
(reset! sending-images? false))
(when-not reply?
(reset! replying? false)))
(defn edit-effect
[{:keys [text-value saved-cursor-position]}
{:keys [editing? input-ref]}
edit]
(let [edit-text (get-in edit [:content :text])]
(when (and (not @editing?) edit @input-ref)
(.focus ^js @input-ref)
(reset! editing? true)
(reset! text-value edit-text)
(reset! saved-cursor-position (count edit-text)))
(when-not edit-text
(reset! editing? false))))
(defn empty-effect
[{:keys [text-value maximized? focused?]}
{:keys [container-opacity]}
images?
reply?]
(when (and (empty? @text-value) (not images?) (not reply?) (not @maximized?) (not @focused?))
(reanimated/animate-delay container-opacity constants/empty-opacity 200)))
(defn component-will-unmount
[{:keys [keyboard-show-listener keyboard-hide-listener keyboard-frame-listener]}]
(.remove ^js @keyboard-show-listener)
(.remove ^js @keyboard-hide-listener)
(.remove ^js @keyboard-frame-listener))
(defn initialize
[props state animations {:keys [max-height] :as dimensions} chat-input keyboard-height images? reply?
edit]
(rn/use-effect
(fn []
(maximized-effect state animations dimensions chat-input)
(reenter-screen-effect state dimensions chat-input)
(layout-effect state)
(kb-default-height-effect state)
(background-effect state animations dimensions chat-input)
(images-or-reply-effect animations props images? reply?)
(edit-effect state props edit)
(empty-effect state animations images? reply?)
(kb/add-kb-listeners props state animations dimensions keyboard-height)
#(component-will-unmount props))
[max-height]))
@@ -1,10 +1,10 @@
(ns status-im2.contexts.chat.composer.gesture
(ns status-im2.contexts.chat.bottom-sheet-composer.gesture
(:require
[react-native.gesture :as gesture]
[react-native.reanimated :as reanimated]
[oops.core :as oops]
[status-im2.contexts.chat.composer.constants :as constants]
[status-im2.contexts.chat.composer.utils :as utils]
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]
[status-im2.contexts.chat.bottom-sheet-composer.utils :as utils]
[utils.re-frame :as rf]))
(defn set-opacity
@@ -35,12 +35,10 @@
(rf/dispatch [:chat.ui/set-input-maximized true]))
(defn minimize
[{:keys [input-ref emoji-kb-extra-height saved-emoji-kb-extra-height]}
{:keys [maximized?]}]
[{:keys [input-ref emoji-kb-extra-height saved-emoji-kb-extra-height]}]
(when @emoji-kb-extra-height
(reset! saved-emoji-kb-extra-height @emoji-kb-extra-height)
(reset! emoji-kb-extra-height nil))
(reset! maximized? false)
(rf/dispatch [:chat.ui/set-input-maximized false])
(when @input-ref
(.blur ^js @input-ref)))
@@ -56,7 +54,7 @@
(defn drag-gesture
[{:keys [input-ref] :as props}
{:keys [gesture-enabled? text-value] :as state}
{:keys [gesture-enabled?] :as state}
{:keys [height saved-height last-height opacity background-y container-opacity] :as animations}
{:keys [max-height lines] :as dimensions}
keyboard-shown]
@@ -67,7 +65,6 @@
(gesture/on-start (fn [event]
(if-not keyboard-shown
(do ; focus and end
(reset! text-value (str (oops/oget event "velocityY")))
(when (< (oops/oget event "velocityY") constants/velocity-threshold)
(reanimated/set-shared-value container-opacity 1)
(reanimated/set-shared-value last-height max-height))
@@ -77,26 +74,21 @@
(do ; else, will start gesture
(reanimated/set-shared-value background-y 0)
(reset! expanding? (neg? (oops/oget event "velocityY")))))))
(gesture/on-update
(fn [event]
(let [translation (oops/oget event "translationY")
min-height (utils/get-min-height lines)
new-height (- (reanimated/get-shared-value saved-height) translation)
bounded-height (utils/bounded-val new-height min-height max-height)]
(when keyboard-shown
(if (>= new-height min-height)
(do ; expand sheet
(reanimated/set-shared-value height bounded-height)
(set-opacity (oops/oget event "velocityY")
opacity
translation
@expanding?
min-height
max-height
bounded-height
saved-height))
(when @input-ref ; sheet at min-height, collapse keyboard
(.blur ^js @input-ref)))))))
(gesture/on-update (fn [event]
(let [translation (oops/oget event "translationY")
min-height (utils/get-min-height lines)
new-height (- (reanimated/get-shared-value saved-height) translation)
new-height (utils/bounded-val new-height min-height max-height)]
(when keyboard-shown
(reanimated/set-shared-value height new-height)
(set-opacity (oops/oget event "velocityY")
opacity
translation
@expanding?
min-height
max-height
new-height
saved-height)))))
(gesture/on-end (fn []
(let [diff (- (reanimated/get-shared-value height)
(reanimated/get-shared-value saved-height))]
@@ -106,8 +98,6 @@
(maximize state animations dimensions)
(bounce-back animations dimensions starting-opacity))
(if (> (Math/abs diff) constants/drag-threshold)
(minimize props state)
(minimize props)
(bounce-back animations dimensions starting-opacity)))
(reset! gesture-enabled? true))))))))
@@ -1,4 +1,4 @@
(ns status-im2.contexts.chat.composer.gradients.style
(ns status-im2.contexts.chat.bottom-sheet-composer.gradients.style
(:require
[quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]
@@ -0,0 +1,23 @@
(ns status-im2.contexts.chat.bottom-sheet-composer.gradients.view
(:require
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.bottom-sheet-composer.gradients.style :as style]))
(defn view
[{:keys [input-ref]}
{:keys [gradient-z-index]}
{:keys [gradient-opacity]}
show-bottom-gradient?]
[:f>
(fn []
[:<>
[reanimated/linear-gradient (style/top-gradient gradient-opacity @gradient-z-index)]
(when show-bottom-gradient?
[rn/touchable-without-feedback
{:on-press #(when @input-ref (.focus ^js @input-ref))
:accessibility-label :bottom-gradient}
[linear-gradient/linear-gradient (style/bottom-gradient)]])])])
@@ -1,14 +1,11 @@
(ns status-im2.contexts.chat.composer.handlers
(:require
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[oops.core :as oops]
[status-im2.contexts.chat.composer.constants :as constants]
[status-im2.contexts.chat.composer.keyboard :as kb]
[status-im2.contexts.chat.composer.utils :as utils]
[status-im2.contexts.chat.composer.selection :as selection]
[utils.re-frame :as rf]))
(ns status-im2.contexts.chat.bottom-sheet-composer.handlers
(:require [react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[oops.core :as oops]
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]
[status-im2.contexts.chat.bottom-sheet-composer.keyboard :as kb]
[status-im2.contexts.chat.bottom-sheet-composer.utils :as utils]
[utils.re-frame :as rf]))
(defn focus
[{:keys [input-ref] :as props}
@@ -49,15 +46,14 @@
(reanimated/set-shared-value saved-height min-height)
(reanimated/animate opacity 0)
(js/setTimeout #(reanimated/set-shared-value background-y (- window-height)) 300)
(when (utils/empty-input? @text-value images reply nil)
(when (utils/empty-input? @text-value images reply)
(reanimated/animate container-opacity constants/empty-opacity))
(reanimated/animate gradient-opacity 0)
(reset! lock-selection? true)
(reset! saved-cursor-position @cursor-position)
(reset! gradient-z-index (if (= (reanimated/get-shared-value gradient-opacity) 1) -1 0))
(when (not= reopen-height max-height)
(reset! maximized? false)
(rf/dispatch [:chat.ui/set-input-maximized false]))))
(reset! maximized? false))))
(defn content-size-change
[event
@@ -74,8 +70,7 @@
(reanimated/animate height new-height)
(reanimated/set-shared-value saved-height new-height))
(when (= new-height max-height)
(reset! maximized? true)
(rf/dispatch [:chat.ui/set-input-maximized true]))
(reset! maximized? true))
(if (utils/show-background? saved-height max-height new-height)
(do
(reanimated/set-shared-value background-y 0)
@@ -87,12 +82,10 @@
(defn scroll
[event
{:keys [scroll-y]}
{:keys [gradient-z-index focused?]}
{:keys [gradient-opacity]}
{:keys [lines max-lines]}]
(let [y (oops/oget event "nativeEvent.contentOffset.y")]
(reset! scroll-y y)
(when (utils/show-top-gradient? y lines max-lines gradient-opacity focused?)
(reset! gradient-z-index 1)
(js/setTimeout #(reanimated/animate gradient-opacity 1) 0))
@@ -102,38 +95,19 @@
(defn change-text
[text
{:keys [input-ref record-reset-fn]}
{:keys [text-value cursor-position recording?]}]
{:keys [input-ref]}
{:keys [text-value cursor-position]}]
(reset! text-value text)
(reagent/next-tick #(when @input-ref
(.setNativeProps ^js @input-ref
(clj->js {:selection {:start @cursor-position
:end @cursor-position}}))))
(when @recording?
(@record-reset-fn)
(reset! recording? false))
(rf/dispatch [:chat.ui/set-chat-input-text text])
(rf/dispatch [:mention/on-change-text text]))
(rf/dispatch [:chat.ui/set-chat-input-text text]))
(defn selection-change
[event
{:keys [input-ref selection-event selection-manager]}
{:keys [lock-selection? cursor-position first-level? menu-items]}]
(let [start (oops/oget event "nativeEvent.selection.start")
end (oops/oget event "nativeEvent.selection.end")
selection? (not= start end)
text-input-handle (rn/find-node-handle @input-ref)]
(when-not @lock-selection?
(reset! cursor-position end))
(when (and selection? (not @first-level?))
(js/setTimeout #(oops/ocall selection-manager :startActionMode text-input-handle) 500))
(when (and (not selection?) (not @first-level?))
(oops/ocall selection-manager :hideLastActionMode)
(selection/reset-to-first-level-menu first-level? menu-items))
(when @selection-event
(let [{:keys [start end text-input-handle]} @selection-event]
(selection/update-selection text-input-handle start end)
(reset! selection-event nil)))))
[event {:keys [lock-selection? cursor-position]}]
(when-not @lock-selection?
(reset! cursor-position (oops/oget event "nativeEvent.selection.end"))))
(defn layout
[event
@@ -1,4 +1,4 @@
(ns status-im2.contexts.chat.composer.images.style
(ns status-im2.contexts.chat.bottom-sheet-composer.images.style
(:require [quo2.foundations.colors :as colors]))
(def image-container
@@ -0,0 +1,41 @@
(ns status-im2.contexts.chat.bottom-sheet-composer.images.view
(:require [quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.bottom-sheet-composer.images.style :as style]
[utils.re-frame :as rf]
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]))
(defn image
[item]
[rn/view style/image-container
[rn/image
{:source {:uri (:resized-uri (val item))}
:style style/small-image}]
[rn/touchable-opacity
{:on-press #(rf/dispatch [:chat.ui/image-unselected (val item)])
:style style/remove-photo-container
:hit-slop {:right 5
:left 5
:top 10
:bottom 10}}
[quo/icon :i/close {:color colors/white :size 12}]]])
(defn images-list
[]
[:f>
(fn []
(let [images (rf/sub [:chats/sending-image])
height (reanimated/use-shared-value (if (seq images) constants/images-container-height 0))]
(rn/use-effect (fn []
(reanimated/animate height
(if (seq images) constants/images-container-height 0)))
[images])
[reanimated/view {:style (reanimated/apply-animations-to-style {:height height} {})}
[rn/flat-list
{:key-fn first
:render-fn image
:data images
:horizontal true
:keyboard-should-persist-taps :handled}]]))])
@@ -1,4 +1,4 @@
(ns status-im2.contexts.chat.composer.keyboard
(ns status-im2.contexts.chat.bottom-sheet-composer.keyboard
(:require [oops.core :as oops]
[status-im.async-storage.core :as async-storage]
[react-native.core :as rn]
@@ -1,12 +1,5 @@
(ns status-im2.contexts.chat.composer.reply.style)
(ns status-im2.contexts.chat.bottom-sheet-composer.reply.style)
(defn container
[pin? in-chat-input?]
{:flex-direction :row
:height (when-not pin? 24)
:margin-left (when (and (not in-chat-input?) (not pin?)) 26)
:margin-bottom (when (and (not in-chat-input?) (not pin?)) 8)})
(defn reply-content
[pin?]
{:padding-right (when-not pin? 10)
@@ -1,15 +1,15 @@
(ns status-im2.contexts.chat.composer.reply.view
(ns status-im2.contexts.chat.bottom-sheet-composer.reply.view
(:require [clojure.string :as string]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.composer.constants :as constants]
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]
[utils.i18n :as i18n]
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
[status-im2.constants :as constant]
[status-im.ethereum.stateofus :as stateofus]
[utils.re-frame :as rf]
[status-im2.contexts.chat.composer.reply.style :as style]
[status-im2.contexts.chat.bottom-sheet-composer.reply.style :as style]
[react-native.linear-gradient :as linear-gradient]))
(defn get-quoted-text-with-mentions
@@ -78,7 +78,7 @@
:style style/message-author-text}
(format-reply-author from contact-name current-public-key)]]))
(defn quoted-message
(defn reply-message
[{:keys [from identicon content-type contentType parsed-text content deleted? deleted-for-me?
album-images-count]}
in-chat-input? pin? recording-audio?]
@@ -86,8 +86,9 @@
current-public-key (rf/sub [:multiaccount/public-key])
content-type (or content-type contentType)]
[rn/view
{:style (style/container pin? in-chat-input?)
:accessibility-label :reply-message}
{:style {:flex-direction :row
:height (when-not pin? 24)
:accessibility-label :reply-message}}
[rn/view {:style (style/reply-content pin?)}
(when-not pin?
[quo/icon :i/connector
@@ -142,13 +143,13 @@
:style style/gradient}])]))
(defn- f-view
[recording?]
[]
(let [reply (rf/sub [:chats/reply-message])
height (reanimated/use-shared-value (if reply constants/reply-container-height 0))]
(rn/use-effect #(reanimated/animate height (if reply constants/reply-container-height 0)) [reply])
[reanimated/view {:style (reanimated/apply-animations-to-style {:height height} {})}
(when reply [quoted-message reply true false recording?])]))
(when reply [reply-message reply true false false])]))
(defn view
[{:keys [recording?]}]
[:f> f-view @recording?])
[]
[:f> f-view])
@@ -1,9 +1,9 @@
(ns status-im2.contexts.chat.composer.style
(ns status-im2.contexts.chat.bottom-sheet-composer.style
(:require [quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.composer.constants :as constants]))
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]))
(defn shadow
[focused?]
@@ -54,23 +54,19 @@
{:max-height max-height
:overflow :hidden}))
(defn input-view
[{:keys [saved-emoji-kb-extra-height]}
{:keys [focused? recording?]}]
{:z-index 1
:position (if @saved-emoji-kb-extra-height :relative :absolute)
:top 0
:left 0
:right (when (or focused? platform/ios?) 0)
:flex 1
:display (if @recording? :none :flex)
:min-height constants/input-height})
(defn input-text
[]
(defn input
[focused? saved-keyboard-height]
(merge typography/paragraph-1
{:color (colors/theme-colors :black :white)
:text-align-vertical :top}))
{:min-height constants/input-height
:color (colors/theme-colors :black :white)
:text-align-vertical :top
:flex 1
:z-index 1
:position (if saved-keyboard-height :relative :absolute)
:top 0
:left 0
:right (when (or focused? platform/ios?) 0)}))
(defn background
[opacity background-y window-height]
(reanimated/apply-animations-to-style
@@ -85,11 +81,11 @@
:z-index 1}))
(defn blur-container
[height focused?]
[height]
(reanimated/apply-animations-to-style
{:height height}
{:position :absolute
:elevation (if-not @focused? 10 0)
:elevation 10
:left 0
:right 0
:bottom 0
@@ -0,0 +1,18 @@
(ns status-im2.contexts.chat.bottom-sheet-composer.sub-view
(:require
[react-native.blur :as blur]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.bottom-sheet-composer.style :as style]))
(defn bar
[]
[rn/view {:style style/bar-container}
[rn/view {:style (style/bar)}]])
(defn blur-view
[layout-height]
[:f>
(fn []
[reanimated/view {:style (style/blur-container layout-height)}
[blur/view (style/blur-view)]])])
@@ -0,0 +1,92 @@
(ns status-im2.contexts.chat.bottom-sheet-composer.utils
(:require
[oops.core :as oops]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]
[utils.re-frame :as rf]))
(defn bounded-val
[val min-val max-val]
(max min-val (min val max-val)))
(defn get-min-height
[lines]
(if (> lines 1) constants/multiline-minimized-height constants/input-height))
(defn calc-reopen-height
[text-value min-height content-height saved-height]
(if (empty? @text-value)
min-height
(Math/min @content-height (reanimated/get-shared-value saved-height))))
(defn update-height?
[content-size height max-height maximized?]
(when-not @maximized?
(let [diff (Math/abs (- content-size (reanimated/get-shared-value height)))]
(and (not= (reanimated/get-shared-value height) max-height)
(> diff constants/content-change-threshold)))))
(defn show-top-gradient?
[y lines max-lines gradient-opacity focused?]
(and
(> y constants/line-height)
(>= lines max-lines)
(= (reanimated/get-shared-value gradient-opacity) 0)
@focused?))
(defn hide-top-gradient?
[y gradient-opacity]
(and
(<= y constants/line-height)
(= (reanimated/get-shared-value gradient-opacity) 1)))
(defn show-bottom-gradient?
[{:keys [text-value focused?]} {:keys [lines]}]
(and (not-empty @text-value) (not @focused?) (> lines 2)))
(defn show-background?
[saved-height max-height new-height]
(or (= (reanimated/get-shared-value saved-height) max-height)
(> new-height (* constants/background-threshold max-height))))
(defn update-blur-height?
[event lock-layout? layout-height]
(or (not @lock-layout?)
(> (reanimated/get-shared-value layout-height) (oops/oget event "nativeEvent.layout.height"))))
(defn calc-lines
[height]
(let [lines (Math/round (/ height constants/line-height))]
(if platform/ios? lines (dec lines))))
(defn calc-max-height
[window-height kb-height insets images? reply? edit?]
(let [margin-top (if platform/ios? (:top insets) (+ 10 (:top insets)))
max-height (- window-height
margin-top
kb-height
constants/bar-container-height
constants/actions-container-height)
max-height (if images? (- max-height constants/images-container-height) max-height)
max-height (if reply? (- max-height constants/reply-container-height) max-height)
max-height (if edit? (- max-height constants/edit-container-height) max-height)]
max-height))
(defn empty-input?
[text images reply?]
(and (empty? text) (empty? images) (not reply?)))
(defn android-elevation?
[lines images reply? edit?]
(or (> lines 1) (seq images) reply? edit?))
(defn cancel-edit-message
[{:keys [text-value maximized?]}
{:keys [height saved-height last-height]}]
(when-not @maximized?
(reanimated/animate height constants/input-height)
(reanimated/set-shared-value saved-height constants/input-height)
(reanimated/set-shared-value last-height constants/input-height))
(reset! text-value "")
(rf/dispatch [:chat.ui/set-input-content-height constants/input-height]))
@@ -0,0 +1,158 @@
(ns status-im2.contexts.chat.bottom-sheet-composer.view
(:require
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[react-native.hooks :as hooks]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[utils.i18n :as i18n]
[status-im2.contexts.chat.bottom-sheet-composer.style :as style]
[status-im2.contexts.chat.bottom-sheet-composer.images.view :as images]
[status-im2.contexts.chat.bottom-sheet-composer.reply.view :as reply]
[utils.re-frame :as rf]
[status-im2.contexts.chat.bottom-sheet-composer.edit.view :as edit]
[status-im2.contexts.chat.bottom-sheet-composer.utils :as utils]
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]
[status-im2.contexts.chat.bottom-sheet-composer.actions.view :as actions]
[status-im2.contexts.chat.bottom-sheet-composer.keyboard :as kb]
[status-im2.contexts.chat.bottom-sheet-composer.sub-view :as sub-view]
[status-im2.contexts.chat.bottom-sheet-composer.effects :as effects]
[status-im2.contexts.chat.bottom-sheet-composer.gesture :as drag-gesture]
[status-im2.contexts.chat.bottom-sheet-composer.handlers :as handler]
[status-im2.contexts.chat.bottom-sheet-composer.gradients.view :as gradients]))
(defn sheet
[insets window-height blur-height opacity background-y]
[:f>
(fn []
(let [props {:input-ref (atom nil)
:keyboard-show-listener (atom nil)
:keyboard-frame-listener (atom nil)
:keyboard-hide-listener (atom nil)
:emoji-kb-extra-height (atom nil)
:saved-emoji-kb-extra-height (atom nil)
:replying? (atom nil)
:sending-images? (atom nil)
:editing? (atom nil)}
state {:text-value (reagent/atom "")
:cursor-position (reagent/atom 0)
:saved-cursor-position (reagent/atom 0)
:gradient-z-index (reagent/atom 0)
:kb-default-height (reagent/atom nil)
:gesture-enabled? (reagent/atom true)
:lock-selection? (reagent/atom true)
:focused? (reagent/atom false)
:lock-layout? (reagent/atom false)
:maximized? (reagent/atom false)}]
[:f>
(fn []
(let [images (rf/sub [:chats/sending-image])
reply (rf/sub [:chats/reply-message])
edit (rf/sub [:chats/edit-message])
{:keys [input-text input-content-height]
:as chat-input} (rf/sub [:chats/current-chat-input])
content-height (reagent/atom (or input-content-height
constants/input-height))
{:keys [keyboard-shown keyboard-height]} (hooks/use-keyboard)
kb-height (kb/get-kb-height keyboard-height
@(:kb-default-height state))
max-height (utils/calc-max-height window-height
kb-height
insets
(seq images)
reply
edit)
lines (utils/calc-lines @content-height)
max-lines (utils/calc-lines max-height)
initial-height (if (> lines 1)
constants/multiline-minimized-height
constants/input-height)
animations {:gradient-opacity (reanimated/use-shared-value
0)
:container-opacity (reanimated/use-shared-value
(if (utils/empty-input?
input-text
images
reply)
0.7
1))
:height (reanimated/use-shared-value
initial-height)
:saved-height (reanimated/use-shared-value
initial-height)
:last-height (reanimated/use-shared-value
(utils/bounded-val
@content-height
constants/input-height
max-height))
:opacity opacity
:background-y background-y}
dimensions {:content-height content-height
:max-height max-height
:window-height window-height
:lines lines
:max-lines max-lines}
show-bottom-gradient? (utils/show-bottom-gradient? state dimensions)]
(effects/initialize props
state
animations
dimensions
chat-input
keyboard-height
(seq images)
reply
edit)
[gesture/gesture-detector
{:gesture (drag-gesture/drag-gesture props state animations dimensions keyboard-shown)}
[reanimated/view
{:style (style/sheet-container insets state animations)
:on-layout #(handler/layout % state blur-height)}
[sub-view/bar]
[reply/view reply]
[edit/view edit #(utils/cancel-edit-message state animations)]
[reanimated/touchable-opacity
{:active-opacity 1
:on-press (when @(:input-ref props) #(.focus ^js @(:input-ref props)))
:style (style/input-container (:height animations) max-height)
:accessibility-label :message-input-container}
[rn/text-input
{:ref #(reset! (:input-ref props) %)
:default-value @(:text-value state)
:on-focus #(handler/focus props state animations dimensions)
:on-blur #(handler/blur state animations dimensions images reply)
:on-content-size-change #(handler/content-size-change %
state
animations
dimensions
(or keyboard-shown edit))
:on-scroll #(handler/scroll % state animations dimensions)
:on-change-text #(handler/change-text % props state)
:on-selection-change #(handler/selection-change % state)
:max-height max-height
:max-font-size-multiplier 1
:multiline true
:placeholder (i18n/label :t/type-something)
:placeholder-text-color (colors/theme-colors colors/neutral-40 colors/neutral-50)
:style (style/input @(:focused? state)
@(:saved-emoji-kb-extra-height props))
:accessibility-label :chat-message-input}]
[gradients/view props state animations show-bottom-gradient?]]
[images/images-list]
[actions/view props state animations window-height insets (seq images)]]]))]))])
(defn f-bottom-sheet-composer
[insets]
(let [window-height (rf/sub [:dimensions/window-height])
opacity (reanimated/use-shared-value 0)
background-y (reanimated/use-shared-value (- window-height))
blur-height (reanimated/use-shared-value (+ constants/composer-default-height
(:bottom insets)))]
[rn/view
[reanimated/view {:style (style/background opacity background-y window-height)}]
[sub-view/blur-view blur-height]
[sheet insets window-height blur-height opacity background-y]]))
(defn bottom-sheet-composer
[insets]
[:f> f-bottom-sheet-composer insets])
@@ -1,190 +0,0 @@
(ns status-im2.contexts.chat.composer.actions.view
(:require
[quo2.core :as quo]
[react-native.core :as rn]
[react-native.permissions :as permissions]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[status-im2.common.alert.events :as alert]
[status-im2.contexts.chat.composer.constants :as constants]
[status-im2.contexts.chat.messages.list.view :as messages.list]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[status-im2.contexts.chat.composer.actions.style :as style]))
(defn send-message
[{:keys [text-value focused? maximized?]}
{:keys [height saved-height last-height opacity background-y container-opacity]}
window-height]
(reanimated/animate height constants/input-height)
(reanimated/set-shared-value saved-height constants/input-height)
(reanimated/set-shared-value last-height constants/input-height)
(reanimated/animate opacity 0)
(when-not @focused?
(js/setTimeout #(reanimated/animate container-opacity constants/empty-opacity) 300))
(js/setTimeout #(reanimated/set-shared-value background-y
(- window-height))
300)
(rf/dispatch [:chat.ui/send-current-message])
(rf/dispatch [:chat.ui/set-input-maximized false])
(rf/dispatch [:chat.ui/set-input-content-height constants/input-height])
(rf/dispatch [:chat.ui/set-chat-input-text nil])
(reset! maximized? false)
(reset! text-value "")
(messages.list/scroll-to-bottom))
(defn f-send-button
[{:keys [text-value] :as state}
animations window-height images?
btn-opacity z-index]
(rn/use-effect (fn []
(if (or (seq @text-value) images?)
(when (or (not= @z-index 1) (not= (reanimated/get-shared-value btn-opacity) 1))
(reset! z-index 1)
(js/setTimeout #(reanimated/animate btn-opacity 1) 50))
(when (or (not= @z-index 0) (not= (reanimated/get-shared-value btn-opacity) 0))
(reanimated/animate btn-opacity 0)
(js/setTimeout #(when (and (empty? @text-value) (not images?)) (reset! z-index 0))
300))))
[(and (empty? @text-value) (not images?))])
[reanimated/view
{:style (style/send-button btn-opacity @z-index)}
[quo/button
{:icon true
:size 32
:accessibility-label :send-message-button
:on-press #(send-message state animations window-height)}
:i/arrow-up]])
(defn send-button
[state animations window-height images?]
(let [btn-opacity (reanimated/use-shared-value 0)
z-index (reagent/atom 0)]
[:f> f-send-button state animations window-height images? btn-opacity z-index]))
(defn audio-button
[{:keys [record-reset-fn]}
{:keys [record-permission? recording? gesture-enabled? focused?]}
{:keys [container-opacity]}]
(let [audio (rf/sub [:chats/sending-audio])]
[rn/view
{:style (style/record-audio-container)
:pointer-events :box-none}
[quo/record-audio
{:record-audio-permission-granted @record-permission?
:on-init (fn [reset-fn]
(reset! record-reset-fn reset-fn))
:on-start-recording (fn []
(reset! recording? true)
(reset! gesture-enabled? false)
(reanimated/animate container-opacity 1))
:audio-file audio
:on-reviewing-audio (fn [file]
(rf/dispatch [:chat.ui/set-input-audio file]))
:on-send (fn [{:keys [file-path duration]}]
(reset! recording? false)
(reset! gesture-enabled? true)
(rf/dispatch [:chat/send-audio file-path duration])
(when-not @focused?
(reanimated/animate container-opacity
constants/empty-opacity))
(rf/dispatch [:chat.ui/set-input-audio nil]))
:on-cancel (fn []
(when @recording?
(reset! recording? false)
(reset! gesture-enabled? true)
(when-not @focused?
(reanimated/animate container-opacity
constants/empty-opacity))
(rf/dispatch [:chat.ui/set-input-audio nil])))
:on-check-audio-permissions (fn []
(permissions/permission-granted?
:record-audio
#(reset! record-permission? %)
#(reset! record-permission? false)))
:on-request-record-audio-permission (fn []
(rf/dispatch
[:request-permissions
{:permissions [:record-audio]
:on-allowed
#(reset! record-permission? true)
:on-denied
#(js/setTimeout
(fn []
(alert/show-popup
(i18n/label :t/audio-recorder-error)
(i18n/label
:t/audio-recorder-permissions-error)))
50)}]))}]]))
(defn camera-button
[]
[quo/button
{:on-press #(js/alert "to be implemented")
:icon true
:type :outline
:size 32
:style {:margin-right 12}}
:i/camera])
(defn open-photo-selector
[{:keys [input-ref]}
{:keys [height]}
insets]
(permissions/request-permissions
{:permissions [:read-external-storage :write-external-storage]
:on-allowed (fn []
(when (and platform/android? @input-ref)
(.blur ^js @input-ref))
(rf/dispatch [:chat.ui/set-input-content-height
(reanimated/get-shared-value height)])
(rf/dispatch [:open-modal :photo-selector {:insets insets}]))
:on-denied (fn []
(alert/show-popup (i18n/label :t/error)
(i18n/label
:t/external-storage-denied)))}))
(defn image-button
[props animations insets]
[quo/button
{:on-press #(open-photo-selector props animations insets)
:icon true
:type :outline
:size 32
:style {:margin-right 12}}
:i/image])
(defn reaction-button
[]
[quo/button
{:on-press #(js/alert "to be implemented")
:icon true
:type :outline
:size 32
:style {:margin-right 12}}
:i/reaction])
(defn format-button
[]
[quo/button
{:on-press #(js/alert "to be implemented")
:icon true
:type :outline
:size 32}
:i/format])
(defn view
[props state animations window-height insets edit? images?]
[rn/view {:style style/actions-container}
[rn/view
{:style {:flex-direction :row
:display (if @(:recording? state) :none :flex)}}
[camera-button]
[image-button props animations insets]
[reaction-button]
[format-button]]
[:f> send-button state animations window-height images?]
(when (and (not edit?) (not images?))
[audio-button props state animations])])
@@ -1,174 +0,0 @@
(ns status-im2.contexts.chat.composer.effects
(:require
[react-native.platform :as platform]
[status-im.async-storage.core :as async-storage]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.composer.constants :as constants]
[status-im2.contexts.chat.composer.keyboard :as kb]
[utils.number :as utils.number]
[oops.core :as oops]
[utils.re-frame :as rf]))
(defn reenter-screen-effect
[{:keys [text-value saved-cursor-position maximized?]}
{:keys [content-height]}
{:keys [input-content-height input-text input-maximized?]}]
(when (and (empty? @text-value) (not= input-text nil))
(reset! text-value input-text)
(reset! content-height input-content-height)
(reset! saved-cursor-position (count input-text)))
(when input-maximized?
(reset! maximized? true)))
(defn maximized-effect
[{:keys [maximized?]}
{:keys [height saved-height last-height]}
{:keys [max-height]}
{:keys [input-content-height]}]
(when (or @maximized? (>= input-content-height max-height))
(reanimated/animate height max-height)
(reanimated/set-shared-value saved-height max-height)
(reanimated/set-shared-value last-height max-height)))
(defn layout-effect
[{:keys [lock-layout?]}]
(when-not @lock-layout?
(js/setTimeout #(reset! lock-layout? true) 500)))
(defn kb-default-height-effect
[{:keys [kb-default-height]}]
(when-not @kb-default-height
(async-storage/get-item :kb-default-height
#(reset! kb-default-height (utils.number/parse-int % nil)))))
(defn background-effect
[{:keys [maximized?]}
{:keys [opacity background-y]}
{:keys [max-height]}
{:keys [input-content-height]}]
(when (or @maximized? (>= input-content-height (* max-height constants/background-threshold)))
(reanimated/set-shared-value background-y 0)
(reanimated/animate opacity 1)))
(defn images-effect
[{:keys [sending-images? input-ref]}
{:keys [container-opacity]}
images?]
(when images?
(reanimated/animate container-opacity 1))
(when (and (not @sending-images?) images? @input-ref)
(.focus ^js @input-ref)
(reset! sending-images? true))
(when-not images?
(reset! sending-images? false)))
(defn edit-effect
[{:keys [input-ref]}
{:keys [text-value saved-cursor-position]}
edit]
(rn/use-effect
(fn []
(let [edit-text (get-in edit [:content :text])]
(when (and edit @input-ref)
(.focus ^js @input-ref)
(.setNativeProps ^js @input-ref (clj->js {:text edit-text}))
(reset! text-value edit-text)
(reset! saved-cursor-position (count edit-text)))))
[(:message-id edit)]))
(defn reply-effect
[{:keys [input-ref]}
{:keys [container-opacity]}
reply]
(rn/use-effect
(fn []
(when reply
(reanimated/animate container-opacity 1))
(when (and reply @input-ref)
(.focus ^js @input-ref)))
[(:message-id reply)]))
(defn audio-effect
[{:keys [recording? gesture-enabled?]}
{:keys [container-opacity]}
audio]
(when (and audio (not @recording?))
(reset! recording? true)
(reset! gesture-enabled? false)
(reanimated/animate container-opacity 1)))
(defn empty-effect
[{:keys [text-value maximized? focused?]}
{:keys [container-opacity]}
images?
reply?
audio]
(when
(and (empty? @text-value) (not images?) (not reply?) (not @maximized?) (not @focused?) (not audio))
(reanimated/animate-delay container-opacity constants/empty-opacity 200)))
(defn component-will-unmount
[{:keys [keyboard-show-listener keyboard-hide-listener keyboard-frame-listener]}]
(.remove ^js @keyboard-show-listener)
(.remove ^js @keyboard-hide-listener)
(.remove ^js @keyboard-frame-listener))
(defn initialize
[props state animations {:keys [max-height] :as dimensions} chat-input keyboard-height images? reply?
audio]
(rn/use-effect
(fn []
(maximized-effect state animations dimensions chat-input)
(reenter-screen-effect state dimensions chat-input)
(layout-effect state)
(kb-default-height-effect state)
(background-effect state animations dimensions chat-input)
(images-effect props animations images?)
(audio-effect state animations audio)
(empty-effect state animations images? reply? audio)
(kb/add-kb-listeners props state animations dimensions keyboard-height)
#(component-will-unmount props))
[max-height]))
(defn edit-mentions
[{:keys [input-ref]} {:keys [text-value cursor-position]} input-with-mentions]
(rn/use-effect (fn []
(let [input-text (reduce (fn [acc item]
(str acc (second item)))
""
input-with-mentions)]
(reset! text-value input-text)
(reset! cursor-position (count input-text))
(js/setTimeout #(when @input-ref
(.setNativeProps ^js @input-ref
(clj->js {:selection {:start (count input-text)
:end (count
input-text)}})))
300)))
[(some #(= :mention (first %)) (seq input-with-mentions))]))
(defn update-input-mention
[{:keys [input-ref]}
{:keys [text-value]}
input-text]
(rn/use-effect
(fn []
(when (and input-text (not= @text-value input-text))
(when @input-ref
(.setNativeProps ^js @input-ref (clj->js {:text input-text})))
(reset! text-value input-text)
(rf/dispatch [:mention/on-change-text input-text])))
[input-text]))
(defn did-mount
[{:keys [selectable-input-ref input-ref selection-manager]}]
(rn/use-effect
(fn []
(when platform/android?
(let [selectable-text-input-handle (rn/find-node-handle @selectable-input-ref)
text-input-handle (rn/find-node-handle @input-ref)]
(oops/ocall selection-manager
:setupMenuItems
selectable-text-input-handle
text-input-handle))))))
@@ -1,24 +0,0 @@
(ns status-im2.contexts.chat.composer.gradients.view
(:require
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.composer.gradients.style :as style]))
(defn f-view
[{:keys [input-ref]}
{:keys [gradient-z-index]}
{:keys [gradient-opacity]}
show-bottom-gradient?]
[:<>
[reanimated/linear-gradient (style/top-gradient gradient-opacity @gradient-z-index)]
(when show-bottom-gradient?
[rn/touchable-without-feedback
{:on-press #(when @input-ref (.focus ^js @input-ref))
:accessibility-label :bottom-gradient}
[linear-gradient/linear-gradient (style/bottom-gradient)]])])
(defn view
[props state animations show-bottom-gradient?]
[:f> f-view props state animations show-bottom-gradient?])
@@ -1,47 +0,0 @@
(ns status-im2.contexts.chat.composer.images.view
(:require [quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.composer.images.style :as style]
[utils.re-frame :as rf]
[status-im2.contexts.chat.composer.constants :as constants]))
(defn image
[item]
[rn/view style/image-container
[rn/image
{:source {:uri (:resized-uri (val item))}
:style style/small-image}]
[rn/touchable-opacity
{:on-press #(rf/dispatch [:chat.ui/image-unselected (val item)])
:style style/remove-photo-container
:hit-slop {:right 5
:left 5
:top 10
:bottom 10}}
[quo/icon :i/close {:color colors/white :size 12}]]])
(defn f-images-list
[]
(let [images (rf/sub [:chats/sending-image])
height (reanimated/use-shared-value (if (seq images) constants/images-container-height 0))]
(rn/use-effect (fn []
(reanimated/animate height
(if (seq images) constants/images-container-height 0)))
[images])
[reanimated/view
{:style (reanimated/apply-animations-to-style {:height height} {:margin-horizontal -20})}
[gesture/flat-list
{:key-fn first
:render-fn image
:data images
:content-container-style {:padding-horizontal 20}
:horizontal true
:shows-horizontal-scroll-indicator false
:keyboard-should-persist-taps :handled}]]))
(defn images-list
[]
[:f> f-images-list])
@@ -1,30 +0,0 @@
(ns status-im2.contexts.chat.composer.mentions.style
(:require [quo2.foundations.colors :as colors]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.composer.constants :as constants]))
(defn shadow
[]
(if platform/ios?
{:shadow-radius (colors/theme-colors 30 50)
:shadow-opacity (colors/theme-colors 0.1 0.7)
:shadow-color colors/neutral-100
:shadow-offset {:width 0 :height (colors/theme-colors 8 12)}}
{:elevation 10}))
(defn container
[opacity bottom]
(reanimated/apply-animations-to-style
{:opacity opacity}
(merge
{:position :absolute
:bottom bottom
:left 8
:right 8
:border-radius 16
:z-index 4
:max-height constants/mentions-max-height
:background-color (colors/theme-colors colors/white colors/neutral-95)}
(shadow))))
@@ -1,67 +0,0 @@
(ns status-im2.contexts.chat.composer.mentions.view
(:require
[react-native.hooks :as hooks]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im2.contexts.chat.composer.utils :as utils]
[utils.re-frame :as rf]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[status-im2.common.contact-list-item.view :as contact-list-item]
[status-im2.contexts.chat.composer.mentions.style :as style]))
(defn update-cursor
[user {:keys [cursor-position input-ref]}]
(when platform/android?
(let [new-cursor-pos (+ (count (:primary-name user)) @cursor-position 1)]
(reset! cursor-position new-cursor-pos)
(reagent/next-tick #(when @input-ref
(.setNativeProps ^js @input-ref
(clj->js {:selection {:start new-cursor-pos
:end
new-cursor-pos}})))))))
(defn mention-item
[user _ _ render-data]
[contact-list-item/contact-list-item
{:on-press (fn []
(rf/dispatch [:chat.ui/select-mention nil user])
(update-cursor user render-data))}
user])
(defn- f-view
[suggestions-atom props state animations max-height cursor-pos]
(let [{:keys [keyboard-height]} (hooks/use-keyboard)
suggestions (rf/sub [:chat/mention-suggestions])
opacity (reanimated/use-shared-value (if (seq suggestions) 1 0))
size (count suggestions)
data {:keyboard-height keyboard-height
:insets (safe-area/get-insets)
:curr-height (reanimated/get-shared-value (:height animations))
:window-height (rf/sub [:dimensions/window-height])
:reply (rf/sub [:chats/reply-message])
:edit (rf/sub [:chats/edit-message])}
mentions-pos (utils/calc-suggestions-position cursor-pos max-height size state data)]
(rn/use-effect
(fn []
(if (seq suggestions)
(reset! suggestions-atom suggestions)
(js/setTimeout #(reset! suggestions-atom suggestions) 300))
(reanimated/animate opacity (if (seq suggestions) 1 0)))
[(seq suggestions)])
[reanimated/view
{:style (style/container opacity mentions-pos)}
[rn/flat-list
{:keyboard-should-persist-taps :always
:data (vals @suggestions-atom)
:key-fn :key
:render-fn mention-item
:render-data {:cursor-position (:cursor-position state)
:input-ref (:input-ref props)}
:accessibility-label :mentions-list}]]))
(defn view
[props state animations max-height cursor-pos]
(let [suggestions-atom (reagent/atom {})]
[:f> f-view suggestions-atom props state animations max-height cursor-pos]))
@@ -1,109 +0,0 @@
(ns status-im2.contexts.chat.composer.selection
(:require
[clojure.string :as string]
[react-native.clipboard :as clipboard]
[react-native.core :as rn]
[oops.core :as oops]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(declare first-level-menu-items second-level-menu-items)
(defn update-input-text
[{:keys [text-input]} text]
(rf/dispatch [:chat.ui/set-chat-input-text text])
(.setNativeProps ^js text-input (clj->js {:text text})))
(defn calculate-input-text
[{:keys [full-text selection-start selection-end]} content]
(let [head (subs full-text 0 selection-start)
tail (subs full-text selection-end)]
(str head content tail)))
(defn update-selection
[text-input-handle selection-start selection-end]
;; to avoid something like this
;; https://lightrun.com/answers/facebook-react-native-textinput-controlled-selection-broken-on-both-ios-and-android
;; use native invoke instead! do not use setNativeProps! e.g. (.setNativeProps ^js text-input (clj->js
;; {:selection {:start selection-start :end selection-end}}))
(let [manager (rn/selectable-text-input-manager)]
(oops/ocall manager :setSelection text-input-handle selection-start selection-end)))
(defn reset-to-first-level-menu
[first-level? menu-items]
(reset! first-level? true)
(reset! menu-items first-level-menu-items))
(defn append-markdown-char
[{:keys [first-level? menu-items content selection-start selection-end text-input-handle
selection-event]
:as params} wrap-chars]
(let [content (str wrap-chars content wrap-chars)
new-text (calculate-input-text params content)
len-wrap-chars (count wrap-chars)
selection-start (+ selection-start len-wrap-chars)
selection-end (+ selection-end len-wrap-chars)]
;; don't update selection directly here, process it within on-selection-change instead
;; so that we can avoid java.lang.IndexOutOfBoundsException: setSpan..
(reset! selection-event {:start selection-start
:end selection-end
:text-input-handle text-input-handle})
(update-input-text params new-text)
(reset-to-first-level-menu first-level? menu-items)))
(def first-level-menus
{:cut (fn [{:keys [content] :as params}]
(let [new-text (calculate-input-text params "")]
(clipboard/set-string content)
(update-input-text params new-text)))
:copy-to-clipboard (fn [{:keys [content]}]
(clipboard/set-string content))
:paste (fn [params]
(let [callback (fn [paste-content]
(let [content (string/trim paste-content)
new-text (calculate-input-text params content)]
(update-input-text params new-text)))]
(clipboard/get-string callback)))
:biu (fn [{:keys [first-level? text-input-handle menu-items selection-start
selection-end]}]
(reset! first-level? false)
(reset! menu-items second-level-menu-items)
(update-selection text-input-handle selection-start selection-end))})
(def first-level-menu-items (map i18n/label (keys first-level-menus)))
(def second-level-menus
{:bold #(append-markdown-char % "**")
:italic #(append-markdown-char % "*")
:strikethrough #(append-markdown-char % "~~")})
(def second-level-menu-items (map i18n/label (keys second-level-menus)))
(defn on-menu-item-touched
[{:keys [first-level? event-type] :as params}]
(let [menus (if @first-level? first-level-menus second-level-menus)
menu-item-key (nth (keys menus) event-type)
action (get menus menu-item-key)]
(action params)))
(defn on-selection
[event
{:keys [input-ref selection-event]}
{:keys [first-level? menu-items]}]
(let [{:strs [eventType content selectionStart
selectionEnd]} (js->clj (oops/oget event "nativeEvent"))
full-text (:input-text (rf/sub [:chats/current-chat-input]))]
(on-menu-item-touched {:first-level? first-level?
:event-type eventType
:content content
:selection-start selectionStart
:selection-end selectionEnd
:text-input @input-ref
:text-input-handle (rn/find-node-handle @input-ref)
:full-text full-text
:menu-items menu-items
:selection-event selection-event})))
@@ -1,19 +0,0 @@
(ns status-im2.contexts.chat.composer.sub-view
(:require
[react-native.blur :as blur]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[status-im2.contexts.chat.composer.style :as style]))
(defn bar
[]
[rn/view {:style style/bar-container}
[rn/view {:style (style/bar)}]])
(defn f-blur-view
[layout-height focused?]
[reanimated/view {:style (style/blur-container layout-height focused?)}
[blur/view (style/blur-view)]])
(defn blur-view
[layout-height focused?]
[:f> f-blur-view layout-height focused?])
@@ -1,190 +0,0 @@
(ns status-im2.contexts.chat.composer.utils
(:require
[clojure.string :as string]
[oops.core :as oops]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[status-im2.contexts.chat.composer.constants :as constants]
[status-im2.contexts.chat.composer.selection :as selection]
[utils.re-frame :as rf]))
(defn bounded-val
[val min-val max-val]
(max min-val (min val max-val)))
(defn get-min-height
[lines]
(if (> lines 1) constants/multiline-minimized-height constants/input-height))
(defn calc-reopen-height
[text-value min-height content-height saved-height]
(if (empty? @text-value)
min-height
(Math/min @content-height (reanimated/get-shared-value saved-height))))
(defn update-height?
[content-size height max-height maximized?]
(when-not @maximized?
(let [diff (Math/abs (- content-size (reanimated/get-shared-value height)))]
(and (not= (reanimated/get-shared-value height) max-height)
(> diff constants/content-change-threshold)))))
(defn show-top-gradient?
[y lines max-lines gradient-opacity focused?]
(and
(> y constants/line-height)
(>= lines max-lines)
(= (reanimated/get-shared-value gradient-opacity) 0)
@focused?))
(defn hide-top-gradient?
[y gradient-opacity]
(and
(<= y constants/line-height)
(= (reanimated/get-shared-value gradient-opacity) 1)))
(defn show-bottom-gradient?
[{:keys [text-value focused?]} {:keys [lines]}]
(and (not-empty @text-value) (not @focused?) (> lines 2)))
(defn show-background?
[saved-height max-height new-height]
(or (= (reanimated/get-shared-value saved-height) max-height)
(> new-height (* constants/background-threshold max-height))))
(defn update-blur-height?
[event lock-layout? layout-height]
(or (not @lock-layout?)
(> (reanimated/get-shared-value layout-height) (oops/oget event "nativeEvent.layout.height"))))
(defn calc-lines
[height]
(let [lines (Math/round (/ height constants/line-height))]
(if platform/ios? lines (dec lines))))
(defn calc-max-height
[window-height kb-height insets images? reply? edit?]
(let [margin-top (if platform/ios? (:top insets) (+ 10 (:top insets)))
max-height (- window-height
margin-top
kb-height
constants/bar-container-height
constants/actions-container-height)
max-height (if images? (- max-height constants/images-container-height) max-height)
max-height (if reply? (- max-height constants/reply-container-height) max-height)
max-height (if edit? (- max-height constants/edit-container-height) max-height)]
max-height))
(defn empty-input?
[text images reply? audio?]
(and (empty? text) (empty? images) (not reply?) (not audio?)))
(defn android-elevation?
[lines images reply? edit?]
(or (> lines 1) (seq images) reply? edit?))
(defn cancel-edit-message
[{:keys [text-value]}]
(reset! text-value "")
(rf/dispatch [:chat.ui/set-input-content-height constants/input-height]))
(defn count-lines
[s]
(-> s
(string/split #"\n" -1)
(butlast)
count))
(defn cursor-y-position-relative-to-container
[{:keys [scroll-y]}
{:keys [cursor-position text-value]}]
(let [sub-text (subs @text-value 0 @cursor-position)
sub-text-lines (count-lines sub-text)
scrolled-lines (Math/round (/ @scroll-y constants/line-height))
sub-text-lines-in-view (- sub-text-lines scrolled-lines)]
(* sub-text-lines-in-view constants/line-height)))
(defn calc-suggestions-position
[cursor-pos max-height size
{:keys [maximized?]}
{:keys [insets curr-height window-height keyboard-height edit reply]}]
(let [base (+ constants/composer-default-height (:bottom insets) 8)
base (+ base (- curr-height constants/input-height))
base (if edit
(+ base constants/edit-container-height)
base)
base (if reply
(+ base constants/reply-container-height)
base)
view-height (- window-height keyboard-height (:top insets))
container-height (bounded-val
(* (/ constants/mentions-max-height 4) size)
(/ constants/mentions-max-height 4)
constants/mentions-max-height)]
(if @maximized?
(if (< (+ cursor-pos container-height) max-height)
(+ constants/actions-container-height (:bottom insets))
(+ constants/actions-container-height (:bottom insets) (- max-height cursor-pos) 18))
(if (< (+ base container-height) view-height)
base
(+ constants/actions-container-height (:bottom insets) (- curr-height cursor-pos) 18)))))
(defn init-props
[]
{:input-ref (atom nil)
:selectable-input-ref (atom nil)
:keyboard-show-listener (atom nil)
:keyboard-frame-listener (atom nil)
:keyboard-hide-listener (atom nil)
:emoji-kb-extra-height (atom nil)
:saved-emoji-kb-extra-height (atom nil)
:sending-images? (atom false)
:record-reset-fn (atom nil)
:scroll-y (atom 0)
:selection-event (atom nil)
:selection-manager (rn/selectable-text-input-manager)})
(defn init-state
[]
{:text-value (reagent/atom "")
:cursor-position (reagent/atom 0)
:saved-cursor-position (reagent/atom 0)
:gradient-z-index (reagent/atom 0)
:kb-default-height (reagent/atom nil)
:gesture-enabled? (reagent/atom true)
:lock-selection? (reagent/atom true)
:focused? (reagent/atom false)
:lock-layout? (reagent/atom false)
:maximized? (reagent/atom false)
:record-permission? (reagent/atom true)
:recording? (reagent/atom false)
:first-level? (reagent/atom true)
:menu-items (reagent/atom selection/first-level-menu-items)})
(defn init-animations
[lines input-text images reply audio content-height max-height opacity background-y]
(let [initial-height (if (> lines 1)
constants/multiline-minimized-height
constants/input-height)]
{:gradient-opacity (reanimated/use-shared-value 0)
:container-opacity (reanimated/use-shared-value
(if (empty-input?
input-text
images
reply
audio)
0.7
1))
:height (reanimated/use-shared-value
initial-height)
:saved-height (reanimated/use-shared-value
initial-height)
:last-height (reanimated/use-shared-value
(bounded-val
@content-height
constants/input-height
max-height))
:opacity opacity
:background-y background-y}))
@@ -1,144 +0,0 @@
(ns status-im2.contexts.chat.composer.view
(:require
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[react-native.hooks :as hooks]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[status-im2.contexts.chat.composer.style :as style]
[status-im2.contexts.chat.composer.images.view :as images]
[status-im2.contexts.chat.composer.reply.view :as reply]
[status-im2.contexts.chat.composer.edit.view :as edit]
[status-im2.contexts.chat.composer.mentions.view :as mentions]
[status-im2.contexts.chat.composer.utils :as utils]
[status-im2.contexts.chat.composer.constants :as constants]
[status-im2.contexts.chat.composer.actions.view :as actions]
[status-im2.contexts.chat.composer.keyboard :as kb]
[status-im2.contexts.chat.composer.sub-view :as sub-view]
[status-im2.contexts.chat.composer.effects :as effects]
[status-im2.contexts.chat.composer.gesture :as drag-gesture]
[status-im2.contexts.chat.composer.handlers :as handler]
[status-im2.contexts.chat.composer.gradients.view :as gradients]
[status-im2.contexts.chat.composer.selection :as selection]))
(defn sheet-component
[{:keys [insets window-height blur-height opacity background-y]} props state]
(let [images (rf/sub [:chats/sending-image])
audio (rf/sub [:chats/sending-audio])
reply (rf/sub [:chats/reply-message])
edit (rf/sub [:chats/edit-message])
input-with-mentions (rf/sub [:chat/input-with-mentions])
{:keys [input-text input-content-height]
:as chat-input} (rf/sub [:chats/current-chat-input])
content-height (reagent/atom (or input-content-height
constants/input-height))
{:keys [keyboard-shown keyboard-height]} (hooks/use-keyboard)
kb-height (kb/get-kb-height keyboard-height
@(:kb-default-height state))
max-height (utils/calc-max-height window-height
kb-height
insets
(boolean (seq images))
reply
edit)
lines (utils/calc-lines @content-height)
max-lines (utils/calc-lines max-height)
animations (utils/init-animations
lines
input-text
images
reply
audio
content-height
max-height
opacity
background-y)
dimensions {:content-height content-height
:max-height max-height
:window-height window-height
:lines lines
:max-lines max-lines}
show-bottom-gradient? (utils/show-bottom-gradient? state dimensions)
cursor-pos (utils/cursor-y-position-relative-to-container props
state)]
(effects/did-mount props)
(effects/initialize props
state
animations
dimensions
chat-input
keyboard-height
(boolean (seq images))
reply
audio)
(effects/edit-effect props state edit)
(effects/reply-effect props animations reply)
(effects/update-input-mention props state input-text)
(effects/edit-mentions props state input-with-mentions)
[:<>
[mentions/view props state animations max-height cursor-pos]
[gesture/gesture-detector
{:gesture (drag-gesture/drag-gesture props state animations dimensions keyboard-shown)}
[reanimated/view
{:style (style/sheet-container insets state animations)
:on-layout #(handler/layout % state blur-height)}
[sub-view/bar]
[reply/view state]
[edit/view state]
[reanimated/touchable-opacity
{:active-opacity 1
:on-press (when @(:input-ref props) #(.focus ^js @(:input-ref props)))
:style (style/input-container (:height animations) max-height)
:accessibility-label :message-input-container}
[rn/selectable-text-input
{:ref #(reset! (:selectable-input-ref props) %)
:menu-items @(:menu-items state)
:style (style/input-view props state)}
[rn/text-input
{:ref #(reset! (:input-ref props) %)
:default-value @(:text-value state)
:on-focus #(handler/focus props state animations dimensions)
:on-blur #(handler/blur state animations dimensions images reply)
:on-content-size-change #(handler/content-size-change %
state
animations
dimensions
(or keyboard-shown edit))
:on-scroll #(handler/scroll % props state animations dimensions)
:on-change-text #(handler/change-text % props state)
:on-selection-change #(handler/selection-change % props state)
:on-selection #(selection/on-selection % props state)
:max-height max-height
:max-font-size-multiplier 1
:multiline true
:placeholder (i18n/label :t/type-something)
:placeholder-text-color (colors/theme-colors colors/neutral-40 colors/neutral-50)
:style (style/input-text)
:max-length constants/max-text-size
:accessibility-label :chat-message-input}]]
[gradients/view props state animations show-bottom-gradient?]]
[images/images-list]
[actions/view props state animations window-height insets edit
(boolean (seq images))]]]]))
(defn composer
[insets]
(let [window-height (rf/sub [:dimensions/window-height])
opacity (reanimated/use-shared-value 0)
background-y (reanimated/use-shared-value (- window-height))
blur-height (reanimated/use-shared-value (+ constants/composer-default-height
(:bottom insets)))
extra-params {:insets insets
:window-height window-height
:blur-height blur-height
:opacity opacity
:background-y background-y}
props (utils/init-props)
state (utils/init-state)]
[rn/view
[reanimated/view {:style (style/background opacity background-y window-height)}]
[sub-view/blur-view blur-height (:focused? state)]
[:f> sheet-component extra-params props state]]))
@@ -1,14 +1,12 @@
(ns status-im2.contexts.chat.home.chat-list-item.view
(:require [quo2.core :as quo]
(:require [clojure.string :as string]
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[utils.datetime :as datetime]
[status-im2.common.home.actions.view :as actions]
[status-im2.common.home.actions.view :as actions] ;;TODO move to status-im2
[status-im2.contexts.chat.home.chat-list-item.style :as style]
[utils.re-frame :as rf]
[status-im2.constants :as constants]
[clojure.string :as string]
[utils.i18n :as i18n]))
[utils.re-frame :as rf]))
(def max-subheader-length 50)
@@ -18,149 +16,61 @@
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:chat/navigate-to-chat chat-id])))
(defn parsed-text-to-one-line
[parsed-text]
(reduce
(fn [acc {:keys [type literal children destination]}]
(case type
"paragraph"
(str acc (parsed-text-to-one-line children) " ")
(defn truncate-literal
[literal]
(when literal
(let [size (min max-subheader-length (.-length literal))]
{:components (.substring literal 0 size)
:length size})))
"mention"
(str acc "@" (rf/sub [:messages/resolve-mention literal]))
(defn add-parsed-to-subheader
[acc {:keys [type destination literal children]}]
(let [result (case type
"paragraph"
(reduce
(fn [{:keys [_ length] :as acc-paragraph} parsed-child]
(if (>= length max-subheader-length)
(reduced acc-paragraph)
(add-parsed-to-subheader acc-paragraph parsed-child)))
{:components [quo/text]
:length 0}
children)
"status-tag"
(str acc literal)
"mention"
{:components [quo/text (rf/sub [:messages/resolve-mention literal])]
:length 4} ;; we can't predict name length so take the
;; smallest possible
"link"
(str acc destination)
"status-tag"
(truncate-literal (str "#" literal))
(str acc (string/replace literal #"\n" " "))))
""
parsed-text))
"link"
(truncate-literal destination)
(defn extract-text-from-message
[{:keys [content]}]
(let [{:keys [parsed-text text]} content]
(if parsed-text
(parsed-text-to-one-line parsed-text)
(if text
(string/replace text #"\n" " ")
text))))
(truncate-literal literal))]
{:components (conj (:components acc) (:components result))
:length (+ (:length acc) (:length result))}))
(defn preview-text-from-content
[group-chat primary-name {:keys [content-type album-images-count content outgoing] :as message}]
(let [content-text (extract-text-from-message message)
reply? (not (string/blank? (:response-to content)))
author (if outgoing
:you
(if group-chat
:other-person
:dont-show))
preview-text
(case content-type
constants/content-type-text
(if reply?
(case author
:you (str (i18n/label :t/you-replied) ": " content-text)
:other-person (str (i18n/label :t/user-replied {:user primary-name}) ": " content-text)
:dont-show (str (i18n/label :t/replied) ": " content-text)
(str (i18n/label :t/replied) ": " content-text))
(case author
:you (str (i18n/label :t/You) ": " content-text)
:other-person (str primary-name ": " content-text)
:dont-show content-text
content-text))
constants/content-type-emoji
(case author
:you (str (i18n/label :t/You) ": " content-text)
:other-person (str primary-name ": " content-text)
:dont-show content-text
content-text)
constants/content-type-system-text
(case author
:you (i18n/label :t/you-pinned-a-message)
:other-person (i18n/label :t/user-pinned-a-message {:user primary-name})
:dont-show (i18n/label :t/Pinned-a-message)
(i18n/label :t/Pinned-a-message))
constants/content-type-sticker
(case author
:you (i18n/label :t/you-sent-a-sticker)
:other-person (i18n/label :t/user-sent-a-sticker {:user primary-name})
:dont-show (i18n/label :t/sent-a-sticker)
(i18n/label :t/sent-a-sticker))
constants/content-type-image
(let [sent-photos (if album-images-count
(case author
:you (i18n/label :t/you-sent-n-photos
{:number album-images-count})
:other-person (i18n/label :t/user-sent-n-photos
{:number album-images-count
:user primary-name})
:dont-show (i18n/label :t/sent-n-photos {:number album-images-count})
(i18n/label :t/sent-n-photos {:number album-images-count}))
(case author
:you (i18n/label :t/you-sent-a-photo)
:other-person (i18n/label :t/user-sent-a-photo {:user primary-name})
:dont-show (i18n/label :t/sent-a-photo)
(i18n/label :t/sent-a-photo)))]
(if (not (string/blank? content-text))
(str sent-photos ": " content-text)
sent-photos))
constants/content-type-audio
(case author
:you (i18n/label :t/you-sent-audio-message)
:other-person (i18n/label :t/user-sent-audio-message {:user primary-name})
:dont-show (i18n/label :t/sent-audio-message)
(i18n/label :t/sent-audio-message))
constants/content-type-gif
(case author
:you (i18n/label :t/you-sent-a-gif)
:other-person (i18n/label :t/user-sent-audio-message {:user primary-name})
:dont-show (i18n/label :t/sent-a-gif)
(i18n/label :t/sent-a-gif))
constants/content-type-community
(case author
:you (i18n/label :t/you-shared-a-community)
:other-person (i18n/label :t/user-shared-a-community {:user primary-name})
:dont-show (i18n/label :t/shared-a-community)
(i18n/label :t/shared-a-community))
"")]
(subs preview-text 0 (min (count preview-text) max-subheader-length))))
(defn last-message-preview
(defn render-subheader
"Render the preview of a last message to a maximum of max-subheader-length characters"
[group-chat {:keys [deleted? outgoing from deleted-for-me?] :as message}]
(let [[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity from])
preview-text (if deleted-for-me?
(i18n/label :t/you-deleted-a-message)
(if deleted?
(if outgoing
(i18n/label :t/you-deleted-a-message)
(if group-chat
(i18n/label :t/user-deleted-a-message {:user primary-name})
(i18n/label :t/this-message-was-deleted)))
(preview-text-from-content group-chat primary-name message)))]
[quo/text
{:size :paragraph-2
:style {:color (colors/theme-colors colors/neutral-50
colors/neutral-40)
:flex 1
:margin-right 20}
:number-of-lines 1
:ellipsize-mode :tail
:accessibility-label :chat-message-text}
preview-text]))
[parsed-text]
(let [result
(reduce
(fn [{:keys [_ length] :as acc-text} new-text-chunk]
(if (>= length max-subheader-length)
(reduced acc-text)
(add-parsed-to-subheader acc-text new-text-chunk)))
{:components [quo/text
{:size :paragraph-2
:style {:color (colors/theme-colors colors/neutral-50
colors/neutral-40)
:width "90%"}
:number-of-lines 1
:ellipsize-mode :tail
:accessibility-label :chat-message-text}]
:length 0}
parsed-text)]
(:components result)))
(defn verified-or-contact-icon
[{:keys [ens-verified added?]}]
@@ -226,7 +136,12 @@
:color color}]
[rn/view {:style {:margin-left 8}}
[name-view display-name contact timestamp]
[last-message-preview group-chat last-message]]
(if (string/blank? (get-in last-message [:content :parsed-text]))
[quo/text
{:size :paragraph-2
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
(get-in last-message [:content :text])]
[render-subheader (get-in last-message [:content :parsed-text])])]
(when-not muted
(if (> unviewed-mentions-count 0)
[quo/info-count {:style {:top 16}}
@@ -56,7 +56,8 @@
:start {:x 0 :y 1}
:end {:x 0 :y 0}
:style (style/gradient-container insets animations derived)}
[rn/text {:style style/text-style} text]
(when (not= text "placeholder")
[rn/text {:style style/text-style} text])
[rn/flat-list
{:ref #(reset! (:small-list-ref atoms) %)
:key-fn :message-id
@@ -0,0 +1,28 @@
(ns status-im2.contexts.chat.messages.composer.controls.style
(:require [quo2.foundations.colors :as colors]))
(defn controls
[bottom-inset]
{:padding-horizontal 20
:z-index 3
:position :absolute
:background-color (colors/theme-colors colors/white colors/neutral-90)
:padding-bottom (+ 12 bottom-inset)
:bottom 0
:left 0
:right 0})
(def buttons-container
{:flex-direction :row
:margin-top 12
:min-height 32})
(defn record-audio-container
[bottom-inset]
{:align-items :center
:background-color :transparent
:flex-direction :row
:position :absolute
:left 0
:right 0
:bottom (- bottom-inset 7)})
@@ -0,0 +1,127 @@
(ns status-im2.contexts.chat.messages.composer.controls.view
(:require [react-native.core :as rn]
[react-native.background-timer :as background-timer]
[utils.re-frame :as rf]
[utils.i18n :as i18n]
[quo2.core :as quo]
[status-im2.common.not-implemented :as not-implemented]
[status-im2.contexts.chat.messages.composer.controls.style :as style]
[status-im2.contexts.chat.messages.list.view :as messages.list]
[status-im.ui2.screens.chat.composer.images.view :as composer-images]
[status-im.utils.utils :as utils-old]
[status-im.ui2.screens.chat.composer.input :as input]
[status-im2.common.alert.events :as alert]
[react-native.permissions :as permissions]
[quo.react :as quo.react]))
(defn send-button
[send-ref {:keys [chat-id images]} on-send]
[rn/view
{:ref send-ref
:style (when-not (or (seq (get @input/input-texts chat-id)) (seq images))
{:width 0
:right -100})}
[quo/button
{:icon true
:size 32
:accessibility-label :send-message-button
:on-press (fn []
(on-send)
(messages.list/scroll-to-bottom)
(rf/dispatch [:chat.ui/send-current-message]))}
:i/arrow-up]])
(defn reactions-button
[]
[not-implemented/not-implemented
[quo/button
{:icon true
:type :outline
:size 32} :i/reaction]])
(defn image-button
[insets]
[quo/button
{:on-press (fn []
(permissions/request-permissions
{:permissions [:read-external-storage :write-external-storage]
:on-allowed #(rf/dispatch [:open-modal :photo-selector {:insets insets}])
:on-denied (fn []
(background-timer/set-timeout
#(utils-old/show-popup (i18n/label :t/error)
(i18n/label
:t/external-storage-denied))
50))}))
:icon true
:type :outline
:size 32}
:i/image])
(defn record-audio
[record-ref chat-id bottom-inset]
[rn/view
{:ref record-ref
:style (style/record-audio-container bottom-inset)
:pointer-events :box-none}
[quo/record-audio
{:record-audio-permission-granted @input/record-audio-permission-granted
:on-init (fn [init-fn]
(reset! input/record-audio-reset-fn init-fn)
(reset! input/recording-audio?
(some? (get @input/reviewing-audio-filepath chat-id)))
(when (seq (get @input/input-texts chat-id))
(js/setTimeout #(quo.react/set-native-props
record-ref
#js {:right nil :left -1000}))))
:on-start-recording #(reset! input/recording-audio? true)
:audio-file (get @input/reviewing-audio-filepath chat-id)
:on-reviewing-audio (fn [audio-file]
(swap! input/reviewing-audio-filepath assoc
chat-id
audio-file)
(reset! input/reviewing-audio? true))
:on-send (fn
[{:keys [file-path duration]}]
(rf/dispatch [:chat/send-audio file-path duration])
(reset! input/recording-audio? false)
(reset! input/reviewing-audio? false)
(swap! input/reviewing-audio-filepath dissoc chat-id))
:on-cancel (fn []
(reset! input/recording-audio? false)
(reset! input/reviewing-audio? false)
(swap! input/reviewing-audio-filepath dissoc chat-id))
:on-check-audio-permissions (fn []
(permissions/permission-granted?
:record-audio
#(reset! input/record-audio-permission-granted %)
#(reset! input/record-audio-permission-granted false)))
:on-request-record-audio-permission (fn []
(rf/dispatch
[:request-permissions
{:permissions [:record-audio]
:on-allowed
#(reset! input/record-audio-permission-granted true)
:on-denied
#(js/setTimeout
(fn []
(alert/show-popup
(i18n/label :t/audio-recorder-error)
(i18n/label
:t/audio-recorder-permissions-error)))
50)}]))}]])
(defn view
[send-ref record-ref params bottom-inset chat-id images edit on-send]
[rn/view {:style (style/controls bottom-inset)}
[composer-images/images-list images]
[rn/view {:style style/buttons-container}
(when (and (not @input/recording-audio?)
(nil? (get @input/reviewing-audio-filepath chat-id)))
[:<>
[image-button {:bottom bottom-inset}]
[rn/view {:width 12}]
[reactions-button]
[rn/view {:flex 1}]
[send-button send-ref params on-send]])]
(when (and (not edit) (not (seq images)))
[record-audio record-ref chat-id bottom-inset])])
@@ -0,0 +1,35 @@
(ns status-im2.contexts.chat.messages.composer.mentions.view
(:require [utils.re-frame :as rf]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[status-im2.common.contact-list-item.view :as contact-list-item]))
(defn mention-item
[user _ _ text-input-ref]
[contact-list-item/contact-list-item
{:on-press #(rf/dispatch [:chat.ui/select-mention text-input-ref user])} user])
(defn f-mentions
[{:keys [refs suggestions max-y]} bottom-inset]
(let [translate-y (reanimated/use-shared-value 0)]
(rn/use-effect
(fn []
(reanimated/set-shared-value translate-y
(reanimated/with-timing (if (seq suggestions) 0 200)))))
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]}
{:bottom (or bottom-inset 0)
:position :absolute
:left 0
:right 0
:z-index 5
:elevation 5
:max-height (/ max-y 2)})}
[rn/flat-list
{:keyboard-should-persist-taps :always
:data (vals suggestions)
:key-fn :key
:render-fn mention-item
:render-data (:text-input-ref refs)
:accessibility-label :mentions-list}]]))
@@ -0,0 +1,42 @@
(ns status-im2.contexts.chat.messages.composer.style
(:require [quo2.foundations.colors :as colors]
[status-im.utils.platform :as platform]))
(defn input-bottom-sheet
[window-height]
(merge {:border-top-left-radius 20
:border-top-right-radius 20
:position :absolute
:left 0
:right 0
:bottom (- window-height)
:height window-height
:flex 1
:background-color (colors/theme-colors colors/white colors/neutral-90)
:z-index 2}
(if platform/ios?
{:shadow-radius 16
:shadow-opacity 1
:shadow-color "rgba(9, 16, 28, 0.04)"
:shadow-offset {:width 0 :height -2}}
{:elevation 4})))
(defn bottom-sheet-handle
[]
{:width 32
:height 4
:background-color (colors/theme-colors colors/neutral-100 colors/white)
:opacity 0.05
:border-radius 100
:align-self :center
:margin-top 8})
(defn bottom-sheet-background
[window-height]
{:position :absolute
:left 0
:right 0
:bottom 0
:height window-height
:background-color colors/neutral-95-opa-70
:z-index 1})
@@ -0,0 +1,241 @@
(ns status-im2.contexts.chat.messages.composer.view
(:require [clojure.string :as string]
[oops.core :refer [oget]]
[react-native.core :as rn]
[react-native.hooks :as hooks]
[react-native.gesture :as gesture]
[react-native.reanimated :as reanimated]
[react-native.background-timer :as background-timer]
[utils.re-frame :as rf]
[status-im2.contexts.chat.messages.composer.style :as style]
[status-im2.contexts.chat.messages.composer.controls.view :as controls]
[status-im2.contexts.chat.messages.composer.mentions.view :as mentions]
[status-im.ui2.screens.chat.composer.edit.view :as edit]
[status-im.ui2.screens.chat.composer.input :as input]
[status-im.ui2.screens.chat.composer.reply :as reply]
[quo.react :refer [set-native-props]]))
(def initial-content-height (atom nil))
(def keyboard-hiding? (atom false))
(defn minimize
[{:keys [min-y set-bg-opacity set-translate-y set-parent-height refs chat-id]}]
(set-bg-opacity 0)
(set-translate-y (- min-y))
(set-parent-height min-y)
(when-not (seq (get @input/input-texts chat-id))
(set-native-props (:record-ref refs) #js {:right 0 :left 0})))
(defn maximize
[{:keys [max-y set-bg-opacity set-translate-y set-parent-height max-parent-height refs]}]
(set-bg-opacity 1)
(set-translate-y (- max-y))
(set-parent-height max-parent-height)
(set-native-props (:record-ref refs) #js {:right nil :left -1000}))
(defn clean-and-minimize
[{:keys [chat-id refs] :as params}]
(input/clear-input chat-id refs)
(minimize params))
(def gesture-values (atom {}))
(defn get-bottom-sheet-gesture
[{:keys [translate-y refs keyboard-shown min-y max-y] :as params}]
(let [{:keys [text-input-ref]} refs]
(-> (gesture/gesture-pan)
(gesture/on-start
(fn [_]
(if (and keyboard-shown (not @input/recording-audio?))
(swap! gesture-values assoc :pan-y (reanimated/get-shared-value translate-y))
(input/input-focus text-input-ref))))
(gesture/on-update
(fn [evt]
(when (and keyboard-shown (not @input/recording-audio?))
(let [tY (oget evt "translationY")]
(swap! gesture-values assoc :dy (- tY (:pdy @gesture-values)))
(swap! gesture-values assoc :pdy tY)
(reanimated/set-shared-value
translate-y
(max (min (+ tY (:pan-y @gesture-values)) (- min-y)) (- max-y)))))))
(gesture/on-end
(fn [_]
(when (and keyboard-shown (not @input/recording-audio?))
(if (< (:dy @gesture-values) 0)
(maximize params)
(do
(reset! keyboard-hiding? true)
(minimize params)
(background-timer/set-timeout #(rf/dispatch [:dismiss-keyboard]) 200)))))))))
(defn update-y
[{:keys [max-parent-height set-bg-opacity keyboard-shown max-y set-translate-y set-parent-height
min-y chat-id]
:as params}]
(let [content-height (get @input/input-text-content-heights chat-id)
new-y (+ min-y
(if (and content-height @initial-content-height)
(- content-height
@initial-content-height)
0))]
(if (< new-y max-y)
(when keyboard-shown
(if (> new-y max-parent-height)
(set-bg-opacity 1)
(set-bg-opacity 0))
(set-translate-y (- new-y))
(set-parent-height (min new-y max-parent-height)))
(if keyboard-shown
(maximize params)
(set-bg-opacity 0)))))
(defn get-input-content-change
[{:keys [chat-id] :as params}]
(fn [evt]
(let [new-height (oget evt "nativeEvent.contentSize.height")]
(swap! input/input-text-content-heights assoc chat-id new-height)
(if @initial-content-height
(do
;;on Android when last symbol removed, height value is wrong, like 300px
(when (string/blank? (get @input/input-texts chat-id))
(swap! input/input-text-content-heights assoc chat-id @initial-content-height))
(update-y params))
(reset! initial-content-height new-height)))))
(defn effect!
[{:keys [keyboard-shown reply edit suggestions images] :as params}]
(rn/use-effect
(fn []
(when (or (not @keyboard-hiding?)
(and @keyboard-hiding? (not keyboard-shown)))
(reset! keyboard-hiding? false)
(if (not keyboard-shown)
(minimize params)
(update-y params))))
[reply edit suggestions images]))
(defn prepare-params
[[refs window-height translate-y bg-opacity bg-bottom min-y max-y parent-height
max-parent-height chat-id suggestions reply edit images keyboard-shown]]
{:chat-id chat-id
:translate-y translate-y
:min-y min-y
:max-y max-y
:max-parent-height max-parent-height
:parent-height parent-height
:suggestions suggestions
:reply reply
:edit edit
:images images
:refs refs
:keyboard-shown keyboard-shown
:bg-opacity bg-opacity
:bg-bottom bg-bottom
:window-height window-height
:set-bg-opacity (fn [value]
(reanimated/set-shared-value bg-bottom (if (= value 1) 0 (- window-height)))
(reanimated/set-shared-value bg-opacity (reanimated/with-timing value)))
:set-translate-y (fn [value]
(reanimated/set-shared-value translate-y (reanimated/with-timing value)))
:set-parent-height (fn [value]
(reanimated/set-shared-value parent-height (reanimated/with-timing value)))})
;; IMPORTANT! PLEASE READ BEFORE MAKING CHANGES
;; 1) insets are not stable so we can't rely on the first values and we have to use it from render
;; function, so effect will be triggered a few times, for example first time it will be 0 and second
;; time 38 on ios, and 400 and 0 on Android
;; 2) for keyboard we use hooks, for some reason it triggers effect a few times with the same values,
;; also it changes value only when keyboard completely closed or opened, that's why its not visually
;; smooth, there is a small visible delay between keyboard and sheet animations
;; 3) when we minimize sheet and hide keyboard in `sheet-gesture` function, keyboard hook triggers
;; effect and there is a visual glitch when we have more than one line of text, so we use
;; `keyboard-hiding?` flag
;; 4) we store input content height in `input/input-text-content-heights` atom , we need it when chat
;; screen is reopened
(defn f-composer
[_ _]
(let [text-input-ref (rn/create-ref)
send-ref (rn/create-ref)
record-ref (rn/create-ref)
refs {:send-ref send-ref
:text-input-ref text-input-ref
:record-ref record-ref}]
(fn [chat-id insets]
(let [reply (rf/sub [:chats/reply-message])
edit (rf/sub [:chats/edit-message])
suggestions (rf/sub [:chat/mention-suggestions])
images (rf/sub [:chats/sending-image])
bottom-inset (max 20 (:bottom insets))
{window-height :height} (rn/get-window)
{:keys [keyboard-shown keyboard-height]} (hooks/use-keyboard)
translate-y (reanimated/use-shared-value 0)
bg-opacity (reanimated/use-shared-value 0)
bg-bottom (reanimated/use-shared-value (- window-height))
suggestions? (and (seq suggestions)
keyboard-shown
(not @keyboard-hiding?))
max-y (- window-height
(- (if (> keyboard-height 0)
keyboard-height
360)
bottom-inset)
46)
min-y (+ 108
bottom-inset
(if suggestions?
(min (/ max-y 2)
(+ 16
(* 46 (dec (count suggestions)))))
(+ 0
(when (and
(or edit reply)
(not @input/recording-audio?))
38)
(when (seq images) 80))))
parent-height (reanimated/use-shared-value min-y)
max-parent-height (Math/abs (- max-y 110 bottom-inset))
params
(prepare-params
[refs window-height translate-y bg-opacity bg-bottom min-y max-y parent-height
max-parent-height chat-id suggestions reply edit images keyboard-shown])
input-content-change (get-input-content-change params)
bottom-sheet-gesture (get-bottom-sheet-gesture params)]
(effect! params)
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:height parent-height}
{})}
;;;;input
[gesture/gesture-detector {:gesture bottom-sheet-gesture}
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]}
(style/input-bottom-sheet window-height))}
[rn/view {:style (style/bottom-sheet-handle)}]
[edit/edit-message-auto-focus-wrapper text-input-ref edit #(clean-and-minimize params)]
[reply/reply-message-auto-focus-wrapper text-input-ref reply]
[rn/view
{:style {:height (- max-y (- min-y 38))}}
[input/text-input
{:chat-id chat-id
:on-content-size-change input-content-change
:sending-image (seq images)
:refs refs}]]]]
(if suggestions?
[:f> mentions/f-mentions (select-keys params [:refs :suggestions :max-y]) bottom-inset]
[controls/view send-ref record-ref params bottom-inset chat-id images
edit #(clean-and-minimize params)])
;;;;black background
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:opacity bg-opacity
:transform [{:translateY bg-bottom}]}
(style/bottom-sheet-background window-height))}]]))))
@@ -29,10 +29,12 @@
images-count (count (:album message))
;; album images are always square, except when we have 3 images, then they must be rectangular
;; (portrait or landscape)
portrait? (and (= images-count rectangular-style-count) (= album-style :portrait))]
portrait? (and (= images-count rectangular-style-count) (= album-style :portrait))
text (:text (:content first-image))]
(if (and albumize? (> images-count 1))
[:<>
[rn/view {:style {:margin-bottom 10}} [text/text-content first-image context]]
(when (not= text "placeholder")
[rn/view {:style {:margin-bottom 10}} [text/text-content first-image context]])
[rn/view
{:style (style/album-container portrait?)}
(map-indexed
@@ -1,52 +0,0 @@
(ns status-im2.contexts.chat.messages.content.audio.component-spec
(:require [status-im2.contexts.chat.messages.content.audio.view :as audio-message]
[test-helpers.component :as h]
[react-native.audio-toolkit :as audio]
[re-frame.core :as re-frame]))
(def message
{:audio-duration-ms 5000
:message-id "message-id"})
(def context
{:in-pinned-view? false})
(defn setup-subs
[subs]
(doseq [keyval subs]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(h/describe "audio message"
(h/before-each
#(setup-subs {:mediaserver/port 1000}))
(h/test "renders correctly"
(h/render [audio-message/audio-message message context])
(h/is-truthy (h/get-by-label-text :audio-message-container)))
(h/test "press play calls audio/toggle-playpause-player"
(with-redefs [audio/toggle-playpause-player (js/jest.fn)
audio/new-player (fn [_ _ _] {})
audio/destroy-player #()
audio/prepare-player (fn [_ on-success _] (on-success))
audio-message/download-audio-http (fn [_ on-success] (on-success "audio-uri"))]
(h/render [audio-message/audio-message message context])
(h/fire-event
:on-press
(h/get-by-label-text :play-pause-audio-message-button))
(-> (h/expect audio/toggle-playpause-player)
(.toHaveBeenCalledTimes 1))))
(h/test "press play renders error"
(h/render [audio-message/audio-message message context])
(with-redefs [audio/toggle-playpause-player (fn [_ _ _ on-error] (on-error))
audio/new-player (fn [_ _ _] {})
audio/destroy-player #()
audio/prepare-player (fn [_ on-success _] (on-success))
audio-message/download-audio-http (fn [_ on-success] (on-success "audio-uri"))]
(h/fire-event
:on-press
(h/get-by-label-text :play-pause-audio-message-button))
(h/wait-for #(h/is-truthy (h/get-by-label-text :audio-error-label))))))
@@ -1,41 +0,0 @@
(ns status-im2.contexts.chat.messages.content.audio.style
(:require [quo2.foundations.colors :as colors]
[quo2.theme :as theme]))
(defn container
[]
{:width 295
:height 56
:border-radius 12
:border-width 1
:padding 12
:flex-direction :row
:align-items :center
:justify-content :space-between
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80)
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80-opa-40)})
(def play-pause-slider-container
{:flex-direction :row
:align-items :center})
(def slider-container
{:position :absolute
:left 60
:right 71
:bottom nil})
(defn play-pause-container
[]
{:background-color (get-in colors/customization [:blue (if (theme/dark?) 60 50)])
:width 32
:height 32
:border-radius 16
:align-items :center
:justify-content :center})
(def timestamp
{:margin-left 4})
(def error-label
{:margin-bottom 16})
@@ -1,204 +0,0 @@
(ns status-im2.contexts.chat.messages.content.audio.view
(:require ["react-native-blob-util" :default ReactNativeBlobUtil]
[goog.string :as gstring]
[reagent.core :as reagent]
[react-native.audio-toolkit :as audio]
[status-im2.contexts.chat.messages.content.audio.style :as style]
[react-native.platform :as platform]
[taoensso.timbre :as log]
[quo2.foundations.colors :as colors]
[quo2.core :as quo]
[react-native.core :as rn]
[utils.re-frame :as rf]
[utils.i18n :as i18n]))
(def ^:const media-server-uri-prefix "https://localhost:")
(def ^:const audio-path "/messages/audio")
(def ^:const uri-param "?messageId=")
(defonce active-players (atom {}))
(defonce audio-uris (atom {}))
(defonce progress-timer (atom nil))
(defonce current-player-key (reagent/atom nil))
(defn get-player-key
[message-id in-pinned-view?]
(str in-pinned-view? message-id))
(defn destroy-player
[player-key]
(when-let [player (@active-players player-key)]
(audio/destroy-player player)
(swap! active-players dissoc player-key)))
(defn update-state
[state new-state]
(when-not (= @state new-state)
(reset! state new-state)))
(defn seek-player
[player-key player-state value on-success]
(when-let [player (@active-players player-key)]
(audio/seek-player
player
value
#(when on-success (on-success))
#(update-state player-state :error))
(update-state player-state :seeking)))
(defn download-audio-http
[base64-uri on-success]
(-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?}))
(.fetch "GET" (str base64-uri))
(.then #(on-success (.base64 ^js %)))
(.catch #(log/error "could not fetch audio " base64-uri))))
(defn create-player
[{:keys [progress player-state player-key]} audio-url on-success]
(download-audio-http
audio-url
(fn [base64-data]
(let [player (audio/new-player
(str "data:audio/acc;base64," base64-data)
{:autoDestroy false
:continuesToPlayInBackground false}
(fn []
(update-state player-state :ready-to-play)
(reset! progress 0)
(when (and @progress-timer (= @current-player-key player-key))
(js/clearInterval @progress-timer)
(reset! progress-timer nil))))]
(swap! active-players assoc player-key player)
(audio/prepare-player
player
#(when on-success (on-success))
#(update-state player-state :error)))))
(update-state player-state :preparing))
(defn play-pause-player
[{:keys [player-key player-state progress message-id audio-duration-ms seeking-audio?
user-interaction?]
:as params}]
(let [mediaserver-port (rf/sub [:mediaserver/port])
audio-uri (str media-server-uri-prefix
mediaserver-port
audio-path
uri-param
message-id)
player (@active-players player-key)
playing? (= @player-state :playing)]
(when-not playing?
(reset! current-player-key player-key))
(if (and player
(= (@audio-uris player-key) audio-uri))
(audio/toggle-playpause-player
player
(fn []
(update-state player-state :playing)
(when @progress-timer
(js/clearInterval @progress-timer))
(reset! progress-timer
(js/setInterval
(fn []
(let [player (@active-players player-key)
current-time (audio/get-player-current-time player)
playing? (= @player-state :playing)]
(when (and playing? (not @seeking-audio?) (> current-time 0))
(reset! progress current-time))))
100)))
(fn []
(update-state player-state :ready-to-play)
(when (and @progress-timer user-interaction?)
(js/clearInterval @progress-timer)
(reset! progress-timer nil)))
#(update-state player-state :error))
(do
(swap! audio-uris assoc player-key audio-uri)
(destroy-player player-key)
(create-player params
audio-uri
(fn []
(reset! seeking-audio? false)
(if (> @progress 0)
(let [seek-time (* audio-duration-ms @progress)
checked-seek-time (min audio-duration-ms seek-time)]
(seek-player
player-key
player-state
checked-seek-time
#(play-pause-player params)))
(play-pause-player params))))))))
(defn f-audio-message
[player-state progress seeking-audio? {:keys [audio-duration-ms message-id]}
{:keys [in-pinned-view?]}]
(let [player-key (get-player-key message-id in-pinned-view?)
player (@active-players player-key)
duration (if (and player (not (#{:preparing :not-loaded :error} @player-state)))
(audio/get-player-duration player)
audio-duration-ms)
time-secs (quot
(if (or @seeking-audio? (#{:playing :seeking} @player-state))
(if (<= @progress 1) (* duration @progress) @progress)
duration)
1000)]
(rn/use-effect (fn [] #(destroy-player player-key)))
(rn/use-effect
(fn []
(when (and (some? @current-player-key)
(not= @current-player-key player-key)
(= @player-state :playing))
(play-pause-player {:player-key player-key
:player-state player-state
:progress progress
:message-id message-id
:audio-duration-ms duration
:seeking-audio? seeking-audio?
:user-interaction? false})))
[@current-player-key])
(if (= @player-state :error)
[quo/text
{:style style/error-label
:accessibility-label :audio-error-label
:weight :medium
:size :paragraph-2}
(i18n/label :error-loading-audio)]
[rn/view
{:accessibility-label :audio-message-container
:style (style/container)}
[rn/touchable-opacity
{:accessibility-label :play-pause-audio-message-button
:on-press #(play-pause-player {:player-key player-key
:player-state player-state
:progress progress
:message-id message-id
:audio-duration-ms duration
:seeking-audio? seeking-audio?
:user-interaction? true})
:style (style/play-pause-container)}
[quo/icon
(case @player-state
:preparing :i/loading
:playing :i/pause-audio
:i/play-audio)
{:size 20
:color colors/white}]]
[:f> quo/soundtrack
{:style style/slider-container
:audio-current-time-ms progress
:player-ref (@active-players player-key)
:seeking-audio? seeking-audio?}]
[quo/text
{:style style/timestamp
:accessibility-label :audio-duration-label
:weight :medium
:size :paragraph-2}
(gstring/format "%02d:%02d" (quot time-secs 60) (mod time-secs 60))]])))
(defn audio-message
[message context]
(let [player-state (reagent/atom :not-loaded)
progress (reagent/atom 0)
seeking-audio? (reagent/atom false)]
(fn []
[:f> f-audio-message player-state progress seeking-audio? message context])))
@@ -16,7 +16,8 @@
(defn image-message
[index {:keys [content image-width image-height message-id] :as message} context on-long-press]
(let [insets (safe-area/get-insets)
dimensions (calculate-dimensions (or image-width 1000) (or image-height 1000))]
dimensions (calculate-dimensions (or image-width 1000) (or image-height 1000))
text (:text content)]
(fn []
(let [shared-element-id (rf/sub [:shared-element-id])]
[rn/touchable-opacity
@@ -29,7 +30,7 @@
{:messages [message]
:index 0
:insets insets}])}
(when (= index 0)
(when (and (not= text "placeholder") (= index 0))
[rn/view {:style {:margin-bottom 10}} [text/text-content message context]])
[fast-image/fast-image
{:source {:uri (:image content)}
@@ -1,6 +1,5 @@
(ns status-im2.contexts.chat.messages.content.pin.view
(:require [react-native.core :as rn]
[status-im2.contexts.chat.composer.reply.view :as reply]
[status-im2.contexts.chat.messages.content.pin.style :as style]
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
@@ -54,4 +53,4 @@
(old-style/message-timestamp-text))
:accessibility-label :message-timestamp}
timestamp-str]]
[reply/quoted-message quoted-message false true]]])
[old-message/quoted-message quoted-message true]]])
@@ -124,7 +124,6 @@
(defn render-parsed-text
[{:keys [content chat-id edited-at]}]
^{:key (:parsed-text content)}
[rn/view {:style style/parsed-text-block}
(reduce (fn [acc e]
(render-block acc e chat-id))
@@ -1,29 +1,26 @@
(ns status-im2.contexts.chat.messages.content.view
(:require
[react-native.core :as rn]
[quo2.foundations.colors :as colors]
[react-native.platform :as platform]
[status-im2.contexts.chat.messages.content.style :as style]
[status-im2.contexts.chat.messages.content.pin.view :as pin]
[status-im2.constants :as constants]
[status-im2.contexts.chat.messages.content.unknown.view :as content.unknown]
[status-im2.contexts.chat.messages.content.text.view :as content.text]
[status-im2.contexts.chat.messages.drawers.view :as drawers]
[status-im2.contexts.chat.messages.content.reactions.view :as reactions]
[status-im2.contexts.chat.messages.content.status.view :as status]
[status-im2.contexts.chat.messages.content.system.text.view :as system.text]
[status-im2.contexts.chat.messages.content.album.view :as album]
[status-im2.contexts.chat.messages.avatar.view :as avatar]
[status-im2.contexts.chat.messages.content.image.view :as image]
[status-im2.contexts.chat.messages.content.audio.view :as audio]
[quo2.core :as quo]
[utils.re-frame :as rf]
[status-im.ui2.screens.chat.messages.message :as old-message]
[status-im2.contexts.chat.composer.reply.view :as reply]
[status-im2.common.not-implemented :as not-implemented]
[utils.datetime :as datetime]
[reagent.core :as reagent]
[utils.address :as address]))
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
[react-native.platform :as platform]
[status-im2.contexts.chat.messages.content.style :as style]
[status-im2.contexts.chat.messages.content.pin.view :as pin]
[status-im2.constants :as constants]
[status-im2.contexts.chat.messages.content.unknown.view :as content.unknown]
[status-im2.contexts.chat.messages.content.text.view :as content.text]
[status-im2.contexts.chat.messages.drawers.view :as drawers]
[status-im2.contexts.chat.messages.content.reactions.view :as reactions]
[status-im2.contexts.chat.messages.content.status.view :as status]
[status-im2.contexts.chat.messages.content.system.text.view :as system.text]
[status-im2.contexts.chat.messages.content.album.view :as album]
[status-im2.contexts.chat.messages.avatar.view :as avatar]
[status-im2.contexts.chat.messages.content.image.view :as image]
[quo2.core :as quo]
[utils.re-frame :as rf]
[status-im.ui2.screens.chat.messages.message :as old-message]
[status-im2.common.not-implemented :as not-implemented]
[utils.datetime :as datetime]
[reagent.core :as reagent]
[utils.address :as address]))
(def delivery-state-showing-time-ms 3000)
@@ -111,7 +108,7 @@
:on-long-press #(on-long-press message-data context)}
[rn/view {:style {:padding-vertical 8}}
(when (and (seq response-to) quoted-message)
[reply/quoted-message quoted-message])
[old-message/quoted-message quoted-message])
[rn/view
{:style {:padding-horizontal 12
:flex-direction :row}}
@@ -131,7 +128,7 @@
[not-implemented/not-implemented [old-message/sticker message-data]]
constants/content-type-audio
[audio/audio-message message-data context]
[not-implemented/not-implemented [old-message/audio message-data]]
constants/content-type-image
[image/image-message 0 message-data context on-long-press]
@@ -2,7 +2,7 @@
(:require [quo2.core :as quo]
[react-native.core :as rn]
[status-im.ui.components.react :as react]
[status-im2.contexts.chat.composer.reply.view :as reply]
[status-im.ui2.screens.chat.components.reply.view :as components.reply]
[status-im2.common.not-implemented :as not-implemented]
[status-im2.constants :as constants]
[utils.i18n :as i18n]
@@ -44,10 +44,11 @@
:icon :i/reply
:id :reply}])
(when (and (not (or deleted? deleted-for-me?))
(not= (get content :text) "placeholder")
(not= content-type constants/content-type-audio))
[{:type :main
:on-press #(react/copy-to-clipboard
(reply/get-quoted-text-with-mentions
(components.reply/get-quoted-text-with-mentions
(get content :parsed-text)))
:label (i18n/label :t/copy-text)
:icon :i/copy
@@ -0,0 +1,211 @@
(ns status-im2.contexts.chat.messages.list.new-temp-view
(:require [oops.core :as oops]
[quo2.core :as quo]
[react-native.background-timer :as background-timer]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[status-im.ui.screens.chat.group :as chat.group]
[status-im.ui.screens.chat.message.gap :as message.gap]
[status-im2.common.not-implemented :as not-implemented]
[status-im2.constants :as constants]
[status-im2.contexts.chat.bottom-sheet-composer.utils :as utils]
[status-im2.contexts.chat.messages.content.deleted.view :as content.deleted]
[status-im2.contexts.chat.messages.content.view :as message]
[status-im2.contexts.chat.messages.list.state :as state]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[status-im2.contexts.chat.bottom-sheet-composer.constants :as composer.constants]))
(defonce messages-list-ref (atom nil))
(defn list-key-fn [{:keys [message-id value]}] (or message-id value))
(defn list-ref [ref] (reset! messages-list-ref ref))
(defn scroll-to-bottom
[]
(some-> ^js @messages-list-ref
(.scrollToOffset #js {:y 0 :animated true})))
(defonce ^:const threshold-percentage-to-show-floating-scroll-down-button 75)
(defonce show-floating-scroll-down-button (reagent/atom false))
(defn on-scroll
[evt]
(let [y (oops/oget evt "nativeEvent.contentOffset.y")
layout-height (oops/oget evt "nativeEvent.layoutMeasurement.height")
threshold-height (* (/ layout-height 100)
threshold-percentage-to-show-floating-scroll-down-button)
reached-threshold? (> y threshold-height)]
(when (not= reached-threshold? @show-floating-scroll-down-button)
(rn/configure-next (:ease-in-ease-out rn/layout-animation-presets))
(reset! show-floating-scroll-down-button reached-threshold?))))
(defn on-viewable-items-changed
[evt]
(when @messages-list-ref
(reset! state/first-not-visible-item
(when-let [last-visible-element (aget (oops/oget evt "viewableItems")
(dec (oops/oget evt "viewableItems.length")))]
(let [index (oops/oget last-visible-element "index")
;; Get first not visible element, if it's a datemark/gap
;; we might unnecessarely add messages on receiving as
;; they do not have a clock value, but most of the times
;; it will be a message
first-not-visible (aget (oops/oget @messages-list-ref "props.data") (inc index))]
(when (and first-not-visible
(= :message (:type first-not-visible)))
first-not-visible))))))
;;TODO this is not really working in pair with inserting new messages because we stop inserting new
;;messages
;;if they outside the viewarea, but we load more here because end is reached,so its slowdown UI because
;;we
;;load and render 20 messages more, but we can't prevent this , because otherwise :on-end-reached will
;;work wrong
(defn list-on-end-reached
[]
(if @state/scrolling
(rf/dispatch [:chat.ui/load-more-messages-for-current-chat])
(background-timer/set-timeout #(rf/dispatch [:chat.ui/load-more-messages-for-current-chat])
(if platform/low-device? 700 200))))
(defonce messages-view-height (reagent/atom 0))
(defn on-messages-view-layout
[evt]
(reset! messages-view-height (oops/oget evt "nativeEvent.layout.height")))
(defn list-footer
[{:keys [chat-id]}]
(let [loading-messages? (rf/sub [:chats/loading-messages? chat-id])
all-loaded? (rf/sub [:chats/all-loaded? chat-id])]
(when (or loading-messages? (not chat-id) (not all-loaded?))
[rn/view {:style (when platform/android? {:scaleY -1})}
[quo/skeleton @messages-view-height]])))
(defn list-header
[{:keys [chat-id chat-type invitation-admin]}]
(when (= chat-type constants/private-group-chat-type)
[rn/view {:style (when platform/android? {:scaleY -1})}
[chat.group/group-chat-footer chat-id invitation-admin]]))
(defn render-fn
[{:keys [type value deleted? deleted-for-me? content-type] :as message-data} _ _
{:keys [context keyboard-shown]}]
[rn/view {:style (when platform/android? {:scaleY -1})}
(if (= type :datemark)
[quo/divider-date value]
(if (= content-type constants/content-type-gap)
[not-implemented/not-implemented
[message.gap/gap message-data]]
[rn/view {:padding-horizontal 8}
(if (or deleted? deleted-for-me?)
[content.deleted/deleted-message message-data context]
[message/message-with-reactions message-data context keyboard-shown])]))])
(defn calc-shell-position
[y]
(let [{:keys [input-content-height focused?]} (rf/sub [:chats/current-chat-input])
reply (rf/sub [:chats/reply-message])
edit (rf/sub [:chats/edit-message])
lines (utils/calc-lines input-content-height)
base (if (or reply edit)
(- composer.constants/edit-container-height)
0)]
(if (not focused?)
(if (> lines 1) (+ -18 base) base)
(if (> lines 12)
(reanimated/get-shared-value y)
(if (> lines 1) (- (- input-content-height composer.constants/input-height base)) base)))))
(defn shell-button
[insets]
(let [y (reanimated/use-shared-value 0)
shell-position (calc-shell-position y)]
(rn/use-effect (fn []
(reanimated/animate y shell-position))
[shell-position])
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:transform [{:translate-y y}]}
{:bottom (+ composer.constants/composer-default-height (:bottom insets) 6)
:position :absolute
:left 0
:right 0})}
[quo/floating-shell-button
(merge {:jump-to
{:on-press #(do
(rf/dispatch [:chat/close true])
(rf/dispatch [:shell/navigate-to-jump-to]))
:label (i18n/label :t/jump-to)
:style {:align-self :center}}}
(when @show-floating-scroll-down-button
{:scroll-to-bottom {:on-press scroll-to-bottom}}))
{}]]))
(defn messages-list-content
[{:keys [chat-id] :as chat} insets keyboard-shown]
(fn []
(let [context (rf/sub [:chats/current-chat-message-list-view-context])
messages (rf/sub [:chats/raw-chat-messages-stream chat-id])]
[rn/view
{:style {:flex 1}}
;; NOTE: DO NOT use anonymous functions for handlers
[rn/flat-list
{:key-fn list-key-fn
:ref list-ref
:header [list-header chat]
:footer [list-footer chat]
:data messages
:render-data {:context context
:keyboard-shown keyboard-shown}
:render-fn render-fn
:on-viewable-items-changed on-viewable-items-changed
:on-end-reached list-on-end-reached
:on-scroll-to-index-failed identity ; don't remove this
:content-container-style {:padding-top (+ composer.constants/composer-default-height
(:bottom insets)
32)
:padding-bottom 16}
:scroll-indicator-insets {:top (+ composer.constants/composer-default-height
(:bottom insets))}
:keyboard-dismiss-mode :interactive
:keyboard-should-persist-taps :handled
:on-momentum-scroll-begin state/start-scrolling
:on-momentum-scroll-end state/stop-scrolling
:scroll-event-throttle 16
:on-scroll on-scroll
;; TODO https://github.com/facebook/react-native/issues/30034
:inverted (when platform/ios? true)
:style (when platform/android? {:scaleY -1})
:on-layout on-messages-view-layout}]
[:f> shell-button insets]])))
(defn use-keyboard-visibility
[]
(let [show-listener (atom nil)
hide-listener (atom nil)
shown? (atom nil)]
(rn/use-effect
(fn []
(reset! show-listener
(.addListener rn/keyboard "keyboardWillShow" #(reset! shown? true)))
(reset! hide-listener
(.addListener rn/keyboard "keyboardWillHide" #(reset! shown? false)))
(fn []
(.remove ^js @show-listener)
(.remove ^js @hide-listener))))
{:shown? shown?}))
(defn- f-messages-list
[chat insets]
(let [{keyboard-shown? :shown?} (use-keyboard-visibility)]
[messages-list-content chat insets keyboard-shown?]))
(defn messages-list
[chat insets]
[:f> f-messages-list chat insets])
@@ -4,19 +4,16 @@
[react-native.background-timer :as background-timer]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[status-im.ui.screens.chat.group :as chat.group]
[status-im.ui.screens.chat.message.gap :as message.gap]
[status-im2.common.not-implemented :as not-implemented]
[status-im2.constants :as constants]
[status-im2.contexts.chat.composer.utils :as utils]
[status-im2.contexts.chat.messages.content.deleted.view :as content.deleted]
[status-im2.contexts.chat.messages.content.view :as message]
[status-im2.contexts.chat.messages.list.state :as state]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[status-im2.contexts.chat.composer.constants :as composer.constants]))
[utils.re-frame :as rf]))
(defonce messages-list-ref (atom nil))
@@ -93,7 +90,7 @@
(defn render-fn
[{:keys [type value deleted? deleted-for-me? content-type] :as message-data} _ _
{:keys [context keyboard-shown]}]
{:keys [keyboard-shown context]}]
[rn/view {:style (when platform/android? {:scaleY -1})}
(if (= type :datemark)
[quo/divider-date value]
@@ -105,114 +102,47 @@
[content.deleted/deleted-message message-data context]
[message/message-with-reactions message-data context keyboard-shown])]))])
(defn calc-shell-position
[curr-pos input-content-height focused? reply edit images]
(let [lines (utils/calc-lines input-content-height)
base (if reply (- composer.constants/reply-container-height) 0)
base (if edit (- composer.constants/edit-container-height) base)
base (if (seq images) (- composer.constants/images-container-height) base)]
(if (not focused?)
(if (> lines 1) (+ -18 base) base)
(if (> lines 12)
curr-pos
(if (> lines 1) (- (- input-content-height composer.constants/input-height base)) base)))))
(def memoized-calc-shell-position (memoize calc-shell-position))
(defn shell-button
[insets]
(let [y (reanimated/use-shared-value 0)
{:keys [input-content-height focused?]} (rf/sub [:chats/current-chat-input])
reply (rf/sub [:chats/reply-message])
edit (rf/sub [:chats/edit-message])
images (rf/sub [:chats/sending-image])
curr-pos (reanimated/get-shared-value y)
shell-position
(memoized-calc-shell-position curr-pos input-content-height focused? reply edit images)]
(rn/use-effect (fn []
(reanimated/animate y shell-position))
[shell-position])
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:transform [{:translate-y y}]}
{:bottom (+ composer.constants/composer-default-height (:bottom insets) 6)
:position :absolute
:left 0
:right 0})}
(defn messages-list
[{:keys [chat-id] :as chat}]
(let [context (rf/sub [:chats/current-chat-message-list-view-context])
messages (rf/sub [:chats/raw-chat-messages-stream chat-id])
keyboard-shown (atom false)
bottom-space 15]
[rn/view
{:style {:flex 1}}
;; NOTE: DO NOT use anonymous functions for handlers
[rn/flat-list
{:key-fn list-key-fn
:ref list-ref
:header [list-header chat]
:footer [list-footer chat]
:data messages
:render-data {:context context
:keyboard-shown keyboard-shown}
:render-fn render-fn
:on-viewable-items-changed on-viewable-items-changed
:on-end-reached list-on-end-reached
:on-scroll-to-index-failed identity ; don't remove this
:content-container-style {:padding-top (+ bottom-space 32)
:padding-bottom 16}
:scroll-indicator-insets {:top bottom-space} ; iOS only
:keyboard-dismiss-mode :interactive
:keyboard-should-persist-taps :handled
:onMomentumScrollBegin state/start-scrolling
:onMomentumScrollEnd state/stop-scrolling
:scrollEventThrottle 16
:on-scroll on-scroll
;; TODO https://github.com/facebook/react-native/issues/30034
:inverted (when platform/ios? true)
:style (when platform/android? {:scaleY -1})
:on-layout on-messages-view-layout}]
[quo/floating-shell-button
(merge {:jump-to
{:on-press (fn []
(rf/dispatch [:chat/close true])
(rf/dispatch [:shell/navigate-to-jump-to]))
:label (i18n/label :t/jump-to)
:style {:align-self :center}}}
{:on-press #(do
(rf/dispatch [:chat/close true])
(rf/dispatch [:shell/navigate-to-jump-to]))
:label (i18n/label :t/jump-to)}}
(when @show-floating-scroll-down-button
{:scroll-to-bottom {:on-press scroll-to-bottom}}))
{}]]))
(defn messages-list-content
[{:keys [chat-id] :as chat} insets keyboard-shown]
(fn []
(let [context (rf/sub [:chats/current-chat-message-list-view-context])
messages (rf/sub [:chats/raw-chat-messages-stream chat-id])]
[rn/view
{:style {:flex 1}}
;; NOTE: DO NOT use anonymous functions for handlers
[rn/flat-list
{:key-fn list-key-fn
:ref list-ref
:header [list-header chat]
:footer [list-footer chat]
:data messages
:render-data {:context context
:keyboard-shown keyboard-shown}
:render-fn render-fn
:on-viewable-items-changed on-viewable-items-changed
:on-end-reached list-on-end-reached
:on-scroll-to-index-failed identity ; don't remove this
:content-container-style {:padding-top (+ composer.constants/composer-default-height
(:bottom insets)
32)
:padding-bottom 16}
:scroll-indicator-insets {:top (+ composer.constants/composer-default-height
(:bottom insets))}
:keyboard-dismiss-mode :interactive
:keyboard-should-persist-taps :handled
:on-momentum-scroll-begin state/start-scrolling
:on-momentum-scroll-end state/stop-scrolling
:scroll-event-throttle 16
:on-scroll on-scroll
;; TODO https://github.com/facebook/react-native/issues/30034
:inverted (when platform/ios? true)
:style (when platform/android? {:scaleY -1})
:on-layout on-messages-view-layout}]
[:f> shell-button insets]])))
;; This should be replaced with keyboard hook. It has to do with flat-list probably. The keyboard-shown
;; value
;; updates in the parent component, but does not get passed to the children.
;; When using listeners and resetting the value on an atom it works.
(defn use-keyboard-visibility
[]
(let [show-listener (atom nil)
hide-listener (atom nil)
shown? (atom nil)]
(rn/use-effect
(fn []
(reset! show-listener
(.addListener rn/keyboard "keyboardWillShow" #(reset! shown? true)))
(reset! hide-listener
(.addListener rn/keyboard "keyboardWillHide" #(reset! shown? false)))
(fn []
(.remove ^js @show-listener)
(.remove ^js @hide-listener))))
{:shown? shown?}))
(defn- f-messages-list
[chat insets]
(let [{keyboard-shown? :shown?} (use-keyboard-visibility)]
[messages-list-content chat insets keyboard-shown?]))
(defn messages-list
[chat insets]
[:f> f-messages-list chat insets])
{:position :absolute
:bottom 6}]]))
@@ -2,28 +2,19 @@
(:require [quo2.core :as quo]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[status-im2.contexts.chat.messages.resolver.message-resolver :as resolver]
[status-im2.constants :as constants]))
(defn message-text
[{:keys [content-type] :as message}]
(cond (= content-type constants/content-type-audio)
(i18n/label :audio-message)
:else
(get-in message [:content :parsed-text])))
[status-im2.contexts.chat.messages.resolver.message-resolver :as resolver]))
(defn banner
[chat-id]
(let [pinned-message (rf/sub [:chats/last-pinned-message chat-id])
latest-pin-text (message-text pinned-message)
latest-pin-text (get-in pinned-message [:content :parsed-text])
{:keys [deleted? deleted-for-me?]} pinned-message
pins-count (rf/sub [:chats/pin-messages-count chat-id])
content-type-text? (= (:content-type pinned-message) constants/content-type-text)
latest-pin-text
(cond deleted? (i18n/label :t/message-deleted-for-everyone)
deleted-for-me? (i18n/label :t/message-deleted-for-you)
content-type-text? (resolver/resolve-message latest-pin-text)
:else latest-pin-text)]
(cond deleted? (i18n/label :t/message-deleted-for-everyone)
deleted-for-me? (i18n/label :t/message-deleted-for-you)
:else (resolver/resolve-message latest-pin-text))]
[quo/banner
{:latest-pin-text latest-pin-text
:pins-count pins-count
@@ -4,11 +4,14 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im2.config :as config]
[status-im2.constants :as constants]
[status-im2.contexts.chat.composer.view :as composer]
[status-im2.contexts.chat.bottom-sheet-composer.view :as bottom-sheet-composer]
[status-im2.contexts.chat.messages.composer.view :as composer]
[status-im2.contexts.chat.messages.contact-requests.bottom-drawer :as
contact-requests.bottom-drawer]
[status-im2.contexts.chat.messages.list.view :as messages.list]
[status-im2.contexts.chat.messages.list.new-temp-view :as messages.list.new]
[status-im2.contexts.chat.messages.pin.banner.view :as pin.banner]
[status-im2.navigation.state :as navigation.state]
[utils.debounce :as debounce]
@@ -75,10 +78,14 @@
:keyboardVerticalOffset (- (:bottom insets))}
[page-nav]
[pin.banner/banner chat-id]
[messages.list/messages-list chat insets]
(if config/new-composer-enabled?
[messages.list.new/messages-list chat insets]
[messages.list/messages-list chat insets])
(if-not able-to-send-message?
[contact-requests.bottom-drawer/view chat-id contact-request-state group-chat]
[:f> composer/composer insets])]))
(if config/new-composer-enabled?
[bottom-sheet-composer/bottom-sheet-composer insets]
[:f> composer/f-composer chat-id insets]))]))
(defn chat
@@ -56,8 +56,7 @@
[quo/disclaimer
{:accessibility-label :rules-disclaimer-checkbox
:container-style {:margin-top 20}
:on-change #(swap! agreed-to-rules? not)
:checked? @agreed-to-rules?}
:on-change #(swap! agreed-to-rules? not)}
(i18n/label :t/accept-community-rules)]
[rn/view {:style (style/bottom-container)}
[quo/button
@@ -3,10 +3,7 @@
[react-native.blur :as blur]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[oops.core :refer [oget]]
[status-im2.common.resources :as resources]
[status-im.async-storage.core :as async-storage]
[status-im2.contexts.shell.animation :as shell.animation]
[status-im2.contexts.onboarding.common.carousel.view :as carousel]
[status-im2.contexts.onboarding.common.background.style :as style]
[react-native.reanimated :as reanimated]
@@ -32,27 +29,6 @@
(defonce progress (atom nil))
(defonce paused (atom nil))
(defn store-screen-height
[evt]
(let [window-height (rf/sub [:dimensions/window-height])
height (or (oget evt "nativeEvent" "layout" "height") 0)
width (or (oget evt "nativeEvent" "layout" "width") 0)]
;; Layout height calculation
;; 1. Make sure height is more than width, and on-layout is not fired while the
;; screen is horizontal
;; 2. Initialize values with 0 in case of nil
;; 3. In the case of notch devices, the dimensions height will be smaller than
;; on-layout,
;; (without status bar height included)
;; https://github.com/status-im/status-mobile/issues/14633
;; 4. In the case of devices without a notch, both heights should be the same,
;; but actual values differ in some pixels, so arbitrary 5 pixels is allowed
(when (and (> height width)
(>= (+ height 5) (or window-height 0))
(not= height @shell.animation/screen-height))
(reset! shell.animation/screen-height height)
(async-storage/set-item! :screen-height height))))
(defn f-view
[dark-overlay?]
(let [view-id (rf/sub [:view-id])
@@ -70,8 +46,7 @@
[view-id])
[rn/view
{:style style/background-container
:on-layout store-screen-height}
{:style style/background-container}
[carousel/view
{:animate? animate?
:progress progress

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